Merge pull request #2505 from 0xProject/feature/plp-integration
🔂 Liquidity Provider Asset Swapper integration
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
contract DummyLiquidityProvider
|
||||
{
|
||||
/// @dev Quotes the amount of `makerToken` that would be obtained by
|
||||
/// selling `sellAmount` of `takerToken`.
|
||||
/// @param sellAmount Amount of `takerToken` to sell.
|
||||
/// @return makerTokenAmount Amount of `makerToken` that would be obtained.
|
||||
function getSellQuote(
|
||||
address, /* takerToken */
|
||||
address, /* makerToken */
|
||||
uint256 sellAmount
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 makerTokenAmount)
|
||||
{
|
||||
makerTokenAmount = sellAmount - 1;
|
||||
}
|
||||
|
||||
/// @dev Quotes the amount of `takerToken` that would need to be sold in
|
||||
/// order to obtain `buyAmount` of `makerToken`.
|
||||
/// @param buyAmount Amount of `makerToken` to buy.
|
||||
/// @return takerTokenAmount Amount of `takerToken` that would need to be sold.
|
||||
function getBuyQuote(
|
||||
address, /* takerToken */
|
||||
address, /* makerToken */
|
||||
uint256 buyAmount
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 takerTokenAmount)
|
||||
{
|
||||
takerTokenAmount = buyAmount + 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
contract DummyLiquidityProviderRegistry
|
||||
{
|
||||
address private constant NULL_ADDRESS = address(0x0);
|
||||
|
||||
mapping (address => mapping (address => address)) internal _gAddressBook;
|
||||
|
||||
/// @dev Sets address of pool for a market given market (xAsset, yAsset).
|
||||
/// @param xToken First asset managed by pool.
|
||||
/// @param yToken Second asset managed by pool.
|
||||
/// @param poolAddress Address of pool.
|
||||
function setLiquidityProviderForMarket(
|
||||
address xToken,
|
||||
address yToken,
|
||||
address poolAddress
|
||||
)
|
||||
external
|
||||
{
|
||||
_gAddressBook[xToken][yToken] = poolAddress;
|
||||
_gAddressBook[yToken][xToken] = poolAddress;
|
||||
}
|
||||
|
||||
/// @dev Returns the address of pool for a market given market (xAsset, yAsset), or reverts if pool does not exist.
|
||||
/// @param xToken First asset managed by pool.
|
||||
/// @param yToken Second asset managed by pool.
|
||||
/// @return Address of pool.
|
||||
function getLiquidityProviderForMarket(
|
||||
address xToken,
|
||||
address yToken
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (address poolAddress)
|
||||
{
|
||||
poolAddress = _gAddressBook[xToken][yToken];
|
||||
require(
|
||||
poolAddress != NULL_ADDRESS,
|
||||
"Registry/MARKET_PAIR_NOT_SET"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -36,9 +36,9 @@
|
||||
"compile:truffle": "truffle compile"
|
||||
},
|
||||
"config": {
|
||||
"publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler,ILiquidityProvider,ILiquidityProviderRegistry",
|
||||
"publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler,ILiquidityProvider,ILiquidityProviderRegistry,DummyLiquidityProviderRegistry,DummyLiquidityProvider",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||
"abis": "./test/generated-artifacts/@(ERC20BridgeSampler|ICurve|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|ILiquidityProvider|ILiquidityProviderRegistry|IUniswapExchangeQuotes|TestERC20BridgeSampler).json"
|
||||
"abis": "./test/generated-artifacts/@(DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|ICurve|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|ILiquidityProvider|ILiquidityProviderRegistry|IUniswapExchangeQuotes|TestERC20BridgeSampler).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as DummyLiquidityProvider from '../generated-artifacts/DummyLiquidityProvider.json';
|
||||
import * as DummyLiquidityProviderRegistry from '../generated-artifacts/DummyLiquidityProviderRegistry.json';
|
||||
import * as ERC20BridgeSampler from '../generated-artifacts/ERC20BridgeSampler.json';
|
||||
import * as IERC20BridgeSampler from '../generated-artifacts/IERC20BridgeSampler.json';
|
||||
import * as ILiquidityProvider from '../generated-artifacts/ILiquidityProvider.json';
|
||||
@@ -14,4 +16,6 @@ export const artifacts = {
|
||||
IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
|
||||
ILiquidityProvider: ILiquidityProvider as ContractArtifact,
|
||||
ILiquidityProviderRegistry: ILiquidityProviderRegistry as ContractArtifact,
|
||||
DummyLiquidityProviderRegistry: DummyLiquidityProviderRegistry as ContractArtifact,
|
||||
DummyLiquidityProvider: DummyLiquidityProvider as ContractArtifact,
|
||||
};
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../generated-wrappers/dummy_liquidity_provider';
|
||||
export * from '../generated-wrappers/dummy_liquidity_provider_registry';
|
||||
export * from '../generated-wrappers/erc20_bridge_sampler';
|
||||
export * from '../generated-wrappers/i_erc20_bridge_sampler';
|
||||
export * from '../generated-wrappers/i_liquidity_provider';
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as DummyLiquidityProvider from '../test/generated-artifacts/DummyLiquidityProvider.json';
|
||||
import * as DummyLiquidityProviderRegistry from '../test/generated-artifacts/DummyLiquidityProviderRegistry.json';
|
||||
import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
|
||||
import * as ICurve from '../test/generated-artifacts/ICurve.json';
|
||||
import * as IDevUtils from '../test/generated-artifacts/IDevUtils.json';
|
||||
@@ -16,6 +18,8 @@ import * as ILiquidityProviderRegistry from '../test/generated-artifacts/ILiquid
|
||||
import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json';
|
||||
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
|
||||
export const artifacts = {
|
||||
DummyLiquidityProvider: DummyLiquidityProvider as ContractArtifact,
|
||||
DummyLiquidityProviderRegistry: DummyLiquidityProviderRegistry as ContractArtifact,
|
||||
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
|
||||
ICurve: ICurve as ContractArtifact,
|
||||
IDevUtils: IDevUtils as ContractArtifact,
|
||||
|
||||
@@ -11,7 +11,11 @@ import { BigNumber, hexUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
import { TestERC20BridgeSamplerContract } from './wrappers';
|
||||
import {
|
||||
DummyLiquidityProviderContract,
|
||||
DummyLiquidityProviderRegistryContract,
|
||||
TestERC20BridgeSamplerContract,
|
||||
} from './wrappers';
|
||||
|
||||
blockchainTests('erc20-bridge-sampler', env => {
|
||||
let testContract: TestERC20BridgeSamplerContract;
|
||||
@@ -716,6 +720,90 @@ blockchainTests('erc20-bridge-sampler', env => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLiquidityProviderFromRegistry', () => {
|
||||
const xAsset = randomAddress();
|
||||
const yAsset = randomAddress();
|
||||
const sampleAmounts = getSampleAmounts(yAsset);
|
||||
let liquidityProvider: DummyLiquidityProviderContract;
|
||||
let registryContract: DummyLiquidityProviderRegistryContract;
|
||||
|
||||
before(async () => {
|
||||
liquidityProvider = await DummyLiquidityProviderContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyLiquidityProvider,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{},
|
||||
);
|
||||
|
||||
registryContract = await DummyLiquidityProviderRegistryContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DummyLiquidityProviderRegistry,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{},
|
||||
);
|
||||
await registryContract
|
||||
.setLiquidityProviderForMarket(xAsset, yAsset, liquidityProvider.address)
|
||||
.awaitTransactionSuccessAsync();
|
||||
});
|
||||
|
||||
it('should be able to get the liquidity provider', async () => {
|
||||
const xyLiquidityProvider = await testContract
|
||||
.getLiquidityProviderFromRegistry(registryContract.address, xAsset, yAsset)
|
||||
.callAsync();
|
||||
const yxLiquidityProvider = await testContract
|
||||
.getLiquidityProviderFromRegistry(registryContract.address, yAsset, xAsset)
|
||||
.callAsync();
|
||||
const unknownLiquidityProvider = await testContract
|
||||
.getLiquidityProviderFromRegistry(registryContract.address, yAsset, randomAddress())
|
||||
.callAsync();
|
||||
|
||||
expect(xyLiquidityProvider).to.eq(liquidityProvider.address);
|
||||
expect(yxLiquidityProvider).to.eq(liquidityProvider.address);
|
||||
expect(unknownLiquidityProvider).to.eq(constants.NULL_ADDRESS);
|
||||
});
|
||||
|
||||
it('should be able to query sells from the liquidity provider', async () => {
|
||||
const result = await testContract
|
||||
.sampleSellsFromLiquidityProviderRegistry(registryContract.address, yAsset, xAsset, sampleAmounts)
|
||||
.callAsync();
|
||||
result.forEach((value, idx) => {
|
||||
expect(value).is.bignumber.eql(sampleAmounts[idx].minus(1));
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to query buys from the liquidity provider', async () => {
|
||||
const result = await testContract
|
||||
.sampleBuysFromLiquidityProviderRegistry(registryContract.address, yAsset, xAsset, sampleAmounts)
|
||||
.callAsync();
|
||||
result.forEach((value, idx) => {
|
||||
expect(value).is.bignumber.eql(sampleAmounts[idx].plus(1));
|
||||
});
|
||||
});
|
||||
|
||||
it('should just return zeros if the liquidity provider cannot be found', async () => {
|
||||
const result = await testContract
|
||||
.sampleBuysFromLiquidityProviderRegistry(
|
||||
registryContract.address,
|
||||
yAsset,
|
||||
randomAddress(),
|
||||
sampleAmounts,
|
||||
)
|
||||
.callAsync();
|
||||
result.forEach(value => {
|
||||
expect(value).is.bignumber.eql(constants.ZERO_AMOUNT);
|
||||
});
|
||||
});
|
||||
|
||||
it('should just return zeros if the registry does not exist', async () => {
|
||||
const result = await testContract
|
||||
.sampleBuysFromLiquidityProviderRegistry(randomAddress(), yAsset, xAsset, sampleAmounts)
|
||||
.callAsync();
|
||||
result.forEach(value => {
|
||||
expect(value).is.bignumber.eql(constants.ZERO_AMOUNT);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('batchCall()', () => {
|
||||
it('can call one function', async () => {
|
||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../test/generated-wrappers/dummy_liquidity_provider';
|
||||
export * from '../test/generated-wrappers/dummy_liquidity_provider_registry';
|
||||
export * from '../test/generated-wrappers/erc20_bridge_sampler';
|
||||
export * from '../test/generated-wrappers/i_curve';
|
||||
export * from '../test/generated-wrappers/i_dev_utils';
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/DummyLiquidityProvider.json",
|
||||
"generated-artifacts/DummyLiquidityProviderRegistry.json",
|
||||
"generated-artifacts/ERC20BridgeSampler.json",
|
||||
"generated-artifacts/IERC20BridgeSampler.json",
|
||||
"generated-artifacts/ILiquidityProvider.json",
|
||||
"generated-artifacts/ILiquidityProviderRegistry.json",
|
||||
"test/generated-artifacts/DummyLiquidityProvider.json",
|
||||
"test/generated-artifacts/DummyLiquidityProviderRegistry.json",
|
||||
"test/generated-artifacts/ERC20BridgeSampler.json",
|
||||
"test/generated-artifacts/ICurve.json",
|
||||
"test/generated-artifacts/IDevUtils.json",
|
||||
|
||||
@@ -21,8 +21,9 @@ import {
|
||||
} from './types';
|
||||
import { assert } from './utils/assert';
|
||||
import { calculateLiquidity } from './utils/calculate_liquidity';
|
||||
import { DexOrderSampler, MarketOperationUtils } from './utils/market_operation_utils';
|
||||
import { MarketOperationUtils } from './utils/market_operation_utils';
|
||||
import { dummyOrderUtils } from './utils/market_operation_utils/dummy_order_utils';
|
||||
import { DexOrderSampler } from './utils/market_operation_utils/sampler';
|
||||
import { orderPrunerUtils } from './utils/order_prune_utils';
|
||||
import { OrderStateUtils } from './utils/order_state_utils';
|
||||
import { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
||||
@@ -144,11 +145,13 @@ export class SwapQuoter {
|
||||
* @return An instance of SwapQuoter
|
||||
*/
|
||||
constructor(supportedProvider: SupportedProvider, orderbook: Orderbook, options: Partial<SwapQuoterOpts> = {}) {
|
||||
const { chainId, expiryBufferMs, permittedOrderFeeTypes, samplerGasLimit } = _.merge(
|
||||
{},
|
||||
constants.DEFAULT_SWAP_QUOTER_OPTS,
|
||||
options,
|
||||
);
|
||||
const {
|
||||
chainId,
|
||||
expiryBufferMs,
|
||||
permittedOrderFeeTypes,
|
||||
samplerGasLimit,
|
||||
liquidityProviderRegistryAddress,
|
||||
} = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options);
|
||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||
assert.isValidOrderbook('orderbook', orderbook);
|
||||
assert.isNumber('chainId', chainId);
|
||||
@@ -167,10 +170,15 @@ export class SwapQuoter {
|
||||
gas: samplerGasLimit,
|
||||
}),
|
||||
);
|
||||
this._marketOperationUtils = new MarketOperationUtils(sampler, this._contractAddresses, {
|
||||
chainId,
|
||||
exchangeAddress: this._contractAddresses.exchange,
|
||||
});
|
||||
this._marketOperationUtils = new MarketOperationUtils(
|
||||
sampler,
|
||||
this._contractAddresses,
|
||||
{
|
||||
chainId,
|
||||
exchangeAddress: this._contractAddresses.exchange,
|
||||
},
|
||||
liquidityProviderRegistryAddress,
|
||||
);
|
||||
this._swapQuoteCalculator = new SwapQuoteCalculator(this._protocolFeeUtils, this._marketOperationUtils);
|
||||
}
|
||||
|
||||
|
||||
@@ -212,6 +212,7 @@ export interface SwapQuoterOpts extends OrderPrunerOpts {
|
||||
expiryBufferMs: number;
|
||||
contractAddresses?: ContractAddresses;
|
||||
samplerGasLimit?: number;
|
||||
liquidityProviderRegistryAddress?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { assert } from '@0x/assert';
|
||||
import { ContractAddresses } from '@0x/contract-addresses';
|
||||
import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
@@ -48,6 +49,7 @@ export class CreateOrderUtils {
|
||||
outputToken: string,
|
||||
path: CollapsedFill[],
|
||||
bridgeSlippage: number,
|
||||
liquidityProviderAddress?: string,
|
||||
): OptimizedMarketOrder[] {
|
||||
const orders: OptimizedMarketOrder[] = [];
|
||||
for (const fill of path) {
|
||||
@@ -58,7 +60,7 @@ export class CreateOrderUtils {
|
||||
createBridgeOrder(
|
||||
orderDomain,
|
||||
fill,
|
||||
this._getBridgeAddressFromSource(fill.source),
|
||||
this._getBridgeAddressFromSource(fill.source, liquidityProviderAddress),
|
||||
outputToken,
|
||||
inputToken,
|
||||
bridgeSlippage,
|
||||
@@ -76,6 +78,7 @@ export class CreateOrderUtils {
|
||||
outputToken: string,
|
||||
path: CollapsedFill[],
|
||||
bridgeSlippage: number,
|
||||
liquidityProviderAddress?: string,
|
||||
): OptimizedMarketOrder[] {
|
||||
const orders: OptimizedMarketOrder[] = [];
|
||||
for (const fill of path) {
|
||||
@@ -86,7 +89,7 @@ export class CreateOrderUtils {
|
||||
createBridgeOrder(
|
||||
orderDomain,
|
||||
fill,
|
||||
this._getBridgeAddressFromSource(fill.source),
|
||||
this._getBridgeAddressFromSource(fill.source, liquidityProviderAddress),
|
||||
inputToken,
|
||||
outputToken,
|
||||
bridgeSlippage,
|
||||
@@ -98,7 +101,7 @@ export class CreateOrderUtils {
|
||||
return orders;
|
||||
}
|
||||
|
||||
private _getBridgeAddressFromSource(source: ERC20BridgeSource): string {
|
||||
private _getBridgeAddressFromSource(source: ERC20BridgeSource, liquidityProviderAddress?: string): string {
|
||||
switch (source) {
|
||||
case ERC20BridgeSource.Eth2Dai:
|
||||
return this._contractAddress.eth2DaiBridge;
|
||||
@@ -111,6 +114,14 @@ export class CreateOrderUtils {
|
||||
case ERC20BridgeSource.CurveUsdcDaiUsdtTusd:
|
||||
case ERC20BridgeSource.CurveUsdcDaiUsdtBusd:
|
||||
return this._contractAddress.curveBridge;
|
||||
case ERC20BridgeSource.LiquidityProvider:
|
||||
if (liquidityProviderAddress === undefined) {
|
||||
throw new Error(
|
||||
'Cannot create a LiquidityProvider order without a LiquidityProvider pool address.',
|
||||
);
|
||||
}
|
||||
assert.isETHAddressHex('liquidityProviderAddress', liquidityProviderAddress);
|
||||
return liquidityProviderAddress;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ContractAddresses } from '@0x/contract-addresses';
|
||||
import { assetDataUtils, ERC20AssetData, orderCalculationUtils } from '@0x/order-utils';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { BigNumber, NULL_ADDRESS } from '@0x/utils';
|
||||
|
||||
import { constants } from '../../constants';
|
||||
import { MarketOperation, SignedOrderWithFillableAmounts } from '../../types';
|
||||
@@ -46,6 +46,7 @@ export class MarketOperationUtils {
|
||||
private readonly _sampler: DexOrderSampler,
|
||||
contractAddresses: ContractAddresses,
|
||||
private readonly _orderDomain: OrderDomain,
|
||||
private readonly _liquidityProviderRegistry: string = NULL_ADDRESS,
|
||||
) {
|
||||
this._createOrderUtils = new CreateOrderUtils(contractAddresses);
|
||||
this._wethAddress = contractAddresses.etherToken;
|
||||
@@ -72,23 +73,40 @@ export class MarketOperationUtils {
|
||||
...opts,
|
||||
};
|
||||
const [makerToken, takerToken] = getOrderTokens(nativeOrders[0]);
|
||||
const [fillableAmounts, ethToMakerAssetRate, dexQuotes] = await this._sampler.executeAsync(
|
||||
const [
|
||||
fillableAmounts,
|
||||
liquidityProviderAddress,
|
||||
ethToMakerAssetRate,
|
||||
dexQuotes,
|
||||
] = await this._sampler.executeAsync(
|
||||
DexOrderSampler.ops.getOrderFillableTakerAmounts(nativeOrders),
|
||||
DexOrderSampler.ops.getLiquidityProviderFromRegistry(
|
||||
this._liquidityProviderRegistry,
|
||||
takerToken,
|
||||
makerToken,
|
||||
),
|
||||
makerToken.toLowerCase() === this._wethAddress.toLowerCase()
|
||||
? DexOrderSampler.ops.constant(new BigNumber(1))
|
||||
: DexOrderSampler.ops.getMedianSellRate(
|
||||
difference(FEE_QUOTE_SOURCES, _opts.excludedSources),
|
||||
difference(FEE_QUOTE_SOURCES, _opts.excludedSources).concat(
|
||||
this._liquidityProviderSourceIfAvailable(_opts.excludedSources),
|
||||
),
|
||||
makerToken,
|
||||
this._wethAddress,
|
||||
ONE_ETHER,
|
||||
this._liquidityProviderRegistry,
|
||||
),
|
||||
DexOrderSampler.ops.getSellQuotes(
|
||||
difference(SELL_SOURCES, _opts.excludedSources),
|
||||
difference(SELL_SOURCES, _opts.excludedSources).concat(
|
||||
this._liquidityProviderSourceIfAvailable(_opts.excludedSources),
|
||||
),
|
||||
makerToken,
|
||||
takerToken,
|
||||
getSampleAmounts(takerAmount, _opts.numSamples, _opts.sampleDistributionBase),
|
||||
this._liquidityProviderRegistry,
|
||||
),
|
||||
);
|
||||
|
||||
const nativeOrdersWithFillableAmounts = createSignedOrdersWithFillableAmounts(
|
||||
nativeOrders,
|
||||
fillableAmounts,
|
||||
@@ -130,6 +148,7 @@ export class MarketOperationUtils {
|
||||
makerToken,
|
||||
collapsePath(optimalPath, false),
|
||||
_opts.bridgeSlippage,
|
||||
liquidityProviderAddress,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -154,21 +173,37 @@ export class MarketOperationUtils {
|
||||
...opts,
|
||||
};
|
||||
const [makerToken, takerToken] = getOrderTokens(nativeOrders[0]);
|
||||
const [fillableAmounts, ethToTakerAssetRate, dexQuotes] = await this._sampler.executeAsync(
|
||||
const [
|
||||
fillableAmounts,
|
||||
liquidityProviderAddress,
|
||||
ethToTakerAssetRate,
|
||||
dexQuotes,
|
||||
] = await this._sampler.executeAsync(
|
||||
DexOrderSampler.ops.getOrderFillableMakerAmounts(nativeOrders),
|
||||
DexOrderSampler.ops.getLiquidityProviderFromRegistry(
|
||||
this._liquidityProviderRegistry,
|
||||
takerToken,
|
||||
makerToken,
|
||||
),
|
||||
takerToken.toLowerCase() === this._wethAddress.toLowerCase()
|
||||
? DexOrderSampler.ops.constant(new BigNumber(1))
|
||||
: DexOrderSampler.ops.getMedianSellRate(
|
||||
difference(FEE_QUOTE_SOURCES, _opts.excludedSources),
|
||||
difference(FEE_QUOTE_SOURCES, _opts.excludedSources).concat(
|
||||
this._liquidityProviderSourceIfAvailable(_opts.excludedSources),
|
||||
),
|
||||
takerToken,
|
||||
this._wethAddress,
|
||||
ONE_ETHER,
|
||||
this._liquidityProviderRegistry,
|
||||
),
|
||||
DexOrderSampler.ops.getBuyQuotes(
|
||||
difference(BUY_SOURCES, _opts.excludedSources),
|
||||
difference(BUY_SOURCES, _opts.excludedSources).concat(
|
||||
this._liquidityProviderSourceIfAvailable(_opts.excludedSources),
|
||||
),
|
||||
makerToken,
|
||||
takerToken,
|
||||
getSampleAmounts(makerAmount, _opts.numSamples, _opts.sampleDistributionBase),
|
||||
this._liquidityProviderRegistry,
|
||||
),
|
||||
);
|
||||
const signedOrderWithFillableAmounts = this._createBuyOrdersPathFromSamplerResultIfExists(
|
||||
@@ -178,6 +213,7 @@ export class MarketOperationUtils {
|
||||
dexQuotes,
|
||||
ethToTakerAssetRate,
|
||||
_opts,
|
||||
liquidityProviderAddress,
|
||||
);
|
||||
if (!signedOrderWithFillableAmounts) {
|
||||
throw new Error(AggregationError.NoOptimalPath);
|
||||
@@ -188,6 +224,9 @@ export class MarketOperationUtils {
|
||||
/**
|
||||
* gets the orders required for a batch of market buy operations by (potentially) merging native orders with
|
||||
* generated bridge orders.
|
||||
*
|
||||
* NOTE: Currently `getBatchMarketBuyOrdersAsync()` does not support external liquidity providers.
|
||||
*
|
||||
* @param batchNativeOrders Batch of Native orders.
|
||||
* @param makerAmounts Array amount of maker asset to buy for each batch.
|
||||
* @param opts Options object.
|
||||
@@ -240,6 +279,13 @@ export class MarketOperationUtils {
|
||||
);
|
||||
}
|
||||
|
||||
private _liquidityProviderSourceIfAvailable(excludedSources: ERC20BridgeSource[]): ERC20BridgeSource[] {
|
||||
return this._liquidityProviderRegistry !== NULL_ADDRESS &&
|
||||
!excludedSources.includes(ERC20BridgeSource.LiquidityProvider)
|
||||
? [ERC20BridgeSource.LiquidityProvider]
|
||||
: [];
|
||||
}
|
||||
|
||||
private _createBuyOrdersPathFromSamplerResultIfExists(
|
||||
nativeOrders: SignedOrder[],
|
||||
makerAmount: BigNumber,
|
||||
@@ -247,6 +293,7 @@ export class MarketOperationUtils {
|
||||
dexQuotes: DexSample[][],
|
||||
ethToTakerAssetRate: BigNumber,
|
||||
opts: GetMarketOrdersOpts,
|
||||
liquidityProviderAddress?: string,
|
||||
): OptimizedMarketOrder[] | undefined {
|
||||
const nativeOrdersWithFillableAmounts = createSignedOrdersWithFillableAmounts(
|
||||
nativeOrders,
|
||||
@@ -289,6 +336,7 @@ export class MarketOperationUtils {
|
||||
outputToken,
|
||||
collapsePath(optimalPath, true),
|
||||
opts.bridgeSlippage,
|
||||
liquidityProviderAddress,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -470,6 +518,9 @@ function sourceToFillFlags(source: ERC20BridgeSource): number {
|
||||
if (source === ERC20BridgeSource.Uniswap) {
|
||||
return FillFlags.SourceUniswap;
|
||||
}
|
||||
if (source === ERC20BridgeSource.LiquidityProvider) {
|
||||
return FillFlags.SourceLiquidityPool;
|
||||
}
|
||||
return FillFlags.SourceNative;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,283 +1,8 @@
|
||||
import { IERC20BridgeSamplerContract } from '@0x/contract-wrappers';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { constants } from '../../constants';
|
||||
|
||||
import { DexSample, ERC20BridgeSource } from './types';
|
||||
|
||||
/**
|
||||
* A composable operation the be run in `DexOrderSampler.executeAsync()`.
|
||||
*/
|
||||
export interface BatchedOperation<TResult> {
|
||||
encodeCall(contract: IERC20BridgeSamplerContract): string;
|
||||
handleCallResultsAsync(contract: IERC20BridgeSamplerContract, callResults: string): Promise<TResult>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Composable operations that can be batched in a single transaction,
|
||||
* for use with `DexOrderSampler.executeAsync()`.
|
||||
*/
|
||||
const samplerOperations = {
|
||||
getOrderFillableTakerAmounts(orders: SignedOrder[]): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.getOrderFillableTakerAssetAmounts(orders, orders.map(o => o.signature))
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('getOrderFillableTakerAssetAmounts', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getOrderFillableMakerAmounts(orders: SignedOrder[]): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.getOrderFillableMakerAssetAmounts(orders, orders.map(o => o.signature))
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('getOrderFillableMakerAssetAmounts', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getKyberSellQuotes(
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
takerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleSellsFromKyberNetwork(takerToken, makerToken, takerFillAmounts)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('sampleSellsFromKyberNetwork', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getUniswapSellQuotes(
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
takerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleSellsFromUniswap(takerToken, makerToken, takerFillAmounts)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('sampleSellsFromUniswap', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getEth2DaiSellQuotes(
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
takerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleSellsFromEth2Dai(takerToken, makerToken, takerFillAmounts)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('sampleSellsFromEth2Dai', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getCurveSellQuotes(
|
||||
curveAddress: string,
|
||||
fromTokenIdx: number,
|
||||
toTokenIdx: number,
|
||||
takerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleSellsFromCurve(
|
||||
curveAddress,
|
||||
new BigNumber(fromTokenIdx),
|
||||
new BigNumber(toTokenIdx),
|
||||
takerFillAmounts,
|
||||
)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('sampleSellsFromCurve', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getUniswapBuyQuotes(
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
makerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleBuysFromUniswap(takerToken, makerToken, makerFillAmounts)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('sampleBuysFromUniswap', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getEth2DaiBuyQuotes(
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
makerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleBuysFromEth2Dai(takerToken, makerToken, makerFillAmounts)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('sampleBuysFromEth2Dai', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getMedianSellRate(
|
||||
sources: ERC20BridgeSource[],
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
takerFillAmount: BigNumber,
|
||||
): BatchedOperation<BigNumber> {
|
||||
const getSellQuotes = samplerOperations.getSellQuotes(sources, makerToken, takerToken, [takerFillAmount]);
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
const subCalls = [getSellQuotes.encodeCall(contract)];
|
||||
return contract.batchCall(subCalls).getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
const rawSubCallResults = contract.getABIDecodedReturnData<string[]>('batchCall', callResults);
|
||||
const samples = await getSellQuotes.handleCallResultsAsync(contract, rawSubCallResults[0]);
|
||||
if (samples.length === 0) {
|
||||
return new BigNumber(0);
|
||||
}
|
||||
const flatSortedSamples = samples
|
||||
.reduce((acc, v) => acc.concat(...v))
|
||||
.sort((a, b) => a.output.comparedTo(b.output));
|
||||
if (flatSortedSamples.length === 0) {
|
||||
return new BigNumber(0);
|
||||
}
|
||||
const medianSample = flatSortedSamples[Math.floor(flatSortedSamples.length / 2)];
|
||||
return medianSample.output.div(medianSample.input);
|
||||
},
|
||||
};
|
||||
},
|
||||
constant<T>(result: T): BatchedOperation<T> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return '0x';
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return result;
|
||||
},
|
||||
};
|
||||
},
|
||||
getSellQuotes(
|
||||
sources: ERC20BridgeSource[],
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
takerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<DexSample[][]> {
|
||||
const subOps = sources
|
||||
.map(source => {
|
||||
let batchedOperation;
|
||||
if (source === ERC20BridgeSource.Eth2Dai) {
|
||||
batchedOperation = samplerOperations.getEth2DaiSellQuotes(makerToken, takerToken, takerFillAmounts);
|
||||
} else if (source === ERC20BridgeSource.Uniswap) {
|
||||
batchedOperation = samplerOperations.getUniswapSellQuotes(makerToken, takerToken, takerFillAmounts);
|
||||
} else if (source === ERC20BridgeSource.Kyber) {
|
||||
batchedOperation = samplerOperations.getKyberSellQuotes(makerToken, takerToken, takerFillAmounts);
|
||||
} else if (Object.keys(constants.DEFAULT_CURVE_OPTS).includes(source)) {
|
||||
const { curveAddress, tokens } = constants.DEFAULT_CURVE_OPTS[source];
|
||||
const fromTokenIdx = tokens.indexOf(takerToken);
|
||||
const toTokenIdx = tokens.indexOf(makerToken);
|
||||
if (fromTokenIdx !== -1 && toTokenIdx !== -1) {
|
||||
batchedOperation = samplerOperations.getCurveSellQuotes(
|
||||
curveAddress,
|
||||
fromTokenIdx,
|
||||
toTokenIdx,
|
||||
takerFillAmounts,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Unsupported sell sample source: ${source}`);
|
||||
}
|
||||
return { batchedOperation, source };
|
||||
})
|
||||
.filter(op => op.batchedOperation) as Array<{
|
||||
batchedOperation: BatchedOperation<BigNumber[]>;
|
||||
source: ERC20BridgeSource;
|
||||
}>;
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
const subCalls = subOps.map(op => op.batchedOperation.encodeCall(contract));
|
||||
return contract.batchCall(subCalls).getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
const rawSubCallResults = contract.getABIDecodedReturnData<string[]>('batchCall', callResults);
|
||||
const samples = await Promise.all(
|
||||
subOps.map(async (op, i) =>
|
||||
op.batchedOperation.handleCallResultsAsync(contract, rawSubCallResults[i]),
|
||||
),
|
||||
);
|
||||
return subOps.map((op, i) => {
|
||||
return samples[i].map((output, j) => ({
|
||||
source: op.source,
|
||||
output,
|
||||
input: takerFillAmounts[j],
|
||||
}));
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
getBuyQuotes(
|
||||
sources: ERC20BridgeSource[],
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
makerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<DexSample[][]> {
|
||||
const subOps = sources.map(source => {
|
||||
if (source === ERC20BridgeSource.Eth2Dai) {
|
||||
return samplerOperations.getEth2DaiBuyQuotes(makerToken, takerToken, makerFillAmounts);
|
||||
} else if (source === ERC20BridgeSource.Uniswap) {
|
||||
return samplerOperations.getUniswapBuyQuotes(makerToken, takerToken, makerFillAmounts);
|
||||
} else {
|
||||
throw new Error(`Unsupported buy sample source: ${source}`);
|
||||
}
|
||||
});
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
const subCalls = subOps.map(op => op.encodeCall(contract));
|
||||
return contract.batchCall(subCalls).getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
const rawSubCallResults = contract.getABIDecodedReturnData<string[]>('batchCall', callResults);
|
||||
const samples = await Promise.all(
|
||||
subOps.map(async (op, i) => op.handleCallResultsAsync(contract, rawSubCallResults[i])),
|
||||
);
|
||||
return sources.map((source, i) => {
|
||||
return samples[i].map((output, j) => ({
|
||||
source,
|
||||
output,
|
||||
input: makerFillAmounts[j],
|
||||
}));
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
import { samplerOperations } from './sampler_operations';
|
||||
import { BatchedOperation } from './types';
|
||||
|
||||
/**
|
||||
* Generate sample amounts up to `maxFillAmount`.
|
||||
|
||||
@@ -0,0 +1,368 @@
|
||||
import { BigNumber, ERC20BridgeSource, SignedOrder } from '../..';
|
||||
import { constants } from '../../constants';
|
||||
|
||||
import { BatchedOperation, DexSample } from './types';
|
||||
|
||||
/**
|
||||
* Composable operations that can be batched in a single transaction,
|
||||
* for use with `DexOrderSampler.executeAsync()`.
|
||||
*/
|
||||
export const samplerOperations = {
|
||||
getOrderFillableTakerAmounts(orders: SignedOrder[]): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.getOrderFillableTakerAssetAmounts(orders, orders.map(o => o.signature))
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('getOrderFillableTakerAssetAmounts', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getOrderFillableMakerAmounts(orders: SignedOrder[]): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.getOrderFillableMakerAssetAmounts(orders, orders.map(o => o.signature))
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('getOrderFillableMakerAssetAmounts', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getKyberSellQuotes(
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
takerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleSellsFromKyberNetwork(takerToken, makerToken, takerFillAmounts)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('sampleSellsFromKyberNetwork', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getUniswapSellQuotes(
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
takerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleSellsFromUniswap(takerToken, makerToken, takerFillAmounts)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('sampleSellsFromUniswap', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getLiquidityProviderSellQuotes(
|
||||
liquidityProviderRegistryAddress: string,
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
takerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleSellsFromLiquidityProviderRegistry(
|
||||
liquidityProviderRegistryAddress,
|
||||
takerToken,
|
||||
makerToken,
|
||||
takerFillAmounts,
|
||||
)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>(
|
||||
'sampleSellsFromLiquidityProviderRegistry',
|
||||
callResults,
|
||||
);
|
||||
},
|
||||
};
|
||||
},
|
||||
getLiquidityProviderBuyQuotes(
|
||||
liquidityProviderRegistryAddress: string,
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
makerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleBuysFromLiquidityProviderRegistry(
|
||||
liquidityProviderRegistryAddress,
|
||||
takerToken,
|
||||
makerToken,
|
||||
makerFillAmounts,
|
||||
)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>(
|
||||
'sampleBuysFromLiquidityProviderRegistry',
|
||||
callResults,
|
||||
);
|
||||
},
|
||||
};
|
||||
},
|
||||
getEth2DaiSellQuotes(
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
takerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleSellsFromEth2Dai(takerToken, makerToken, takerFillAmounts)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('sampleSellsFromEth2Dai', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getCurveSellQuotes(
|
||||
curveAddress: string,
|
||||
fromTokenIdx: number,
|
||||
toTokenIdx: number,
|
||||
takerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleSellsFromCurve(
|
||||
curveAddress,
|
||||
new BigNumber(fromTokenIdx),
|
||||
new BigNumber(toTokenIdx),
|
||||
takerFillAmounts,
|
||||
)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('sampleSellsFromCurve', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getUniswapBuyQuotes(
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
makerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleBuysFromUniswap(takerToken, makerToken, makerFillAmounts)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('sampleBuysFromUniswap', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getEth2DaiBuyQuotes(
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
makerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleBuysFromEth2Dai(takerToken, makerToken, makerFillAmounts)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('sampleBuysFromEth2Dai', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getMedianSellRate(
|
||||
sources: ERC20BridgeSource[],
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
takerFillAmount: BigNumber,
|
||||
liquidityProviderRegistryAddress?: string | undefined,
|
||||
): BatchedOperation<BigNumber> {
|
||||
const getSellQuotes = samplerOperations.getSellQuotes(
|
||||
sources,
|
||||
makerToken,
|
||||
takerToken,
|
||||
[takerFillAmount],
|
||||
liquidityProviderRegistryAddress,
|
||||
);
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
const subCalls = [getSellQuotes.encodeCall(contract)];
|
||||
return contract.batchCall(subCalls).getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
const rawSubCallResults = contract.getABIDecodedReturnData<string[]>('batchCall', callResults);
|
||||
const samples = await getSellQuotes.handleCallResultsAsync(contract, rawSubCallResults[0]);
|
||||
if (samples.length === 0) {
|
||||
return new BigNumber(0);
|
||||
}
|
||||
const flatSortedSamples = samples
|
||||
.reduce((acc, v) => acc.concat(...v))
|
||||
.sort((a, b) => a.output.comparedTo(b.output));
|
||||
if (flatSortedSamples.length === 0) {
|
||||
return new BigNumber(0);
|
||||
}
|
||||
const medianSample = flatSortedSamples[Math.floor(flatSortedSamples.length / 2)];
|
||||
return medianSample.output.div(medianSample.input);
|
||||
},
|
||||
};
|
||||
},
|
||||
constant<T>(result: T): BatchedOperation<T> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return '0x';
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return result;
|
||||
},
|
||||
};
|
||||
},
|
||||
getLiquidityProviderFromRegistry(
|
||||
registryAddress: string,
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
): BatchedOperation<string> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.getLiquidityProviderFromRegistry(registryAddress, takerToken, makerToken)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<string>('getLiquidityProviderFromRegistry', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getSellQuotes(
|
||||
sources: ERC20BridgeSource[],
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
takerFillAmounts: BigNumber[],
|
||||
liquidityProviderRegistryAddress?: string | undefined,
|
||||
): BatchedOperation<DexSample[][]> {
|
||||
const subOps = sources
|
||||
.map(source => {
|
||||
let batchedOperation;
|
||||
if (source === ERC20BridgeSource.Eth2Dai) {
|
||||
batchedOperation = samplerOperations.getEth2DaiSellQuotes(makerToken, takerToken, takerFillAmounts);
|
||||
} else if (source === ERC20BridgeSource.Uniswap) {
|
||||
batchedOperation = samplerOperations.getUniswapSellQuotes(makerToken, takerToken, takerFillAmounts);
|
||||
} else if (source === ERC20BridgeSource.Kyber) {
|
||||
batchedOperation = samplerOperations.getKyberSellQuotes(makerToken, takerToken, takerFillAmounts);
|
||||
} else if (Object.keys(constants.DEFAULT_CURVE_OPTS).includes(source)) {
|
||||
const { curveAddress, tokens } = constants.DEFAULT_CURVE_OPTS[source];
|
||||
const fromTokenIdx = tokens.indexOf(takerToken);
|
||||
const toTokenIdx = tokens.indexOf(makerToken);
|
||||
if (fromTokenIdx !== -1 && toTokenIdx !== -1) {
|
||||
batchedOperation = samplerOperations.getCurveSellQuotes(
|
||||
curveAddress,
|
||||
fromTokenIdx,
|
||||
toTokenIdx,
|
||||
takerFillAmounts,
|
||||
);
|
||||
}
|
||||
} else if (source === ERC20BridgeSource.LiquidityProvider) {
|
||||
if (liquidityProviderRegistryAddress === undefined) {
|
||||
throw new Error(
|
||||
'Cannot sample liquidity from a LiquidityProvider liquidity pool, if a registry is not provided.',
|
||||
);
|
||||
}
|
||||
batchedOperation = samplerOperations.getLiquidityProviderSellQuotes(
|
||||
liquidityProviderRegistryAddress,
|
||||
makerToken,
|
||||
takerToken,
|
||||
takerFillAmounts,
|
||||
);
|
||||
} else {
|
||||
throw new Error(`Unsupported sell sample source: ${source}`);
|
||||
}
|
||||
return { batchedOperation, source };
|
||||
})
|
||||
.filter(op => op.batchedOperation) as Array<{
|
||||
batchedOperation: BatchedOperation<BigNumber[]>;
|
||||
source: ERC20BridgeSource;
|
||||
}>;
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
const subCalls = subOps.map(op => op.batchedOperation.encodeCall(contract));
|
||||
return contract.batchCall(subCalls).getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
const rawSubCallResults = contract.getABIDecodedReturnData<string[]>('batchCall', callResults);
|
||||
const samples = await Promise.all(
|
||||
subOps.map(async (op, i) =>
|
||||
op.batchedOperation.handleCallResultsAsync(contract, rawSubCallResults[i]),
|
||||
),
|
||||
);
|
||||
return subOps.map((op, i) => {
|
||||
return samples[i].map((output, j) => ({
|
||||
source: op.source,
|
||||
output,
|
||||
input: takerFillAmounts[j],
|
||||
}));
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
getBuyQuotes(
|
||||
sources: ERC20BridgeSource[],
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
makerFillAmounts: BigNumber[],
|
||||
liquidityProviderRegistryAddress?: string | undefined,
|
||||
): BatchedOperation<DexSample[][]> {
|
||||
const subOps = sources.map(source => {
|
||||
if (source === ERC20BridgeSource.Eth2Dai) {
|
||||
return samplerOperations.getEth2DaiBuyQuotes(makerToken, takerToken, makerFillAmounts);
|
||||
} else if (source === ERC20BridgeSource.Uniswap) {
|
||||
return samplerOperations.getUniswapBuyQuotes(makerToken, takerToken, makerFillAmounts);
|
||||
} else if (source === ERC20BridgeSource.LiquidityProvider) {
|
||||
if (liquidityProviderRegistryAddress === undefined) {
|
||||
throw new Error(
|
||||
'Cannot sample liquidity from a LiquidityProvider liquidity pool, if a registry is not provided.',
|
||||
);
|
||||
}
|
||||
return samplerOperations.getLiquidityProviderBuyQuotes(
|
||||
liquidityProviderRegistryAddress,
|
||||
makerToken,
|
||||
takerToken,
|
||||
makerFillAmounts,
|
||||
);
|
||||
} else {
|
||||
throw new Error(`Unsupported buy sample source: ${source}`);
|
||||
}
|
||||
});
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
const subCalls = subOps.map(op => op.encodeCall(contract));
|
||||
return contract.batchCall(subCalls).getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
const rawSubCallResults = contract.getABIDecodedReturnData<string[]>('batchCall', callResults);
|
||||
const samples = await Promise.all(
|
||||
subOps.map(async (op, i) => op.handleCallResultsAsync(contract, rawSubCallResults[i])),
|
||||
);
|
||||
return sources.map((source, i) => {
|
||||
return samples[i].map((output, j) => ({
|
||||
source,
|
||||
output,
|
||||
input: makerFillAmounts[j],
|
||||
}));
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
import { IERC20BridgeSamplerContract } from '@0x/contract-wrappers';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { SignedOrderWithFillableAmounts } from '../../types';
|
||||
@@ -31,6 +32,7 @@ export enum ERC20BridgeSource {
|
||||
CurveUsdcDai = 'Curve_USDC_DAI',
|
||||
CurveUsdcDaiUsdt = 'Curve_USDC_DAI_USDT',
|
||||
CurveUsdcDaiUsdtTusd = 'Curve_USDC_DAI_USDT_TUSD',
|
||||
LiquidityProvider = 'LiquidityProvider',
|
||||
CurveUsdcDaiUsdtBusd = 'Curve_USDC_DAI_USDT_BUSD',
|
||||
}
|
||||
|
||||
@@ -61,6 +63,7 @@ export enum FillFlags {
|
||||
SourceUniswap = 0x2,
|
||||
SourceEth2Dai = 0x4,
|
||||
SourceKyber = 0x8,
|
||||
SourceLiquidityPool = 0x10,
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,3 +178,11 @@ export interface GetMarketOrdersOpts {
|
||||
*/
|
||||
fees: { [source: string]: BigNumber };
|
||||
}
|
||||
|
||||
/**
|
||||
* A composable operation the be run in `DexOrderSampler.executeAsync()`.
|
||||
*/
|
||||
export interface BatchedOperation<TResult> {
|
||||
encodeCall(contract: IERC20BridgeSamplerContract): string;
|
||||
handleCallResultsAsync(contract: IERC20BridgeSamplerContract, callResults: string): Promise<TResult>;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { constants, expect, getRandomFloat, getRandomInteger, randomAddress } from '@0x/contracts-test-utils';
|
||||
import {
|
||||
constants,
|
||||
expect,
|
||||
getRandomFloat,
|
||||
getRandomInteger,
|
||||
randomAddress,
|
||||
toBaseUnitAmount,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
import { BigNumber, hexUtils } from '@0x/utils';
|
||||
@@ -134,6 +141,72 @@ describe('DexSampler tests', () => {
|
||||
expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts);
|
||||
});
|
||||
|
||||
it('getLiquidityProviderSellQuotes()', async () => {
|
||||
const expectedMakerToken = randomAddress();
|
||||
const expectedTakerToken = randomAddress();
|
||||
const registry = randomAddress();
|
||||
const sampler = new MockSamplerContract({
|
||||
sampleSellsFromLiquidityProviderRegistry: (registryAddress, takerToken, makerToken, fillAmounts) => {
|
||||
expect(registryAddress).to.eq(registry);
|
||||
expect(takerToken).to.eq(expectedTakerToken);
|
||||
expect(makerToken).to.eq(expectedMakerToken);
|
||||
return [toBaseUnitAmount(1001)];
|
||||
},
|
||||
});
|
||||
const dexOrderSampler = new DexOrderSampler(sampler);
|
||||
const [result] = await dexOrderSampler.executeAsync(
|
||||
DexOrderSampler.ops.getSellQuotes(
|
||||
[ERC20BridgeSource.LiquidityProvider],
|
||||
expectedMakerToken,
|
||||
expectedTakerToken,
|
||||
[toBaseUnitAmount(1000)],
|
||||
registry,
|
||||
),
|
||||
);
|
||||
expect(result).to.deep.equal([
|
||||
[
|
||||
{
|
||||
source: 'LiquidityProvider',
|
||||
output: toBaseUnitAmount(1001),
|
||||
input: toBaseUnitAmount(1000),
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it('getLiquidityProviderBuyQuotes()', async () => {
|
||||
const expectedMakerToken = randomAddress();
|
||||
const expectedTakerToken = randomAddress();
|
||||
const registry = randomAddress();
|
||||
const sampler = new MockSamplerContract({
|
||||
sampleBuysFromLiquidityProviderRegistry: (registryAddress, takerToken, makerToken, fillAmounts) => {
|
||||
expect(registryAddress).to.eq(registry);
|
||||
expect(takerToken).to.eq(expectedTakerToken);
|
||||
expect(makerToken).to.eq(expectedMakerToken);
|
||||
return [toBaseUnitAmount(999)];
|
||||
},
|
||||
});
|
||||
const dexOrderSampler = new DexOrderSampler(sampler);
|
||||
const [result] = await dexOrderSampler.executeAsync(
|
||||
DexOrderSampler.ops.getBuyQuotes(
|
||||
[ERC20BridgeSource.LiquidityProvider],
|
||||
expectedMakerToken,
|
||||
expectedTakerToken,
|
||||
[toBaseUnitAmount(1000)],
|
||||
registry,
|
||||
),
|
||||
);
|
||||
expect(result).to.deep.equal([
|
||||
[
|
||||
{
|
||||
source: 'LiquidityProvider',
|
||||
output: toBaseUnitAmount(999),
|
||||
input: toBaseUnitAmount(1000),
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it('getEth2DaiSellQuotes()', async () => {
|
||||
const expectedTakerToken = randomAddress();
|
||||
const expectedMakerToken = randomAddress();
|
||||
|
||||
@@ -8,10 +8,10 @@ import {
|
||||
Numberish,
|
||||
randomAddress,
|
||||
} from '@0x/contracts-test-utils';
|
||||
|
||||
import { Web3Wrapper } from '@0x/dev-utils';
|
||||
import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
import { BigNumber, hexUtils } from '@0x/utils';
|
||||
import { AssetProxyId, ERC20BridgeAssetData, SignedOrder } from '@0x/types';
|
||||
import { BigNumber, fromTokenUnitAmount, hexUtils, NULL_ADDRESS } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { constants as assetSwapperConstants } from '../src/constants';
|
||||
@@ -141,6 +141,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
fillAmounts: BigNumber[],
|
||||
liquidityProviderAddress?: string,
|
||||
) => DexSample[][];
|
||||
|
||||
function createGetMultipleSellQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation {
|
||||
@@ -155,6 +156,28 @@ describe('MarketOperationUtils tests', () => {
|
||||
};
|
||||
}
|
||||
|
||||
function callTradeOperationAndRetainLiquidityProviderParams(
|
||||
tradeOperation: (rates: RatesBySource) => GetMultipleQuotesOperation,
|
||||
rates: RatesBySource,
|
||||
): [{ sources: ERC20BridgeSource[]; liquidityProviderAddress?: string }, GetMultipleQuotesOperation] {
|
||||
const liquidityPoolParams: { sources: ERC20BridgeSource[]; liquidityProviderAddress?: string } = {
|
||||
sources: [],
|
||||
liquidityProviderAddress: undefined,
|
||||
};
|
||||
const fn = (
|
||||
sources: ERC20BridgeSource[],
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
fillAmounts: BigNumber[],
|
||||
liquidityProviderAddress?: string,
|
||||
) => {
|
||||
liquidityPoolParams.liquidityProviderAddress = liquidityProviderAddress;
|
||||
liquidityPoolParams.sources = sources;
|
||||
return tradeOperation(rates)(sources, makerToken, takerToken, fillAmounts, liquidityProviderAddress);
|
||||
};
|
||||
return [liquidityPoolParams, fn];
|
||||
}
|
||||
|
||||
function createGetMultipleBuyQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation {
|
||||
return (sources: ERC20BridgeSource[], makerToken: string, takerToken: string, fillAmounts: BigNumber[]) => {
|
||||
return sources.map(s =>
|
||||
@@ -172,14 +195,47 @@ describe('MarketOperationUtils tests', () => {
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
fillAmounts: BigNumber[],
|
||||
liquidityProviderAddress?: string,
|
||||
) => BigNumber;
|
||||
|
||||
type GetLiquidityProviderFromRegistryOperation = (
|
||||
registryAddress: string,
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
) => string;
|
||||
|
||||
function createGetMedianSellRate(rate: Numberish): GetMedianRateOperation {
|
||||
return (sources: ERC20BridgeSource[], makerToken: string, takerToken: string, fillAmounts: BigNumber[]) => {
|
||||
return new BigNumber(rate);
|
||||
};
|
||||
}
|
||||
|
||||
function getLiquidityProviderFromRegistry(): GetLiquidityProviderFromRegistryOperation {
|
||||
return (registryAddress: string, takerToken: string, makerToken: string): string => {
|
||||
return NULL_ADDRESS;
|
||||
};
|
||||
}
|
||||
|
||||
function getLiquidityProviderFromRegistryAndReturnCallParameters(
|
||||
liquidityProviderAddress: string = NULL_ADDRESS,
|
||||
): [
|
||||
{ registryAddress?: string; takerToken?: string; makerToken?: string },
|
||||
GetLiquidityProviderFromRegistryOperation
|
||||
] {
|
||||
const callArgs: { registryAddress?: string; takerToken?: string; makerToken?: string } = {
|
||||
registryAddress: undefined,
|
||||
takerToken: undefined,
|
||||
makerToken: undefined,
|
||||
};
|
||||
const fn = (registryAddress: string, takerToken: string, makerToken: string): string => {
|
||||
callArgs.makerToken = makerToken;
|
||||
callArgs.takerToken = takerToken;
|
||||
callArgs.registryAddress = registryAddress;
|
||||
return liquidityProviderAddress;
|
||||
};
|
||||
return [callArgs, fn];
|
||||
}
|
||||
|
||||
function createDecreasingRates(count: number): BigNumber[] {
|
||||
const rates: BigNumber[] = [];
|
||||
const initialRate = getRandomFloat(1e-3, 1e2);
|
||||
@@ -205,6 +261,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
[ERC20BridgeSource.CurveUsdcDaiUsdt]: _.times(NUM_SAMPLES, () => 0),
|
||||
[ERC20BridgeSource.CurveUsdcDaiUsdtTusd]: _.times(NUM_SAMPLES, () => 0),
|
||||
[ERC20BridgeSource.CurveUsdcDaiUsdtBusd]: _.times(NUM_SAMPLES, () => 0),
|
||||
[ERC20BridgeSource.LiquidityProvider]: _.times(NUM_SAMPLES, () => 0),
|
||||
};
|
||||
|
||||
function findSourceWithMaxOutput(rates: RatesBySource): ERC20BridgeSource {
|
||||
@@ -239,6 +296,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(DEFAULT_RATES),
|
||||
getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(DEFAULT_RATES),
|
||||
getMedianSellRate: createGetMedianSellRate(1),
|
||||
getLiquidityProviderFromRegistry: getLiquidityProviderFromRegistry(),
|
||||
};
|
||||
|
||||
function replaceSamplerOps(ops: Partial<typeof DEFAULT_OPS> = {}): void {
|
||||
@@ -313,6 +371,31 @@ describe('MarketOperationUtils tests', () => {
|
||||
expect(sourcesPolled.sort()).to.deep.eq(SELL_SOURCES.slice().sort());
|
||||
});
|
||||
|
||||
it('polls the liquidity provider when the registry is provided in the arguments', async () => {
|
||||
const [args, fn] = callTradeOperationAndRetainLiquidityProviderParams(
|
||||
createGetMultipleSellQuotesOperationFromRates,
|
||||
DEFAULT_RATES,
|
||||
);
|
||||
replaceSamplerOps({
|
||||
getSellQuotes: fn,
|
||||
});
|
||||
const registryAddress = randomAddress();
|
||||
const newMarketOperationUtils = new MarketOperationUtils(
|
||||
MOCK_SAMPLER,
|
||||
contractAddresses,
|
||||
ORDER_DOMAIN,
|
||||
registryAddress,
|
||||
);
|
||||
await newMarketOperationUtils.getMarketSellOrdersAsync(ORDERS, FILL_AMOUNT, {
|
||||
...DEFAULT_OPTS,
|
||||
excludedSources: [],
|
||||
});
|
||||
expect(args.sources.sort()).to.deep.eq(
|
||||
SELL_SOURCES.concat([ERC20BridgeSource.LiquidityProvider]).sort(),
|
||||
);
|
||||
expect(args.liquidityProviderAddress).to.eql(registryAddress);
|
||||
});
|
||||
|
||||
it('does not poll DEXes in `excludedSources`', async () => {
|
||||
const excludedSources = _.sampleSize(SELL_SOURCES, _.random(1, SELL_SOURCES.length));
|
||||
let sourcesPolled: ERC20BridgeSource[] = [];
|
||||
@@ -530,6 +613,62 @@ describe('MarketOperationUtils tests', () => {
|
||||
const expectedSources = [ERC20BridgeSource.Native, ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Kyber];
|
||||
expect(orderSources).to.deep.eq(expectedSources);
|
||||
});
|
||||
|
||||
it('is able to create a order from LiquidityProvider', async () => {
|
||||
const registryAddress = randomAddress();
|
||||
const liquidityProviderAddress = randomAddress();
|
||||
const xAsset = randomAddress();
|
||||
const yAsset = randomAddress();
|
||||
const toSell = fromTokenUnitAmount(10);
|
||||
|
||||
const [getSellQuotesParams, getSellQuotesFn] = callTradeOperationAndRetainLiquidityProviderParams(
|
||||
createGetMultipleSellQuotesOperationFromRates,
|
||||
{
|
||||
[ERC20BridgeSource.LiquidityProvider]: createDecreasingRates(5),
|
||||
},
|
||||
);
|
||||
const [
|
||||
getLiquidityProviderParams,
|
||||
getLiquidityProviderFn,
|
||||
] = getLiquidityProviderFromRegistryAndReturnCallParameters(liquidityProviderAddress);
|
||||
replaceSamplerOps({
|
||||
getOrderFillableTakerAmounts: () => [constants.ZERO_AMOUNT],
|
||||
getSellQuotes: getSellQuotesFn,
|
||||
getLiquidityProviderFromRegistry: getLiquidityProviderFn,
|
||||
});
|
||||
|
||||
const sampler = new MarketOperationUtils(
|
||||
MOCK_SAMPLER,
|
||||
contractAddresses,
|
||||
ORDER_DOMAIN,
|
||||
registryAddress,
|
||||
);
|
||||
const result = await sampler.getMarketSellOrdersAsync(
|
||||
[
|
||||
createOrder({
|
||||
makerAssetData: assetDataUtils.encodeERC20AssetData(xAsset),
|
||||
takerAssetData: assetDataUtils.encodeERC20AssetData(yAsset),
|
||||
}),
|
||||
],
|
||||
Web3Wrapper.toBaseUnitAmount(10, 18),
|
||||
{ excludedSources: SELL_SOURCES, numSamples: 4 },
|
||||
);
|
||||
expect(result.length).to.eql(1);
|
||||
expect(result[0].makerAddress).to.eql(liquidityProviderAddress);
|
||||
|
||||
// tslint:disable-next-line:no-unnecessary-type-assertion
|
||||
const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(
|
||||
result[0].makerAssetData,
|
||||
) as ERC20BridgeAssetData;
|
||||
expect(decodedAssetData.assetProxyId).to.eql(AssetProxyId.ERC20Bridge);
|
||||
expect(decodedAssetData.bridgeAddress).to.eql(liquidityProviderAddress);
|
||||
expect(result[0].takerAssetAmount).to.bignumber.eql(toSell);
|
||||
expect(getSellQuotesParams.sources).contains(ERC20BridgeSource.LiquidityProvider);
|
||||
expect(getSellQuotesParams.liquidityProviderAddress).is.eql(registryAddress);
|
||||
expect(getLiquidityProviderParams.registryAddress).is.eql(registryAddress);
|
||||
expect(getLiquidityProviderParams.makerToken).is.eql(xAsset);
|
||||
expect(getLiquidityProviderParams.takerToken).is.eql(yAsset);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMarketBuyOrdersAsync()', () => {
|
||||
@@ -580,6 +719,31 @@ describe('MarketOperationUtils tests', () => {
|
||||
expect(sourcesPolled).to.deep.eq(BUY_SOURCES);
|
||||
});
|
||||
|
||||
it('polls the liquidity provider when the registry is provided in the arguments', async () => {
|
||||
const [args, fn] = callTradeOperationAndRetainLiquidityProviderParams(
|
||||
createGetMultipleBuyQuotesOperationFromRates,
|
||||
DEFAULT_RATES,
|
||||
);
|
||||
replaceSamplerOps({
|
||||
getBuyQuotes: fn,
|
||||
});
|
||||
const registryAddress = randomAddress();
|
||||
const newMarketOperationUtils = new MarketOperationUtils(
|
||||
MOCK_SAMPLER,
|
||||
contractAddresses,
|
||||
ORDER_DOMAIN,
|
||||
registryAddress,
|
||||
);
|
||||
await newMarketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, {
|
||||
...DEFAULT_OPTS,
|
||||
excludedSources: [],
|
||||
});
|
||||
expect(args.sources.sort()).to.deep.eq(
|
||||
BUY_SOURCES.concat([ERC20BridgeSource.LiquidityProvider]).sort(),
|
||||
);
|
||||
expect(args.liquidityProviderAddress).to.eql(registryAddress);
|
||||
});
|
||||
|
||||
it('does not poll DEXes in `excludedSources`', async () => {
|
||||
const excludedSources = _.sampleSize(SELL_SOURCES, _.random(1, SELL_SOURCES.length));
|
||||
let sourcesPolled: ERC20BridgeSource[] = [];
|
||||
|
||||
@@ -21,6 +21,12 @@ export type SampleBuysHandler = (
|
||||
makerToken: string,
|
||||
makerTokenAmounts: BigNumber[],
|
||||
) => SampleResults;
|
||||
export type SampleSellsLPHandler = (
|
||||
registryAddress: string,
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
takerTokenAmounts: BigNumber[],
|
||||
) => SampleResults;
|
||||
|
||||
const DUMMY_PROVIDER = {
|
||||
sendAsync: (...args: any[]): any => {
|
||||
@@ -32,10 +38,12 @@ interface Handlers {
|
||||
getOrderFillableMakerAssetAmounts: GetOrderFillableAssetAmountHandler;
|
||||
getOrderFillableTakerAssetAmounts: GetOrderFillableAssetAmountHandler;
|
||||
sampleSellsFromKyberNetwork: SampleSellsHandler;
|
||||
sampleSellsFromLiquidityProviderRegistry: SampleSellsLPHandler;
|
||||
sampleSellsFromEth2Dai: SampleSellsHandler;
|
||||
sampleSellsFromUniswap: SampleSellsHandler;
|
||||
sampleBuysFromEth2Dai: SampleBuysHandler;
|
||||
sampleBuysFromUniswap: SampleBuysHandler;
|
||||
sampleBuysFromLiquidityProviderRegistry: SampleSellsLPHandler;
|
||||
}
|
||||
|
||||
export class MockSamplerContract extends IERC20BridgeSamplerContract {
|
||||
@@ -119,6 +127,22 @@ export class MockSamplerContract extends IERC20BridgeSamplerContract {
|
||||
);
|
||||
}
|
||||
|
||||
public sampleSellsFromLiquidityProviderRegistry(
|
||||
registryAddress: string,
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
takerAssetAmounts: BigNumber[],
|
||||
): ContractFunctionObj<GetOrderFillableAssetAmountResult> {
|
||||
return this._wrapCall(
|
||||
super.sampleSellsFromLiquidityProviderRegistry,
|
||||
this._handlers.sampleSellsFromLiquidityProviderRegistry,
|
||||
registryAddress,
|
||||
takerToken,
|
||||
makerToken,
|
||||
takerAssetAmounts,
|
||||
);
|
||||
}
|
||||
|
||||
public sampleBuysFromEth2Dai(
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
|
||||
81
packages/contract-artifacts/artifacts/DummyLiquidityProvider.json
generated
Normal file
81
packages/contract-artifacts/artifacts/DummyLiquidityProvider.json
generated
Normal file
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"schemaVersion": "2.0.0",
|
||||
"contractName": "DummyLiquidityProvider",
|
||||
"compilerOutput": {
|
||||
"abi": [
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{ "internalType": "address", "name": "", "type": "address" },
|
||||
{ "internalType": "address", "name": "", "type": "address" },
|
||||
{ "internalType": "uint256", "name": "buyAmount", "type": "uint256" }
|
||||
],
|
||||
"name": "getBuyQuote",
|
||||
"outputs": [{ "internalType": "uint256", "name": "takerTokenAmount", "type": "uint256" }],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{ "internalType": "address", "name": "", "type": "address" },
|
||||
{ "internalType": "address", "name": "", "type": "address" },
|
||||
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" }
|
||||
],
|
||||
"name": "getSellQuote",
|
||||
"outputs": [{ "internalType": "uint256", "name": "makerTokenAmount", "type": "uint256" }],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"devdoc": {
|
||||
"methods": {
|
||||
"getBuyQuote(address,address,uint256)": {
|
||||
"details": "Quotes the amount of `takerToken` that would need to be sold in order to obtain `buyAmount` of `makerToken`.",
|
||||
"params": { "buyAmount": "Amount of `makerToken` to buy." },
|
||||
"return": "takerTokenAmount Amount of `takerToken` that would need to be sold."
|
||||
},
|
||||
"getSellQuote(address,address,uint256)": {
|
||||
"details": "Quotes the amount of `makerToken` that would be obtained by selling `sellAmount` of `takerToken`.",
|
||||
"params": { "sellAmount": "Amount of `takerToken` to sell." },
|
||||
"return": "makerTokenAmount Amount of `makerToken` that would be obtained."
|
||||
}
|
||||
}
|
||||
},
|
||||
"evm": {
|
||||
"bytecode": {
|
||||
"object": "0x608060405234801561001057600080fd5b50610159806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063343fbcdd1461003b57806345060eb014610064575b600080fd5b61004e6100493660046100a8565b610077565b60405161005b91906100e8565b60405180910390f35b61004e6100723660046100a8565b61009f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b60010192915050565b6000806000606084860312156100bc578283fd5b83356100c7816100f1565b925060208401356100d7816100f1565b929592945050506040919091013590565b90815260200190565b73ffffffffffffffffffffffffffffffffffffffff8116811461011357600080fd5b5056fea365627a7a72315820981135e6e25d9062a0a9bcf7e08e326cde449b18310db7488d1db4e79ef0f6f36c6578706572696d656e74616cf564736f6c63430005100040"
|
||||
},
|
||||
"deployedBytecode": {
|
||||
"object": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063343fbcdd1461003b57806345060eb014610064575b600080fd5b61004e6100493660046100a8565b610077565b60405161005b91906100e8565b60405180910390f35b61004e6100723660046100a8565b61009f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b60010192915050565b6000806000606084860312156100bc578283fd5b83356100c7816100f1565b925060208401356100d7816100f1565b929592945050506040919091013590565b90815260200190565b73ffffffffffffffffffffffffffffffffffffffff8116811461011357600080fd5b5056fea365627a7a72315820981135e6e25d9062a0a9bcf7e08e326cde449b18310db7488d1db4e79ef0f6f36c6578706572696d656e74616cf564736f6c63430005100040"
|
||||
}
|
||||
}
|
||||
},
|
||||
"compiler": {
|
||||
"name": "solc",
|
||||
"version": "soljson-v0.5.16+commit.9c3226ce.js",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"devdoc",
|
||||
"evm.bytecode.object",
|
||||
"evm.bytecode.sourceMap",
|
||||
"evm.deployedBytecode.object",
|
||||
"evm.deployedBytecode.sourceMap"
|
||||
]
|
||||
}
|
||||
},
|
||||
"evmVersion": "istanbul"
|
||||
}
|
||||
},
|
||||
"chains": {}
|
||||
}
|
||||
83
packages/contract-artifacts/artifacts/DummyLiquidityProviderRegistry.json
generated
Normal file
83
packages/contract-artifacts/artifacts/DummyLiquidityProviderRegistry.json
generated
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"schemaVersion": "2.0.0",
|
||||
"contractName": "DummyLiquidityProviderRegistry",
|
||||
"compilerOutput": {
|
||||
"abi": [
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{ "internalType": "address", "name": "xToken", "type": "address" },
|
||||
{ "internalType": "address", "name": "yToken", "type": "address" }
|
||||
],
|
||||
"name": "getLiquidityProviderForMarket",
|
||||
"outputs": [{ "internalType": "address", "name": "poolAddress", "type": "address" }],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{ "internalType": "address", "name": "xToken", "type": "address" },
|
||||
{ "internalType": "address", "name": "yToken", "type": "address" },
|
||||
{ "internalType": "address", "name": "poolAddress", "type": "address" }
|
||||
],
|
||||
"name": "setLiquidityProviderForMarket",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"devdoc": {
|
||||
"methods": {
|
||||
"getLiquidityProviderForMarket(address,address)": {
|
||||
"details": "Returns the address of pool for a market given market (xAsset, yAsset), or reverts if pool does not exist.",
|
||||
"params": { "xToken": "First asset managed by pool.", "yToken": "Second asset managed by pool." },
|
||||
"return": "Address of pool."
|
||||
},
|
||||
"setLiquidityProviderForMarket(address,address,address)": {
|
||||
"details": "Sets address of pool for a market given market (xAsset, yAsset).",
|
||||
"params": {
|
||||
"poolAddress": "Address of pool.",
|
||||
"xToken": "First asset managed by pool.",
|
||||
"yToken": "Second asset managed by pool."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"evm": {
|
||||
"bytecode": {
|
||||
"object": "0x608060405234801561001057600080fd5b506102a6806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063153f59971461003b57806384da8d1e14610064575b600080fd5b61004e610049366004610192565b610079565b60405161005b919061020b565b60405180910390f35b6100776100723660046101c6565b6100f2565b005b73ffffffffffffffffffffffffffffffffffffffff808316600090815260208181526040808320858516845290915290205416806100ec576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100e39061022c565b60405180910390fd5b92915050565b73ffffffffffffffffffffffffffffffffffffffff92831660008181526020818152604080832095871683529481528482208054969094167fffffffffffffffffffffffff0000000000000000000000000000000000000000968716811790945581815284822092825291909152919091208054909216179055565b803573ffffffffffffffffffffffffffffffffffffffff811681146100ec57600080fd5b600080604083850312156101a4578182fd5b6101ae848461016e565b91506101bd846020850161016e565b90509250929050565b6000806000606084860312156101da578081fd5b6101e4858561016e565b92506101f3856020860161016e565b9150610202856040860161016e565b90509250925092565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b6020808252601c908201527f52656769737472792f4d41524b45545f504149525f4e4f545f5345540000000060408201526060019056fea365627a7a723158200b589233a17eab806bfb7e334f40bc1ba4502479e55b2aa562c069bc440ceb476c6578706572696d656e74616cf564736f6c63430005100040"
|
||||
},
|
||||
"deployedBytecode": {
|
||||
"object": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063153f59971461003b57806384da8d1e14610064575b600080fd5b61004e610049366004610192565b610079565b60405161005b919061020b565b60405180910390f35b6100776100723660046101c6565b6100f2565b005b73ffffffffffffffffffffffffffffffffffffffff808316600090815260208181526040808320858516845290915290205416806100ec576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100e39061022c565b60405180910390fd5b92915050565b73ffffffffffffffffffffffffffffffffffffffff92831660008181526020818152604080832095871683529481528482208054969094167fffffffffffffffffffffffff0000000000000000000000000000000000000000968716811790945581815284822092825291909152919091208054909216179055565b803573ffffffffffffffffffffffffffffffffffffffff811681146100ec57600080fd5b600080604083850312156101a4578182fd5b6101ae848461016e565b91506101bd846020850161016e565b90509250929050565b6000806000606084860312156101da578081fd5b6101e4858561016e565b92506101f3856020860161016e565b9150610202856040860161016e565b90509250925092565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b6020808252601c908201527f52656769737472792f4d41524b45545f504149525f4e4f545f5345540000000060408201526060019056fea365627a7a723158200b589233a17eab806bfb7e334f40bc1ba4502479e55b2aa562c069bc440ceb476c6578706572696d656e74616cf564736f6c63430005100040"
|
||||
}
|
||||
}
|
||||
},
|
||||
"compiler": {
|
||||
"name": "solc",
|
||||
"version": "soljson-v0.5.16+commit.9c3226ce.js",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"devdoc",
|
||||
"evm.bytecode.object",
|
||||
"evm.bytecode.sourceMap",
|
||||
"evm.deployedBytecode.object",
|
||||
"evm.deployedBytecode.sourceMap"
|
||||
]
|
||||
}
|
||||
},
|
||||
"evmVersion": "istanbul"
|
||||
}
|
||||
},
|
||||
"chains": {}
|
||||
}
|
||||
346
packages/contract-artifacts/artifacts/ERC20BridgeSampler.json
generated
Normal file
346
packages/contract-artifacts/artifacts/ERC20BridgeSampler.json
generated
Normal file
File diff suppressed because one or more lines are too long
@@ -12,6 +12,19 @@
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{ "internalType": "address", "name": "registryAddress", "type": "address" },
|
||||
{ "internalType": "address", "name": "takerToken", "type": "address" },
|
||||
{ "internalType": "address", "name": "makerToken", "type": "address" }
|
||||
],
|
||||
"name": "getLiquidityProviderFromRegistry",
|
||||
"outputs": [{ "internalType": "address", "name": "providerAddress", "type": "address" }],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
@@ -93,6 +106,20 @@
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{ "internalType": "address", "name": "registryAddress", "type": "address" },
|
||||
{ "internalType": "address", "name": "takerToken", "type": "address" },
|
||||
{ "internalType": "address", "name": "makerToken", "type": "address" },
|
||||
{ "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" }
|
||||
],
|
||||
"name": "sampleBuysFromLiquidityProviderRegistry",
|
||||
"outputs": [{ "internalType": "uint256[]", "name": "takerTokenAmounts", "type": "uint256[]" }],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
@@ -146,6 +173,20 @@
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{ "internalType": "address", "name": "registryAddress", "type": "address" },
|
||||
{ "internalType": "address", "name": "takerToken", "type": "address" },
|
||||
{ "internalType": "address", "name": "makerToken", "type": "address" },
|
||||
{ "internalType": "uint256[]", "name": "takerTokenAmounts", "type": "uint256[]" }
|
||||
],
|
||||
"name": "sampleSellsFromLiquidityProviderRegistry",
|
||||
"outputs": [{ "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" }],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
@@ -167,6 +208,14 @@
|
||||
"params": { "callDatas": "ABI-encoded call data for each function call." },
|
||||
"return": "callResults ABI-encoded results data for each call."
|
||||
},
|
||||
"getLiquidityProviderFromRegistry(address,address,address)": {
|
||||
"details": "Returns the address of a liquidity provider for the given market (takerToken, makerToken), from a registry of liquidity providers. Returns address(0) if no such provider exists in the registry.",
|
||||
"params": {
|
||||
"makerToken": "Maker asset managed by liquidity provider.",
|
||||
"takerToken": "Taker asset managed by liquidity provider."
|
||||
},
|
||||
"return": "providerAddress Address of the liquidity provider."
|
||||
},
|
||||
"getOrderFillableMakerAssetAmounts((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],bytes[])": {
|
||||
"details": "Queries the fillable maker asset amounts of native orders.",
|
||||
"params": {
|
||||
@@ -192,6 +241,16 @@
|
||||
},
|
||||
"return": "takerTokenAmounts Taker amounts sold at each maker token amount."
|
||||
},
|
||||
"sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[])": {
|
||||
"details": "Sample buy quotes from an arbitrary on-chain liquidity provider.",
|
||||
"params": {
|
||||
"makerToken": "Address of the maker token (what to buy).",
|
||||
"makerTokenAmounts": "Maker token buy amount for each sample.",
|
||||
"registryAddress": "Address of the liquidity provider registry contract.",
|
||||
"takerToken": "Address of the taker token (what to sell)."
|
||||
},
|
||||
"return": "takerTokenAmounts Taker amounts sold at each maker token amount."
|
||||
},
|
||||
"sampleBuysFromUniswap(address,address,uint256[])": {
|
||||
"details": "Sample buy quotes from Uniswap.",
|
||||
"params": {
|
||||
@@ -229,6 +288,16 @@
|
||||
},
|
||||
"return": "makerTokenAmounts Maker amounts bought at each taker token amount."
|
||||
},
|
||||
"sampleSellsFromLiquidityProviderRegistry(address,address,address,uint256[])": {
|
||||
"details": "Sample sell quotes from an arbitrary on-chain liquidity provider.",
|
||||
"params": {
|
||||
"makerToken": "Address of the maker token (what to buy).",
|
||||
"registryAddress": "Address of the liquidity provider registry contract.",
|
||||
"takerToken": "Address of the taker token (what to sell).",
|
||||
"takerTokenAmounts": "Taker token sell amount for each sample."
|
||||
},
|
||||
"return": "makerTokenAmounts Maker amounts bought at each taker token amount."
|
||||
},
|
||||
"sampleSellsFromUniswap(address,address,uint256[])": {
|
||||
"details": "Sample sell quotes from Uniswap.",
|
||||
"params": {
|
||||
|
||||
108
packages/contract-artifacts/artifacts/ILiquidityProvider.json
generated
Normal file
108
packages/contract-artifacts/artifacts/ILiquidityProvider.json
generated
Normal file
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"schemaVersion": "2.0.0",
|
||||
"contractName": "ILiquidityProvider",
|
||||
"compilerOutput": {
|
||||
"abi": [
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{ "internalType": "address", "name": "tokenAddress", "type": "address" },
|
||||
{ "internalType": "address", "name": "from", "type": "address" },
|
||||
{ "internalType": "address", "name": "to", "type": "address" },
|
||||
{ "internalType": "uint256", "name": "amount", "type": "uint256" },
|
||||
{ "internalType": "bytes", "name": "bridgeData", "type": "bytes" }
|
||||
],
|
||||
"name": "bridgeTransferFrom",
|
||||
"outputs": [{ "internalType": "bytes4", "name": "success", "type": "bytes4" }],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{ "internalType": "address", "name": "takerToken", "type": "address" },
|
||||
{ "internalType": "address", "name": "makerToken", "type": "address" },
|
||||
{ "internalType": "uint256", "name": "buyAmount", "type": "uint256" }
|
||||
],
|
||||
"name": "getBuyQuote",
|
||||
"outputs": [{ "internalType": "uint256", "name": "takerTokenAmount", "type": "uint256" }],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{ "internalType": "address", "name": "takerToken", "type": "address" },
|
||||
{ "internalType": "address", "name": "makerToken", "type": "address" },
|
||||
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" }
|
||||
],
|
||||
"name": "getSellQuote",
|
||||
"outputs": [{ "internalType": "uint256", "name": "makerTokenAmount", "type": "uint256" }],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"devdoc": {
|
||||
"methods": {
|
||||
"bridgeTransferFrom(address,address,address,uint256,bytes)": {
|
||||
"details": "Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.",
|
||||
"params": {
|
||||
"amount": "Amount of asset to transfer.",
|
||||
"bridgeData": "Arbitrary asset data needed by the bridge contract.",
|
||||
"from": "Address to transfer asset from.",
|
||||
"to": "Address to transfer asset to.",
|
||||
"tokenAddress": "The address of the ERC20 token to transfer."
|
||||
},
|
||||
"return": "success The magic bytes `0xdc1600f3` if successful."
|
||||
},
|
||||
"getBuyQuote(address,address,uint256)": {
|
||||
"details": "Quotes the amount of `takerToken` that would need to be sold in order to obtain `buyAmount` of `makerToken`.",
|
||||
"params": {
|
||||
"buyAmount": "Amount of `makerToken` to buy.",
|
||||
"makerToken": "Address of the maker token (what to buy).",
|
||||
"takerToken": "Address of the taker token (what to sell)."
|
||||
},
|
||||
"return": "takerTokenAmount Amount of `takerToken` that would need to be sold."
|
||||
},
|
||||
"getSellQuote(address,address,uint256)": {
|
||||
"details": "Quotes the amount of `makerToken` that would be obtained by selling `sellAmount` of `takerToken`.",
|
||||
"params": {
|
||||
"makerToken": "Address of the maker token (what to buy).",
|
||||
"sellAmount": "Amount of `takerToken` to sell.",
|
||||
"takerToken": "Address of the taker token (what to sell)."
|
||||
},
|
||||
"return": "makerTokenAmount Amount of `makerToken` that would be obtained."
|
||||
}
|
||||
}
|
||||
},
|
||||
"evm": { "bytecode": { "object": "0x" }, "deployedBytecode": { "object": "0x" } }
|
||||
},
|
||||
"compiler": {
|
||||
"name": "solc",
|
||||
"version": "soljson-v0.5.16+commit.9c3226ce.js",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"devdoc",
|
||||
"evm.bytecode.object",
|
||||
"evm.bytecode.sourceMap",
|
||||
"evm.deployedBytecode.object",
|
||||
"evm.deployedBytecode.sourceMap"
|
||||
]
|
||||
}
|
||||
},
|
||||
"evmVersion": "istanbul"
|
||||
}
|
||||
},
|
||||
"chains": {}
|
||||
}
|
||||
58
packages/contract-artifacts/artifacts/ILiquidityProviderRegistry.json
generated
Normal file
58
packages/contract-artifacts/artifacts/ILiquidityProviderRegistry.json
generated
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"schemaVersion": "2.0.0",
|
||||
"contractName": "ILiquidityProviderRegistry",
|
||||
"compilerOutput": {
|
||||
"abi": [
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{ "internalType": "address", "name": "takerToken", "type": "address" },
|
||||
{ "internalType": "address", "name": "makerToken", "type": "address" }
|
||||
],
|
||||
"name": "getLiquidityProviderForMarket",
|
||||
"outputs": [{ "internalType": "address", "name": "providerAddress", "type": "address" }],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"devdoc": {
|
||||
"methods": {
|
||||
"getLiquidityProviderForMarket(address,address)": {
|
||||
"details": "Returns the address of a liquidity provider for the given market (takerToken, makerToken), reverting if the pool does not exist.",
|
||||
"params": {
|
||||
"makerToken": "Maker asset managed by liquidity provider.",
|
||||
"takerToken": "Taker asset managed by liquidity provider."
|
||||
},
|
||||
"return": "Address of the liquidity provider."
|
||||
}
|
||||
}
|
||||
},
|
||||
"evm": { "bytecode": { "object": "0x" }, "deployedBytecode": { "object": "0x" } }
|
||||
},
|
||||
"compiler": {
|
||||
"name": "solc",
|
||||
"version": "soljson-v0.5.16+commit.9c3226ce.js",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"devdoc",
|
||||
"evm.bytecode.object",
|
||||
"evm.bytecode.sourceMap",
|
||||
"evm.deployedBytecode.object",
|
||||
"evm.deployedBytecode.sourceMap"
|
||||
]
|
||||
}
|
||||
},
|
||||
"evmVersion": "istanbul"
|
||||
}
|
||||
},
|
||||
"chains": {}
|
||||
}
|
||||
@@ -31,7 +31,7 @@
|
||||
"wrappers:generate": "abi-gen --abis ${npm_package_config_abis} --output src/generated-wrappers --backend ethers"
|
||||
},
|
||||
"config": {
|
||||
"abis": "../contract-artifacts/artifacts/@(DevUtils|ERC20Token|ERC721Token|Exchange|Forwarder|IAssetData|LibTransactionDecoder|WETH9|Coordinator|Staking|StakingProxy|IERC20BridgeSampler|GodsUnchainedValidator|Broker|MaximumGasPrice).json"
|
||||
"abis": "../contract-artifacts/artifacts/@(DevUtils|ERC20Token|ERC721Token|Exchange|Forwarder|IAssetData|LibTransactionDecoder|WETH9|Coordinator|Staking|StakingProxy|IERC20BridgeSampler|ERC20BridgeSampler|GodsUnchainedValidator|Broker|ILiquidityProvider|ILiquidityProviderRegistry|MaximumGasPrice).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
1185
packages/contract-wrappers/src/generated-wrappers/erc20_bridge_sampler.ts
generated
Normal file
1185
packages/contract-wrappers/src/generated-wrappers/erc20_bridge_sampler.ts
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -180,6 +180,33 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'registryAddress',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'takerToken',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'makerToken',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
name: 'getLiquidityProviderFromRegistry',
|
||||
outputs: [
|
||||
{
|
||||
name: 'providerAddress',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
@@ -369,6 +396,37 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'registryAddress',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'takerToken',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'makerToken',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'makerTokenAmounts',
|
||||
type: 'uint256[]',
|
||||
},
|
||||
],
|
||||
name: 'sampleBuysFromLiquidityProviderRegistry',
|
||||
outputs: [
|
||||
{
|
||||
name: 'takerTokenAmounts',
|
||||
type: 'uint256[]',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
@@ -481,6 +539,37 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'registryAddress',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'takerToken',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'makerToken',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'takerTokenAmounts',
|
||||
type: 'uint256[]',
|
||||
},
|
||||
],
|
||||
name: 'sampleSellsFromLiquidityProviderRegistry',
|
||||
outputs: [
|
||||
{
|
||||
name: 'makerTokenAmounts',
|
||||
type: 'uint256[]',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
@@ -613,6 +702,45 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Returns the address of a liquidity provider for the given market
|
||||
* (takerToken, makerToken), from a registry of liquidity providers.
|
||||
* Returns address(0) if no such provider exists in the registry.
|
||||
* @param takerToken Taker asset managed by liquidity provider.
|
||||
* @param makerToken Maker asset managed by liquidity provider.
|
||||
* @returns providerAddress Address of the liquidity provider.
|
||||
*/
|
||||
public getLiquidityProviderFromRegistry(
|
||||
registryAddress: string,
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
): ContractFunctionObj<string> {
|
||||
const self = (this as any) as IERC20BridgeSamplerContract;
|
||||
assert.isString('registryAddress', registryAddress);
|
||||
assert.isString('takerToken', takerToken);
|
||||
assert.isString('makerToken', makerToken);
|
||||
const functionSignature = 'getLiquidityProviderFromRegistry(address,address,address)';
|
||||
|
||||
return {
|
||||
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<string> {
|
||||
BaseContract._assertCallParams(callData, defaultBlock);
|
||||
const rawCallResult = await self._performCallAsync(
|
||||
{ ...callData, data: this.getABIEncodedTransactionData() },
|
||||
defaultBlock,
|
||||
);
|
||||
const abiEncoder = self._lookupAbiEncoder(functionSignature);
|
||||
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
|
||||
return abiEncoder.strictDecodeReturnValue<string>(rawCallResult);
|
||||
},
|
||||
getABIEncodedTransactionData(): string {
|
||||
return self._strictEncodeArguments(functionSignature, [
|
||||
registryAddress.toLowerCase(),
|
||||
takerToken.toLowerCase(),
|
||||
makerToken.toLowerCase(),
|
||||
]);
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Queries the fillable maker asset amounts of native orders.
|
||||
* @param orders Native orders to query.
|
||||
@@ -744,6 +872,48 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Sample buy quotes from an arbitrary on-chain liquidity provider.
|
||||
* @param registryAddress Address of the liquidity provider registry contract.
|
||||
* @param takerToken Address of the taker token (what to sell).
|
||||
* @param makerToken Address of the maker token (what to buy).
|
||||
* @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
* @returns takerTokenAmounts Taker amounts sold at each maker token amount.
|
||||
*/
|
||||
public sampleBuysFromLiquidityProviderRegistry(
|
||||
registryAddress: string,
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
makerTokenAmounts: BigNumber[],
|
||||
): ContractFunctionObj<BigNumber[]> {
|
||||
const self = (this as any) as IERC20BridgeSamplerContract;
|
||||
assert.isString('registryAddress', registryAddress);
|
||||
assert.isString('takerToken', takerToken);
|
||||
assert.isString('makerToken', makerToken);
|
||||
assert.isArray('makerTokenAmounts', makerTokenAmounts);
|
||||
const functionSignature = 'sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[])';
|
||||
|
||||
return {
|
||||
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber[]> {
|
||||
BaseContract._assertCallParams(callData, defaultBlock);
|
||||
const rawCallResult = await self._performCallAsync(
|
||||
{ ...callData, data: this.getABIEncodedTransactionData() },
|
||||
defaultBlock,
|
||||
);
|
||||
const abiEncoder = self._lookupAbiEncoder(functionSignature);
|
||||
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
|
||||
return abiEncoder.strictDecodeReturnValue<BigNumber[]>(rawCallResult);
|
||||
},
|
||||
getABIEncodedTransactionData(): string {
|
||||
return self._strictEncodeArguments(functionSignature, [
|
||||
registryAddress.toLowerCase(),
|
||||
takerToken.toLowerCase(),
|
||||
makerToken.toLowerCase(),
|
||||
makerTokenAmounts,
|
||||
]);
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Sample buy quotes from Uniswap.
|
||||
* @param takerToken Address of the taker token (what to sell).
|
||||
@@ -900,6 +1070,48 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Sample sell quotes from an arbitrary on-chain liquidity provider.
|
||||
* @param registryAddress Address of the liquidity provider registry contract.
|
||||
* @param takerToken Address of the taker token (what to sell).
|
||||
* @param makerToken Address of the maker token (what to buy).
|
||||
* @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
* @returns makerTokenAmounts Maker amounts bought at each taker token amount.
|
||||
*/
|
||||
public sampleSellsFromLiquidityProviderRegistry(
|
||||
registryAddress: string,
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
takerTokenAmounts: BigNumber[],
|
||||
): ContractFunctionObj<BigNumber[]> {
|
||||
const self = (this as any) as IERC20BridgeSamplerContract;
|
||||
assert.isString('registryAddress', registryAddress);
|
||||
assert.isString('takerToken', takerToken);
|
||||
assert.isString('makerToken', makerToken);
|
||||
assert.isArray('takerTokenAmounts', takerTokenAmounts);
|
||||
const functionSignature = 'sampleSellsFromLiquidityProviderRegistry(address,address,address,uint256[])';
|
||||
|
||||
return {
|
||||
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber[]> {
|
||||
BaseContract._assertCallParams(callData, defaultBlock);
|
||||
const rawCallResult = await self._performCallAsync(
|
||||
{ ...callData, data: this.getABIEncodedTransactionData() },
|
||||
defaultBlock,
|
||||
);
|
||||
const abiEncoder = self._lookupAbiEncoder(functionSignature);
|
||||
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
|
||||
return abiEncoder.strictDecodeReturnValue<BigNumber[]>(rawCallResult);
|
||||
},
|
||||
getABIEncodedTransactionData(): string {
|
||||
return self._strictEncodeArguments(functionSignature, [
|
||||
registryAddress.toLowerCase(),
|
||||
takerToken.toLowerCase(),
|
||||
makerToken.toLowerCase(),
|
||||
takerTokenAmounts,
|
||||
]);
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Sample sell quotes from Uniswap.
|
||||
* @param takerToken Address of the taker token (what to sell).
|
||||
|
||||
503
packages/contract-wrappers/src/generated-wrappers/i_liquidity_provider.ts
generated
Normal file
503
packages/contract-wrappers/src/generated-wrappers/i_liquidity_provider.ts
generated
Normal file
@@ -0,0 +1,503 @@
|
||||
// tslint:disable:no-consecutive-blank-lines ordered-imports align trailing-comma enum-naming
|
||||
// tslint:disable:whitespace no-unbound-method no-trailing-whitespace
|
||||
// tslint:disable:no-unused-variable
|
||||
import {
|
||||
AwaitTransactionSuccessOpts,
|
||||
ContractFunctionObj,
|
||||
ContractTxFunctionObj,
|
||||
SendTransactionOpts,
|
||||
BaseContract,
|
||||
PromiseWithTransactionHash,
|
||||
methodAbiToFunctionSignature,
|
||||
linkLibrariesInBytecode,
|
||||
} from '@0x/base-contract';
|
||||
import { schemas } from '@0x/json-schemas';
|
||||
import {
|
||||
BlockParam,
|
||||
BlockParamLiteral,
|
||||
BlockRange,
|
||||
CallData,
|
||||
ContractAbi,
|
||||
ContractArtifact,
|
||||
DecodedLogArgs,
|
||||
MethodAbi,
|
||||
TransactionReceiptWithDecodedLogs,
|
||||
TxData,
|
||||
TxDataPayable,
|
||||
SupportedProvider,
|
||||
} from 'ethereum-types';
|
||||
import { BigNumber, classUtils, hexUtils, logUtils, providerUtils } from '@0x/utils';
|
||||
import { EventCallback, IndexedFilterValues, SimpleContractArtifact } from '@0x/types';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { assert } from '@0x/assert';
|
||||
import * as ethers from 'ethers';
|
||||
// tslint:enable:no-unused-variable
|
||||
|
||||
/* istanbul ignore next */
|
||||
// tslint:disable:array-type
|
||||
// tslint:disable:no-parameter-reassignment
|
||||
// tslint:disable-next-line:class-name
|
||||
export class ILiquidityProviderContract extends BaseContract {
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public static deployedBytecode: string | undefined;
|
||||
public static contractName = 'ILiquidityProvider';
|
||||
private readonly _methodABIIndex: { [name: string]: number } = {};
|
||||
public static async deployFrom0xArtifactAsync(
|
||||
artifact: ContractArtifact | SimpleContractArtifact,
|
||||
supportedProvider: SupportedProvider,
|
||||
txDefaults: Partial<TxData>,
|
||||
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
|
||||
): Promise<ILiquidityProviderContract> {
|
||||
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
|
||||
schemas.addressSchema,
|
||||
schemas.numberSchema,
|
||||
schemas.jsNumber,
|
||||
]);
|
||||
if (artifact.compilerOutput === undefined) {
|
||||
throw new Error('Compiler output not found in the artifact file');
|
||||
}
|
||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||
const bytecode = artifact.compilerOutput.evm.bytecode.object;
|
||||
const abi = artifact.compilerOutput.abi;
|
||||
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
|
||||
if (Object.keys(logDecodeDependencies) !== undefined) {
|
||||
for (const key of Object.keys(logDecodeDependencies)) {
|
||||
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
|
||||
}
|
||||
}
|
||||
return ILiquidityProviderContract.deployAsync(
|
||||
bytecode,
|
||||
abi,
|
||||
provider,
|
||||
txDefaults,
|
||||
logDecodeDependenciesAbiOnly,
|
||||
);
|
||||
}
|
||||
|
||||
public static async deployWithLibrariesFrom0xArtifactAsync(
|
||||
artifact: ContractArtifact,
|
||||
libraryArtifacts: { [libraryName: string]: ContractArtifact },
|
||||
supportedProvider: SupportedProvider,
|
||||
txDefaults: Partial<TxData>,
|
||||
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
|
||||
): Promise<ILiquidityProviderContract> {
|
||||
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
|
||||
schemas.addressSchema,
|
||||
schemas.numberSchema,
|
||||
schemas.jsNumber,
|
||||
]);
|
||||
if (artifact.compilerOutput === undefined) {
|
||||
throw new Error('Compiler output not found in the artifact file');
|
||||
}
|
||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||
const abi = artifact.compilerOutput.abi;
|
||||
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
|
||||
if (Object.keys(logDecodeDependencies) !== undefined) {
|
||||
for (const key of Object.keys(logDecodeDependencies)) {
|
||||
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
|
||||
}
|
||||
}
|
||||
const libraryAddresses = await ILiquidityProviderContract._deployLibrariesAsync(
|
||||
artifact,
|
||||
libraryArtifacts,
|
||||
new Web3Wrapper(provider),
|
||||
txDefaults,
|
||||
);
|
||||
const bytecode = linkLibrariesInBytecode(artifact, libraryAddresses);
|
||||
return ILiquidityProviderContract.deployAsync(
|
||||
bytecode,
|
||||
abi,
|
||||
provider,
|
||||
txDefaults,
|
||||
logDecodeDependenciesAbiOnly,
|
||||
);
|
||||
}
|
||||
|
||||
public static async deployAsync(
|
||||
bytecode: string,
|
||||
abi: ContractAbi,
|
||||
supportedProvider: SupportedProvider,
|
||||
txDefaults: Partial<TxData>,
|
||||
logDecodeDependencies: { [contractName: string]: ContractAbi },
|
||||
): Promise<ILiquidityProviderContract> {
|
||||
assert.isHexString('bytecode', bytecode);
|
||||
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
|
||||
schemas.addressSchema,
|
||||
schemas.numberSchema,
|
||||
schemas.jsNumber,
|
||||
]);
|
||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||
const constructorAbi = BaseContract._lookupConstructorAbi(abi);
|
||||
[] = BaseContract._formatABIDataItemList(constructorAbi.inputs, [], BaseContract._bigNumberToString);
|
||||
const iface = new ethers.utils.Interface(abi);
|
||||
const deployInfo = iface.deployFunction;
|
||||
const txData = deployInfo.encode(bytecode, []);
|
||||
const web3Wrapper = new Web3Wrapper(provider);
|
||||
const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync(
|
||||
{
|
||||
data: txData,
|
||||
...txDefaults,
|
||||
},
|
||||
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
|
||||
);
|
||||
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
|
||||
logUtils.log(`transactionHash: ${txHash}`);
|
||||
const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||
logUtils.log(`ILiquidityProvider successfully deployed at ${txReceipt.contractAddress}`);
|
||||
const contractInstance = new ILiquidityProviderContract(
|
||||
txReceipt.contractAddress as string,
|
||||
provider,
|
||||
txDefaults,
|
||||
logDecodeDependencies,
|
||||
);
|
||||
contractInstance.constructorArgs = [];
|
||||
return contractInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The contract ABI
|
||||
*/
|
||||
public static ABI(): ContractAbi {
|
||||
const abi = [
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: 'tokenAddress',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'from',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'to',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'amount',
|
||||
type: 'uint256',
|
||||
},
|
||||
{
|
||||
name: 'bridgeData',
|
||||
type: 'bytes',
|
||||
},
|
||||
],
|
||||
name: 'bridgeTransferFrom',
|
||||
outputs: [
|
||||
{
|
||||
name: 'success',
|
||||
type: 'bytes4',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'takerToken',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'makerToken',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'buyAmount',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'getBuyQuote',
|
||||
outputs: [
|
||||
{
|
||||
name: 'takerTokenAmount',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'takerToken',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'makerToken',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'sellAmount',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
name: 'getSellQuote',
|
||||
outputs: [
|
||||
{
|
||||
name: 'makerTokenAmount',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
] as ContractAbi;
|
||||
return abi;
|
||||
}
|
||||
|
||||
protected static async _deployLibrariesAsync(
|
||||
artifact: ContractArtifact,
|
||||
libraryArtifacts: { [libraryName: string]: ContractArtifact },
|
||||
web3Wrapper: Web3Wrapper,
|
||||
txDefaults: Partial<TxData>,
|
||||
libraryAddresses: { [libraryName: string]: string } = {},
|
||||
): Promise<{ [libraryName: string]: string }> {
|
||||
const links = artifact.compilerOutput.evm.bytecode.linkReferences;
|
||||
// Go through all linked libraries, recursively deploying them if necessary.
|
||||
for (const link of Object.values(links)) {
|
||||
for (const libraryName of Object.keys(link)) {
|
||||
if (!libraryAddresses[libraryName]) {
|
||||
// Library not yet deployed.
|
||||
const libraryArtifact = libraryArtifacts[libraryName];
|
||||
if (!libraryArtifact) {
|
||||
throw new Error(`Missing artifact for linked library "${libraryName}"`);
|
||||
}
|
||||
// Deploy any dependent libraries used by this library.
|
||||
await ILiquidityProviderContract._deployLibrariesAsync(
|
||||
libraryArtifact,
|
||||
libraryArtifacts,
|
||||
web3Wrapper,
|
||||
txDefaults,
|
||||
libraryAddresses,
|
||||
);
|
||||
// Deploy this library.
|
||||
const linkedLibraryBytecode = linkLibrariesInBytecode(libraryArtifact, libraryAddresses);
|
||||
const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync(
|
||||
{
|
||||
data: linkedLibraryBytecode,
|
||||
...txDefaults,
|
||||
},
|
||||
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
|
||||
);
|
||||
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
|
||||
logUtils.log(`transactionHash: ${txHash}`);
|
||||
const { contractAddress } = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||
logUtils.log(`${libraryArtifact.contractName} successfully deployed at ${contractAddress}`);
|
||||
libraryAddresses[libraryArtifact.contractName] = contractAddress as string;
|
||||
}
|
||||
}
|
||||
}
|
||||
return libraryAddresses;
|
||||
}
|
||||
|
||||
public getFunctionSignature(methodName: string): string {
|
||||
const index = this._methodABIIndex[methodName];
|
||||
const methodAbi = ILiquidityProviderContract.ABI()[index] as MethodAbi; // tslint:disable-line:no-unnecessary-type-assertion
|
||||
const functionSignature = methodAbiToFunctionSignature(methodAbi);
|
||||
return functionSignature;
|
||||
}
|
||||
|
||||
public getABIDecodedTransactionData<T>(methodName: string, callData: string): T {
|
||||
const functionSignature = this.getFunctionSignature(methodName);
|
||||
const self = (this as any) as ILiquidityProviderContract;
|
||||
const abiEncoder = self._lookupAbiEncoder(functionSignature);
|
||||
const abiDecodedCallData = abiEncoder.strictDecode<T>(callData);
|
||||
return abiDecodedCallData;
|
||||
}
|
||||
|
||||
public getABIDecodedReturnData<T>(methodName: string, callData: string): T {
|
||||
const functionSignature = this.getFunctionSignature(methodName);
|
||||
const self = (this as any) as ILiquidityProviderContract;
|
||||
const abiEncoder = self._lookupAbiEncoder(functionSignature);
|
||||
const abiDecodedCallData = abiEncoder.strictDecodeReturnValue<T>(callData);
|
||||
return abiDecodedCallData;
|
||||
}
|
||||
|
||||
public getSelector(methodName: string): string {
|
||||
const functionSignature = this.getFunctionSignature(methodName);
|
||||
const self = (this as any) as ILiquidityProviderContract;
|
||||
const abiEncoder = self._lookupAbiEncoder(functionSignature);
|
||||
return abiEncoder.getSelector();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
|
||||
* @param tokenAddress The address of the ERC20 token to transfer.
|
||||
* @param from Address to transfer asset from.
|
||||
* @param to Address to transfer asset to.
|
||||
* @param amount Amount of asset to transfer.
|
||||
* @param bridgeData Arbitrary asset data needed by the bridge contract.
|
||||
* @returns success The magic bytes `0xdc1600f3` if successful.
|
||||
*/
|
||||
public bridgeTransferFrom(
|
||||
tokenAddress: string,
|
||||
from: string,
|
||||
to: string,
|
||||
amount: BigNumber,
|
||||
bridgeData: string,
|
||||
): ContractTxFunctionObj<string> {
|
||||
const self = (this as any) as ILiquidityProviderContract;
|
||||
assert.isString('tokenAddress', tokenAddress);
|
||||
assert.isString('from', from);
|
||||
assert.isString('to', to);
|
||||
assert.isBigNumber('amount', amount);
|
||||
assert.isString('bridgeData', bridgeData);
|
||||
const functionSignature = 'bridgeTransferFrom(address,address,address,uint256,bytes)';
|
||||
|
||||
return {
|
||||
async sendTransactionAsync(
|
||||
txData?: Partial<TxData> | undefined,
|
||||
opts: SendTransactionOpts = { shouldValidate: true },
|
||||
): Promise<string> {
|
||||
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync(
|
||||
{ ...txData, data: this.getABIEncodedTransactionData() },
|
||||
this.estimateGasAsync.bind(this),
|
||||
);
|
||||
if (opts.shouldValidate !== false) {
|
||||
await this.callAsync(txDataWithDefaults);
|
||||
}
|
||||
return self._web3Wrapper.sendTransactionAsync(txDataWithDefaults);
|
||||
},
|
||||
awaitTransactionSuccessAsync(
|
||||
txData?: Partial<TxData>,
|
||||
opts: AwaitTransactionSuccessOpts = { shouldValidate: true },
|
||||
): PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs> {
|
||||
return self._promiseWithTransactionHash(this.sendTransactionAsync(txData, opts), opts);
|
||||
},
|
||||
async estimateGasAsync(txData?: Partial<TxData> | undefined): Promise<number> {
|
||||
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync({
|
||||
...txData,
|
||||
data: this.getABIEncodedTransactionData(),
|
||||
});
|
||||
return self._web3Wrapper.estimateGasAsync(txDataWithDefaults);
|
||||
},
|
||||
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<string> {
|
||||
BaseContract._assertCallParams(callData, defaultBlock);
|
||||
const rawCallResult = await self._performCallAsync(
|
||||
{ ...callData, data: this.getABIEncodedTransactionData() },
|
||||
defaultBlock,
|
||||
);
|
||||
const abiEncoder = self._lookupAbiEncoder(functionSignature);
|
||||
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
|
||||
return abiEncoder.strictDecodeReturnValue<string>(rawCallResult);
|
||||
},
|
||||
getABIEncodedTransactionData(): string {
|
||||
return self._strictEncodeArguments(functionSignature, [
|
||||
tokenAddress.toLowerCase(),
|
||||
from.toLowerCase(),
|
||||
to.toLowerCase(),
|
||||
amount,
|
||||
bridgeData,
|
||||
]);
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Quotes the amount of `takerToken` that would need to be sold in
|
||||
* order to obtain `buyAmount` of `makerToken`.
|
||||
* @param takerToken Address of the taker token (what to sell).
|
||||
* @param makerToken Address of the maker token (what to buy).
|
||||
* @param buyAmount Amount of `makerToken` to buy.
|
||||
* @returns takerTokenAmount Amount of `takerToken` that would need to be sold.
|
||||
*/
|
||||
public getBuyQuote(takerToken: string, makerToken: string, buyAmount: BigNumber): ContractFunctionObj<BigNumber> {
|
||||
const self = (this as any) as ILiquidityProviderContract;
|
||||
assert.isString('takerToken', takerToken);
|
||||
assert.isString('makerToken', makerToken);
|
||||
assert.isBigNumber('buyAmount', buyAmount);
|
||||
const functionSignature = 'getBuyQuote(address,address,uint256)';
|
||||
|
||||
return {
|
||||
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber> {
|
||||
BaseContract._assertCallParams(callData, defaultBlock);
|
||||
const rawCallResult = await self._performCallAsync(
|
||||
{ ...callData, data: this.getABIEncodedTransactionData() },
|
||||
defaultBlock,
|
||||
);
|
||||
const abiEncoder = self._lookupAbiEncoder(functionSignature);
|
||||
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
|
||||
return abiEncoder.strictDecodeReturnValue<BigNumber>(rawCallResult);
|
||||
},
|
||||
getABIEncodedTransactionData(): string {
|
||||
return self._strictEncodeArguments(functionSignature, [
|
||||
takerToken.toLowerCase(),
|
||||
makerToken.toLowerCase(),
|
||||
buyAmount,
|
||||
]);
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Quotes the amount of `makerToken` that would be obtained by
|
||||
* selling `sellAmount` of `takerToken`.
|
||||
* @param takerToken Address of the taker token (what to sell).
|
||||
* @param makerToken Address of the maker token (what to buy).
|
||||
* @param sellAmount Amount of `takerToken` to sell.
|
||||
* @returns makerTokenAmount Amount of `makerToken` that would be obtained.
|
||||
*/
|
||||
public getSellQuote(takerToken: string, makerToken: string, sellAmount: BigNumber): ContractFunctionObj<BigNumber> {
|
||||
const self = (this as any) as ILiquidityProviderContract;
|
||||
assert.isString('takerToken', takerToken);
|
||||
assert.isString('makerToken', makerToken);
|
||||
assert.isBigNumber('sellAmount', sellAmount);
|
||||
const functionSignature = 'getSellQuote(address,address,uint256)';
|
||||
|
||||
return {
|
||||
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber> {
|
||||
BaseContract._assertCallParams(callData, defaultBlock);
|
||||
const rawCallResult = await self._performCallAsync(
|
||||
{ ...callData, data: this.getABIEncodedTransactionData() },
|
||||
defaultBlock,
|
||||
);
|
||||
const abiEncoder = self._lookupAbiEncoder(functionSignature);
|
||||
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
|
||||
return abiEncoder.strictDecodeReturnValue<BigNumber>(rawCallResult);
|
||||
},
|
||||
getABIEncodedTransactionData(): string {
|
||||
return self._strictEncodeArguments(functionSignature, [
|
||||
takerToken.toLowerCase(),
|
||||
makerToken.toLowerCase(),
|
||||
sellAmount,
|
||||
]);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
constructor(
|
||||
address: string,
|
||||
supportedProvider: SupportedProvider,
|
||||
txDefaults?: Partial<TxData>,
|
||||
logDecodeDependencies?: { [contractName: string]: ContractAbi },
|
||||
deployedBytecode: string | undefined = ILiquidityProviderContract.deployedBytecode,
|
||||
) {
|
||||
super(
|
||||
'ILiquidityProvider',
|
||||
ILiquidityProviderContract.ABI(),
|
||||
address,
|
||||
supportedProvider,
|
||||
txDefaults,
|
||||
logDecodeDependencies,
|
||||
deployedBytecode,
|
||||
);
|
||||
classUtils.bindAll(this, ['_abiEncoderByFunctionSignature', 'address', '_web3Wrapper']);
|
||||
ILiquidityProviderContract.ABI().forEach((item, index) => {
|
||||
if (item.type === 'function') {
|
||||
const methodAbi = item as MethodAbi;
|
||||
this._methodABIIndex[methodAbi.name] = index;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// tslint:disable:max-file-line-count
|
||||
// tslint:enable:no-unbound-method no-parameter-reassignment no-consecutive-blank-lines ordered-imports align
|
||||
// tslint:enable:trailing-comma whitespace no-trailing-whitespace
|
||||
327
packages/contract-wrappers/src/generated-wrappers/i_liquidity_provider_registry.ts
generated
Normal file
327
packages/contract-wrappers/src/generated-wrappers/i_liquidity_provider_registry.ts
generated
Normal file
@@ -0,0 +1,327 @@
|
||||
// tslint:disable:no-consecutive-blank-lines ordered-imports align trailing-comma enum-naming
|
||||
// tslint:disable:whitespace no-unbound-method no-trailing-whitespace
|
||||
// tslint:disable:no-unused-variable
|
||||
import {
|
||||
AwaitTransactionSuccessOpts,
|
||||
ContractFunctionObj,
|
||||
ContractTxFunctionObj,
|
||||
SendTransactionOpts,
|
||||
BaseContract,
|
||||
PromiseWithTransactionHash,
|
||||
methodAbiToFunctionSignature,
|
||||
linkLibrariesInBytecode,
|
||||
} from '@0x/base-contract';
|
||||
import { schemas } from '@0x/json-schemas';
|
||||
import {
|
||||
BlockParam,
|
||||
BlockParamLiteral,
|
||||
BlockRange,
|
||||
CallData,
|
||||
ContractAbi,
|
||||
ContractArtifact,
|
||||
DecodedLogArgs,
|
||||
MethodAbi,
|
||||
TransactionReceiptWithDecodedLogs,
|
||||
TxData,
|
||||
TxDataPayable,
|
||||
SupportedProvider,
|
||||
} from 'ethereum-types';
|
||||
import { BigNumber, classUtils, hexUtils, logUtils, providerUtils } from '@0x/utils';
|
||||
import { EventCallback, IndexedFilterValues, SimpleContractArtifact } from '@0x/types';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { assert } from '@0x/assert';
|
||||
import * as ethers from 'ethers';
|
||||
// tslint:enable:no-unused-variable
|
||||
|
||||
/* istanbul ignore next */
|
||||
// tslint:disable:array-type
|
||||
// tslint:disable:no-parameter-reassignment
|
||||
// tslint:disable-next-line:class-name
|
||||
export class ILiquidityProviderRegistryContract extends BaseContract {
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public static deployedBytecode: string | undefined;
|
||||
public static contractName = 'ILiquidityProviderRegistry';
|
||||
private readonly _methodABIIndex: { [name: string]: number } = {};
|
||||
public static async deployFrom0xArtifactAsync(
|
||||
artifact: ContractArtifact | SimpleContractArtifact,
|
||||
supportedProvider: SupportedProvider,
|
||||
txDefaults: Partial<TxData>,
|
||||
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
|
||||
): Promise<ILiquidityProviderRegistryContract> {
|
||||
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
|
||||
schemas.addressSchema,
|
||||
schemas.numberSchema,
|
||||
schemas.jsNumber,
|
||||
]);
|
||||
if (artifact.compilerOutput === undefined) {
|
||||
throw new Error('Compiler output not found in the artifact file');
|
||||
}
|
||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||
const bytecode = artifact.compilerOutput.evm.bytecode.object;
|
||||
const abi = artifact.compilerOutput.abi;
|
||||
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
|
||||
if (Object.keys(logDecodeDependencies) !== undefined) {
|
||||
for (const key of Object.keys(logDecodeDependencies)) {
|
||||
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
|
||||
}
|
||||
}
|
||||
return ILiquidityProviderRegistryContract.deployAsync(
|
||||
bytecode,
|
||||
abi,
|
||||
provider,
|
||||
txDefaults,
|
||||
logDecodeDependenciesAbiOnly,
|
||||
);
|
||||
}
|
||||
|
||||
public static async deployWithLibrariesFrom0xArtifactAsync(
|
||||
artifact: ContractArtifact,
|
||||
libraryArtifacts: { [libraryName: string]: ContractArtifact },
|
||||
supportedProvider: SupportedProvider,
|
||||
txDefaults: Partial<TxData>,
|
||||
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
|
||||
): Promise<ILiquidityProviderRegistryContract> {
|
||||
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
|
||||
schemas.addressSchema,
|
||||
schemas.numberSchema,
|
||||
schemas.jsNumber,
|
||||
]);
|
||||
if (artifact.compilerOutput === undefined) {
|
||||
throw new Error('Compiler output not found in the artifact file');
|
||||
}
|
||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||
const abi = artifact.compilerOutput.abi;
|
||||
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
|
||||
if (Object.keys(logDecodeDependencies) !== undefined) {
|
||||
for (const key of Object.keys(logDecodeDependencies)) {
|
||||
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
|
||||
}
|
||||
}
|
||||
const libraryAddresses = await ILiquidityProviderRegistryContract._deployLibrariesAsync(
|
||||
artifact,
|
||||
libraryArtifacts,
|
||||
new Web3Wrapper(provider),
|
||||
txDefaults,
|
||||
);
|
||||
const bytecode = linkLibrariesInBytecode(artifact, libraryAddresses);
|
||||
return ILiquidityProviderRegistryContract.deployAsync(
|
||||
bytecode,
|
||||
abi,
|
||||
provider,
|
||||
txDefaults,
|
||||
logDecodeDependenciesAbiOnly,
|
||||
);
|
||||
}
|
||||
|
||||
public static async deployAsync(
|
||||
bytecode: string,
|
||||
abi: ContractAbi,
|
||||
supportedProvider: SupportedProvider,
|
||||
txDefaults: Partial<TxData>,
|
||||
logDecodeDependencies: { [contractName: string]: ContractAbi },
|
||||
): Promise<ILiquidityProviderRegistryContract> {
|
||||
assert.isHexString('bytecode', bytecode);
|
||||
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
|
||||
schemas.addressSchema,
|
||||
schemas.numberSchema,
|
||||
schemas.jsNumber,
|
||||
]);
|
||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||
const constructorAbi = BaseContract._lookupConstructorAbi(abi);
|
||||
[] = BaseContract._formatABIDataItemList(constructorAbi.inputs, [], BaseContract._bigNumberToString);
|
||||
const iface = new ethers.utils.Interface(abi);
|
||||
const deployInfo = iface.deployFunction;
|
||||
const txData = deployInfo.encode(bytecode, []);
|
||||
const web3Wrapper = new Web3Wrapper(provider);
|
||||
const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync(
|
||||
{
|
||||
data: txData,
|
||||
...txDefaults,
|
||||
},
|
||||
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
|
||||
);
|
||||
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
|
||||
logUtils.log(`transactionHash: ${txHash}`);
|
||||
const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||
logUtils.log(`ILiquidityProviderRegistry successfully deployed at ${txReceipt.contractAddress}`);
|
||||
const contractInstance = new ILiquidityProviderRegistryContract(
|
||||
txReceipt.contractAddress as string,
|
||||
provider,
|
||||
txDefaults,
|
||||
logDecodeDependencies,
|
||||
);
|
||||
contractInstance.constructorArgs = [];
|
||||
return contractInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The contract ABI
|
||||
*/
|
||||
public static ABI(): ContractAbi {
|
||||
const abi = [
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'takerToken',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'makerToken',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
name: 'getLiquidityProviderForMarket',
|
||||
outputs: [
|
||||
{
|
||||
name: 'providerAddress',
|
||||
type: 'address',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
] as ContractAbi;
|
||||
return abi;
|
||||
}
|
||||
|
||||
protected static async _deployLibrariesAsync(
|
||||
artifact: ContractArtifact,
|
||||
libraryArtifacts: { [libraryName: string]: ContractArtifact },
|
||||
web3Wrapper: Web3Wrapper,
|
||||
txDefaults: Partial<TxData>,
|
||||
libraryAddresses: { [libraryName: string]: string } = {},
|
||||
): Promise<{ [libraryName: string]: string }> {
|
||||
const links = artifact.compilerOutput.evm.bytecode.linkReferences;
|
||||
// Go through all linked libraries, recursively deploying them if necessary.
|
||||
for (const link of Object.values(links)) {
|
||||
for (const libraryName of Object.keys(link)) {
|
||||
if (!libraryAddresses[libraryName]) {
|
||||
// Library not yet deployed.
|
||||
const libraryArtifact = libraryArtifacts[libraryName];
|
||||
if (!libraryArtifact) {
|
||||
throw new Error(`Missing artifact for linked library "${libraryName}"`);
|
||||
}
|
||||
// Deploy any dependent libraries used by this library.
|
||||
await ILiquidityProviderRegistryContract._deployLibrariesAsync(
|
||||
libraryArtifact,
|
||||
libraryArtifacts,
|
||||
web3Wrapper,
|
||||
txDefaults,
|
||||
libraryAddresses,
|
||||
);
|
||||
// Deploy this library.
|
||||
const linkedLibraryBytecode = linkLibrariesInBytecode(libraryArtifact, libraryAddresses);
|
||||
const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync(
|
||||
{
|
||||
data: linkedLibraryBytecode,
|
||||
...txDefaults,
|
||||
},
|
||||
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
|
||||
);
|
||||
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
|
||||
logUtils.log(`transactionHash: ${txHash}`);
|
||||
const { contractAddress } = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||
logUtils.log(`${libraryArtifact.contractName} successfully deployed at ${contractAddress}`);
|
||||
libraryAddresses[libraryArtifact.contractName] = contractAddress as string;
|
||||
}
|
||||
}
|
||||
}
|
||||
return libraryAddresses;
|
||||
}
|
||||
|
||||
public getFunctionSignature(methodName: string): string {
|
||||
const index = this._methodABIIndex[methodName];
|
||||
const methodAbi = ILiquidityProviderRegistryContract.ABI()[index] as MethodAbi; // tslint:disable-line:no-unnecessary-type-assertion
|
||||
const functionSignature = methodAbiToFunctionSignature(methodAbi);
|
||||
return functionSignature;
|
||||
}
|
||||
|
||||
public getABIDecodedTransactionData<T>(methodName: string, callData: string): T {
|
||||
const functionSignature = this.getFunctionSignature(methodName);
|
||||
const self = (this as any) as ILiquidityProviderRegistryContract;
|
||||
const abiEncoder = self._lookupAbiEncoder(functionSignature);
|
||||
const abiDecodedCallData = abiEncoder.strictDecode<T>(callData);
|
||||
return abiDecodedCallData;
|
||||
}
|
||||
|
||||
public getABIDecodedReturnData<T>(methodName: string, callData: string): T {
|
||||
const functionSignature = this.getFunctionSignature(methodName);
|
||||
const self = (this as any) as ILiquidityProviderRegistryContract;
|
||||
const abiEncoder = self._lookupAbiEncoder(functionSignature);
|
||||
const abiDecodedCallData = abiEncoder.strictDecodeReturnValue<T>(callData);
|
||||
return abiDecodedCallData;
|
||||
}
|
||||
|
||||
public getSelector(methodName: string): string {
|
||||
const functionSignature = this.getFunctionSignature(methodName);
|
||||
const self = (this as any) as ILiquidityProviderRegistryContract;
|
||||
const abiEncoder = self._lookupAbiEncoder(functionSignature);
|
||||
return abiEncoder.getSelector();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of a liquidity provider for the given market
|
||||
* (takerToken, makerToken), reverting if the pool does not exist.
|
||||
* @param takerToken Taker asset managed by liquidity provider.
|
||||
* @param makerToken Maker asset managed by liquidity provider.
|
||||
* @returns Address of the liquidity provider.
|
||||
*/
|
||||
public getLiquidityProviderForMarket(takerToken: string, makerToken: string): ContractFunctionObj<string> {
|
||||
const self = (this as any) as ILiquidityProviderRegistryContract;
|
||||
assert.isString('takerToken', takerToken);
|
||||
assert.isString('makerToken', makerToken);
|
||||
const functionSignature = 'getLiquidityProviderForMarket(address,address)';
|
||||
|
||||
return {
|
||||
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<string> {
|
||||
BaseContract._assertCallParams(callData, defaultBlock);
|
||||
const rawCallResult = await self._performCallAsync(
|
||||
{ ...callData, data: this.getABIEncodedTransactionData() },
|
||||
defaultBlock,
|
||||
);
|
||||
const abiEncoder = self._lookupAbiEncoder(functionSignature);
|
||||
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
|
||||
return abiEncoder.strictDecodeReturnValue<string>(rawCallResult);
|
||||
},
|
||||
getABIEncodedTransactionData(): string {
|
||||
return self._strictEncodeArguments(functionSignature, [
|
||||
takerToken.toLowerCase(),
|
||||
makerToken.toLowerCase(),
|
||||
]);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
constructor(
|
||||
address: string,
|
||||
supportedProvider: SupportedProvider,
|
||||
txDefaults?: Partial<TxData>,
|
||||
logDecodeDependencies?: { [contractName: string]: ContractAbi },
|
||||
deployedBytecode: string | undefined = ILiquidityProviderRegistryContract.deployedBytecode,
|
||||
) {
|
||||
super(
|
||||
'ILiquidityProviderRegistry',
|
||||
ILiquidityProviderRegistryContract.ABI(),
|
||||
address,
|
||||
supportedProvider,
|
||||
txDefaults,
|
||||
logDecodeDependencies,
|
||||
deployedBytecode,
|
||||
);
|
||||
classUtils.bindAll(this, ['_abiEncoderByFunctionSignature', 'address', '_web3Wrapper']);
|
||||
ILiquidityProviderRegistryContract.ABI().forEach((item, index) => {
|
||||
if (item.type === 'function') {
|
||||
const methodAbi = item as MethodAbi;
|
||||
this._methodABIIndex[methodAbi.name] = index;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// tslint:disable:max-file-line-count
|
||||
// tslint:enable:no-unbound-method no-parameter-reassignment no-consecutive-blank-lines ordered-imports align
|
||||
// tslint:enable:trailing-comma whitespace no-trailing-whitespace
|
||||
@@ -1,3 +1,5 @@
|
||||
export { ERC20BridgeSamplerContract } from './generated-wrappers/erc20_bridge_sampler';
|
||||
|
||||
export { ContractAddresses } from '@0x/contract-addresses';
|
||||
|
||||
export { ContractWrappers } from './contract_wrappers';
|
||||
|
||||
Reference in New Issue
Block a user