Curve ERC20Bridge (#2480)

* Curve ERC20Bridge

* ERC20BridgeSampler Curve (#2483)

* ERC20Sampler Curve

* Use Bridge Sources for each Curve

* Support multiple versions of the Curve contract

* CHANGELOG and redeployed Curve (mainnet)

* Fix Market ops utils test

* Added Curve DAI USDC USDT TUSD

* Bump sampler gas limit default

* Decode the Curve in tests

* Disable Curve in Buy tests

* blockchainTests.fork.resets Curve and Sampler
This commit is contained in:
Jacob Evans
2020-02-15 17:02:19 +11:00
committed by GitHub
parent dcce8276b8
commit e05a03a842
35 changed files with 839 additions and 35 deletions

View File

@@ -5,6 +5,10 @@
{
"note": "Use `batchCall()` version of the `ERC20BridgeSampler` contract",
"pr": 2477
},
{
"note": "Support for sampling Curve contracts",
"pr": 2483
}
]
},

View File

@@ -12,6 +12,7 @@ import {
} from './types';
import { constants as marketOperationUtilConstants } from './utils/market_operation_utils/constants';
import { ERC20BridgeSource } from './utils/market_operation_utils/types';
const ETH_GAS_STATION_API_BASE_URL = 'https://ethgasstation.info';
const NULL_BYTES = '0x';
@@ -42,7 +43,7 @@ const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
orderRefreshIntervalMs: 10000, // 10 seconds
},
...DEFAULT_ORDER_PRUNER_OPTS,
samplerGasLimit: 36e6,
samplerGasLimit: 59e6,
};
const DEFAULT_FORWARDER_EXTENSION_CONTRACT_OPTS: ForwarderExtensionContractOpts = {
@@ -64,6 +65,34 @@ const DEFAULT_SWAP_QUOTE_REQUEST_OPTS: SwapQuoteRequestOpts = {
...marketOperationUtilConstants.DEFAULT_GET_MARKET_ORDERS_OPTS,
};
// Mainnet Curve configuration
const DEFAULT_CURVE_OPTS: { [source: string]: { version: number; curveAddress: string; tokens: string[] } } = {
[ERC20BridgeSource.CurveUsdcDai]: {
version: 0,
curveAddress: '0x2e60cf74d81ac34eb21eeff58db4d385920ef419',
tokens: ['0x6b175474e89094c44da98b954eedeac495271d0f', '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'],
},
[ERC20BridgeSource.CurveUsdcDaiUsdt]: {
version: 1,
curveAddress: '0x52ea46506b9cc5ef470c5bf89f17dc28bb35d85c',
tokens: [
'0x6b175474e89094c44da98b954eedeac495271d0f',
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
'0xdac17f958d2ee523a2206206994597c13d831ec7',
],
},
[ERC20BridgeSource.CurveUsdcDaiUsdtTusd]: {
version: 1,
curveAddress: '0x45f783cce6b7ff23b2ab2d70e416cdb7d6055f51',
tokens: [
'0x6b175474e89094c44da98b954eedeac495271d0f',
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
'0xdac17f958d2ee523a2206206994597c13d831ec7',
'0x0000000000085d4780b73119b644ae5ecd22b376',
],
},
};
export const constants = {
ETH_GAS_STATION_API_BASE_URL,
PROTOCOL_FEE_MULTIPLIER,
@@ -84,4 +113,5 @@ export const constants = {
PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS,
MARKET_UTILS_AMOUNT_BUFFER_PERCENTAGE,
BRIDGE_ASSET_DATA_PREFIX: '0xdc1600f3',
DEFAULT_CURVE_OPTS,
};

View File

@@ -7,7 +7,14 @@ const INFINITE_TIMESTAMP_SEC = new BigNumber(2524604400);
/**
* Valid sources for market sell.
*/
export const SELL_SOURCES = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Kyber];
export const SELL_SOURCES = [
ERC20BridgeSource.Uniswap,
ERC20BridgeSource.Eth2Dai,
ERC20BridgeSource.Kyber,
ERC20BridgeSource.CurveUsdcDai,
ERC20BridgeSource.CurveUsdcDaiUsdt,
ERC20BridgeSource.CurveUsdcDaiUsdtTusd,
];
/**
* Valid sources for market buy.

View File

@@ -90,6 +90,10 @@ export class CreateOrderUtils {
return this._contractAddress.kyberBridge;
case ERC20BridgeSource.Uniswap:
return this._contractAddress.uniswapBridge;
case ERC20BridgeSource.CurveUsdcDai:
case ERC20BridgeSource.CurveUsdcDaiUsdt:
case ERC20BridgeSource.CurveUsdcDaiUsdtTusd:
return this._contractAddress.curveBridge;
default:
break;
}
@@ -106,13 +110,30 @@ function createBridgeOrder(
slippage: number,
isBuy: boolean = false,
): OptimizedMarketOrder {
return {
makerAddress: bridgeAddress,
makerAssetData: assetDataUtils.encodeERC20BridgeAssetData(
let makerAssetData;
if (
fill.source === ERC20BridgeSource.CurveUsdcDai ||
fill.source === ERC20BridgeSource.CurveUsdcDaiUsdt ||
fill.source === ERC20BridgeSource.CurveUsdcDaiUsdtTusd
) {
const { curveAddress, tokens, version } = constants.DEFAULT_CURVE_OPTS[fill.source];
const fromTokenIdx = tokens.indexOf(takerToken);
const toTokenIdx = tokens.indexOf(makerToken);
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
makerToken,
bridgeAddress,
createCurveBridgeData(curveAddress, fromTokenIdx, toTokenIdx, version),
);
} else {
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
makerToken,
bridgeAddress,
createBridgeData(takerToken),
),
);
}
return {
makerAddress: bridgeAddress,
makerAssetData,
takerAssetData: assetDataUtils.encodeERC20AssetData(takerToken),
...createCommonOrderFields(orderDomain, fill, slippage, isBuy),
};
@@ -123,6 +144,21 @@ function createBridgeData(tokenAddress: string): string {
return encoder.encode({ tokenAddress });
}
function createCurveBridgeData(
curveAddress: string,
fromTokenIdx: number,
toTokenIdx: number,
version: number,
): string {
const curveBridgeDataEncoder = AbiEncoder.create([
{ name: 'curveAddress', type: 'address' },
{ name: 'fromTokenIdx', type: 'int128' },
{ name: 'toTokenIdx', type: 'int128' },
{ name: 'version', type: 'int128' },
]);
return curveBridgeDataEncoder.encode([curveAddress, fromTokenIdx, toTokenIdx, version]);
}
type CommonOrderFields = Pick<
OptimizedMarketOrder,
Exclude<keyof OptimizedMarketOrder, 'makerAddress' | 'makerAssetData' | 'takerAssetData'>

View File

@@ -2,6 +2,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';
/**
@@ -89,6 +91,28 @@ const samplerOperations = {
},
};
},
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,
@@ -127,30 +151,55 @@ const samplerOperations = {
takerToken: string,
takerFillAmounts: BigNumber[],
): BatchedOperation<DexSample[][]> {
const subOps = sources.map(source => {
if (source === ERC20BridgeSource.Eth2Dai) {
return samplerOperations.getEth2DaiSellQuotes(makerToken, takerToken, takerFillAmounts);
} else if (source === ERC20BridgeSource.Uniswap) {
return samplerOperations.getUniswapSellQuotes(makerToken, takerToken, takerFillAmounts);
} else if (source === ERC20BridgeSource.Kyber) {
return samplerOperations.getKyberSellQuotes(makerToken, takerToken, takerFillAmounts);
} else {
throw new Error(`Unsupported sell sample source: ${source}`);
}
});
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 (
source === ERC20BridgeSource.CurveUsdcDai ||
source === ERC20BridgeSource.CurveUsdcDaiUsdt ||
source === ERC20BridgeSource.CurveUsdcDaiUsdtTusd
) {
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.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.handleCallResultsAsync(contract, rawSubCallResults[i])),
subOps.map(async (op, i) =>
op.batchedOperation.handleCallResultsAsync(contract, rawSubCallResults[i]),
),
);
return sources.map((source, i) => {
return subOps.map((op, i) => {
return samples[i].map((output, j) => ({
source,
source: op.source,
output,
input: takerFillAmounts[j],
}));

View File

@@ -28,6 +28,9 @@ export enum ERC20BridgeSource {
Uniswap = 'Uniswap',
Eth2Dai = 'Eth2Dai',
Kyber = 'Kyber',
CurveUsdcDai = 'Curve_USDC_DAI',
CurveUsdcDaiUsdt = 'Curve_USDC_DAI_USDT',
CurveUsdcDaiUsdtTusd = 'Curve_USDC_DAI_USDT_TUSD',
}
// Internal `fillData` field for `Fill` objects.

View File

@@ -14,6 +14,7 @@ import { SignedOrder } from '@0x/types';
import { BigNumber, hexUtils } from '@0x/utils';
import * as _ from 'lodash';
import { constants as assetSwapperConstants } from '../src/constants';
import { MarketOperationUtils } from '../src/utils/market_operation_utils/';
import { constants as marketOperationUtilConstants } from '../src/utils/market_operation_utils/constants';
import { DexOrderSampler } from '../src/utils/market_operation_utils/sampler';
@@ -28,6 +29,7 @@ describe('MarketOperationUtils tests', () => {
const ETH2DAI_BRIDGE_ADDRESS = contractAddresses.eth2DaiBridge;
const KYBER_BRIDGE_ADDRESS = contractAddresses.kyberBridge;
const UNISWAP_BRIDGE_ADDRESS = contractAddresses.uniswapBridge;
const CURVE_BRIDGE_ADDRESS = contractAddresses.curveBridge;
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
@@ -78,6 +80,11 @@ describe('MarketOperationUtils tests', () => {
return ERC20BridgeSource.Eth2Dai;
case UNISWAP_BRIDGE_ADDRESS.toLowerCase():
return ERC20BridgeSource.Uniswap;
case CURVE_BRIDGE_ADDRESS.toLowerCase():
const curveSource = Object.keys(assetSwapperConstants.DEFAULT_CURVE_OPTS).filter(
k => assetData.indexOf(assetSwapperConstants.DEFAULT_CURVE_OPTS[k].curveAddress.slice(2)) !== -1,
);
return curveSource[0] as ERC20BridgeSource;
default:
break;
}
@@ -116,13 +123,15 @@ describe('MarketOperationUtils tests', () => {
type GetQuotesOperation = (makerToken: string, takerToken: string, fillAmounts: BigNumber[]) => BigNumber[];
function createGetSellQuotesOperationFromRates(rates: Numberish[]): GetQuotesOperation {
return (makerToken: string, takerToken: string, fillAmounts: BigNumber[]) => {
return (...args) => {
const fillAmounts = args.pop() as BigNumber[];
return fillAmounts.map((a, i) => a.times(rates[i]).integerValue());
};
}
function createGetBuyQuotesOperationFromRates(rates: Numberish[]): GetQuotesOperation {
return (makerToken: string, takerToken: string, fillAmounts: BigNumber[]) => {
return (...args) => {
const fillAmounts = args.pop() as BigNumber[];
return fillAmounts.map((a, i) => a.div(rates[i]).integerValue());
};
}
@@ -179,6 +188,9 @@ describe('MarketOperationUtils tests', () => {
[ERC20BridgeSource.Eth2Dai]: createDecreasingRates(NUM_SAMPLES),
[ERC20BridgeSource.Kyber]: createDecreasingRates(NUM_SAMPLES),
[ERC20BridgeSource.Uniswap]: createDecreasingRates(NUM_SAMPLES),
[ERC20BridgeSource.CurveUsdcDai]: createDecreasingRates(NUM_SAMPLES),
[ERC20BridgeSource.CurveUsdcDaiUsdt]: createDecreasingRates(NUM_SAMPLES),
[ERC20BridgeSource.CurveUsdcDaiUsdtTusd]: createDecreasingRates(NUM_SAMPLES),
};
function findSourceWithMaxOutput(rates: RatesBySource): ERC20BridgeSource {
@@ -209,6 +221,7 @@ describe('MarketOperationUtils tests', () => {
getEth2DaiSellQuotes: createGetSellQuotesOperationFromRates(DEFAULT_RATES[ERC20BridgeSource.Eth2Dai]),
getUniswapBuyQuotes: createGetBuyQuotesOperationFromRates(DEFAULT_RATES[ERC20BridgeSource.Uniswap]),
getEth2DaiBuyQuotes: createGetBuyQuotesOperationFromRates(DEFAULT_RATES[ERC20BridgeSource.Eth2Dai]),
getCurveSellQuotes: createGetSellQuotesOperationFromRates(DEFAULT_RATES[ERC20BridgeSource.CurveUsdcDai]),
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(DEFAULT_RATES),
getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(DEFAULT_RATES),
};
@@ -386,6 +399,9 @@ describe('MarketOperationUtils tests', () => {
rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05];
rates[ERC20BridgeSource.Eth2Dai] = [0.6, 0.05, 0.05, 0.05];
rates[ERC20BridgeSource.Kyber] = [0.7, 0.05, 0.05, 0.05];
rates[ERC20BridgeSource.CurveUsdcDai] = [0, 0, 0, 0];
rates[ERC20BridgeSource.CurveUsdcDaiUsdt] = [0, 0, 0, 0];
rates[ERC20BridgeSource.CurveUsdcDaiUsdtTusd] = [0, 0, 0, 0];
replaceSamplerOps({
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates),
});
@@ -411,6 +427,9 @@ describe('MarketOperationUtils tests', () => {
rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05];
rates[ERC20BridgeSource.Eth2Dai] = [0.6, 0.05, 0.05, 0.05];
rates[ERC20BridgeSource.Kyber] = [0.4, 0.05, 0.05, 0.05];
rates[ERC20BridgeSource.CurveUsdcDai] = [0, 0, 0, 0];
rates[ERC20BridgeSource.CurveUsdcDaiUsdt] = [0, 0, 0, 0];
rates[ERC20BridgeSource.CurveUsdcDaiUsdtTusd] = [0, 0, 0, 0];
replaceSamplerOps({
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates),
});
@@ -436,6 +455,9 @@ describe('MarketOperationUtils tests', () => {
rates[ERC20BridgeSource.Uniswap] = [0.15, 0.05, 0.05, 0.05];
rates[ERC20BridgeSource.Eth2Dai] = [0.15, 0.05, 0.05, 0.05];
rates[ERC20BridgeSource.Kyber] = [0.7, 0.05, 0.05, 0.05];
rates[ERC20BridgeSource.CurveUsdcDai] = [0, 0, 0, 0];
rates[ERC20BridgeSource.CurveUsdcDaiUsdt] = [0, 0, 0, 0];
rates[ERC20BridgeSource.CurveUsdcDaiUsdtTusd] = [0, 0, 0, 0];
replaceSamplerOps({
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates),
});
@@ -516,7 +538,15 @@ describe('MarketOperationUtils tests', () => {
});
it('returns the most cost-effective single source if `runLimit == 0`', async () => {
const bestSource = findSourceWithMaxOutput(_.omit(DEFAULT_RATES, ERC20BridgeSource.Kyber));
const bestSource = findSourceWithMaxOutput(
_.omit(
DEFAULT_RATES,
ERC20BridgeSource.Kyber,
ERC20BridgeSource.CurveUsdcDai,
ERC20BridgeSource.CurveUsdcDaiUsdt,
ERC20BridgeSource.CurveUsdcDaiUsdtTusd,
),
);
expect(bestSource).to.exist('');
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, {
...DEFAULT_OPTS,

View File

@@ -3,8 +3,16 @@
"version": "4.6.0",
"changes": [
{
"note": "Added ChainlinkStopLimit addresses (mainnet, ropsten, rinkeby)",
"note": "Added `ChainlinkStopLimit` addresses (mainnet, ropsten, rinkeby)",
"pr": 2473
},
{
"note": "Added `CurveBridge` address (mainnet)",
"pr": 2483
},
{
"note": "Update `ERC20BridgeSampler` address (mainnet, kovan)",
"pr": 2483
}
]
},

View File

@@ -20,14 +20,15 @@
"devUtils": "0xb1a3d901bad1df7d710fc8d008db7cdd6bbbffe6",
"erc20BridgeProxy": "0x8ed95d1746bf1e4dab58d8ed4724f1ef95b20db0",
"uniswapBridge": "0x533344cfdf2a3e911e2cf4c6f5ed08e791f5355f",
"erc20BridgeSampler": "0x774c53ee7604af93cd3ed1cd25a788a9e0c06fb2",
"erc20BridgeSampler": "0x43cfd1027bcc01d3df714d9e7bf989622e4bbec1",
"kyberBridge": "0xf342f3a80fdc9b48713d58fe97e17f5cc764ee62",
"eth2DaiBridge": "0xe3379a1956f4a79f39eb2e87bb441419e167538e",
"chaiBridge": "0x77c31eba23043b9a72d13470f3a3a311344d7438",
"dydxBridge": "0x55dc8f21d20d4c6ed3c82916a438a413ca68e335",
"godsUnchainedValidator": "0x09A379Ef7218BCFD8913fAa8B281ebc5A2E0bC04",
"broker": "0xd4690a51044db77D91d7Aa8f7a3a5ad5dA331Af0",
"chainlinkStopLimit": "0xeb27220f95f364e1d9531992c48613f231839f53"
"chainlinkStopLimit": "0xeb27220f95f364e1d9531992c48613f231839f53",
"curveBridge": "0xe335bdd1fb0ee30f9a9a434f18f8b118dec32df7"
},
"3": {
"erc20Proxy": "0xb1408f4c245a23c31b98d2c626777d4c0d766caa",
@@ -57,7 +58,8 @@
"dydxBridge": "0x0000000000000000000000000000000000000000",
"godsUnchainedValidator": "0xd4690a51044db77D91d7Aa8f7a3a5ad5dA331Af0",
"broker": "0x4Aa817C6f383C8e8aE77301d18Ce48efb16Fd2BE",
"chainlinkStopLimit": "0x67a094cf028221ffdd93fc658f963151d05e2a74"
"chainlinkStopLimit": "0x67a094cf028221ffdd93fc658f963151d05e2a74",
"curveBridge": "0x0000000000000000000000000000000000000000"
},
"4": {
"exchangeV2": "0xbff9493f92a3df4b0429b6d00743b3cfb4c85831",
@@ -87,7 +89,8 @@
"dydxBridge": "0x0000000000000000000000000000000000000000",
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
"broker": "0x0000000000000000000000000000000000000000",
"chainlinkStopLimit": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a"
"chainlinkStopLimit": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a",
"curveBridge": "0x0000000000000000000000000000000000000000"
},
"42": {
"erc20Proxy": "0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e",
@@ -111,13 +114,14 @@
"erc20BridgeProxy": "0xfb2dd2a1366de37f7241c83d47da58fd503e2c64",
"uniswapBridge": "0x8224aa8fe5c9f07d5a59c735386ff6cc6aaeb568",
"eth2DaiBridge": "0x9485d65c6a2fae0d519cced5bd830e57c41998a9",
"erc20BridgeSampler": "0xca6485a7d0f1a42192072dff7518324513294adf",
"erc20BridgeSampler": "0x0937795148f54f08538390d936e898163684b33f",
"kyberBridge": "0xde7b2747624a647600fdb349184d0448ab954929",
"chaiBridge": "0x0000000000000000000000000000000000000000",
"dydxBridge": "0x0000000000000000000000000000000000000000",
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
"broker": "0x0000000000000000000000000000000000000000",
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000"
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
"curveBridge": "0x0000000000000000000000000000000000000000"
},
"1337": {
"erc20Proxy": "0x1dc4c1cefef38a777b15aa20260a54e584b16c48",
@@ -147,6 +151,7 @@
"dydxBridge": "0x0000000000000000000000000000000000000000",
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
"broker": "0x0000000000000000000000000000000000000000",
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000"
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
"curveBridge": "0x0000000000000000000000000000000000000000"
}
}

View File

@@ -28,6 +28,7 @@ export interface ContractAddresses {
kyberBridge: string;
chaiBridge: string;
dydxBridge: string;
curveBridge: string;
}
export enum ChainId {

View File

@@ -106,6 +106,20 @@
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{ "internalType": "address", "name": "curveAddress", "type": "address" },
{ "internalType": "int128", "name": "fromTokenIdx", "type": "int128" },
{ "internalType": "int128", "name": "toTokenIdx", "type": "int128" },
{ "internalType": "uint256[]", "name": "takerTokenAmounts", "type": "uint256[]" }
],
"name": "sampleSellsFromCurve",
"outputs": [{ "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
@@ -187,6 +201,16 @@
},
"return": "takerTokenAmounts Taker amounts sold at each maker token amount."
},
"sampleSellsFromCurve(address,int128,int128,uint256[])": {
"details": "Sample sell quotes from Curve.",
"params": {
"curveAddress": "Address of the Curve contract.",
"fromTokenIdx": "Index of the taker token (what to sell).",
"takerTokenAmounts": "Taker token sell amount for each sample.",
"toTokenIdx": "Index of the maker token (what to buy)."
},
"return": "makerTokenAmounts Maker amounts bought at each taker token amount."
},
"sampleSellsFromEth2Dai(address,address,uint256[])": {
"details": "Sample sell quotes from Eth2Dai/Oasis.",
"params": {

View File

@@ -396,6 +396,37 @@ export class IERC20BridgeSamplerContract extends BaseContract {
stateMutability: 'view',
type: 'function',
},
{
constant: true,
inputs: [
{
name: 'curveAddress',
type: 'address',
},
{
name: 'fromTokenIdx',
type: 'int128',
},
{
name: 'toTokenIdx',
type: 'int128',
},
{
name: 'takerTokenAmounts',
type: 'uint256[]',
},
],
name: 'sampleSellsFromCurve',
outputs: [
{
name: 'makerTokenAmounts',
type: 'uint256[]',
},
],
payable: false,
stateMutability: 'view',
type: 'function',
},
{
constant: true,
inputs: [
@@ -751,6 +782,48 @@ export class IERC20BridgeSamplerContract extends BaseContract {
},
};
}
/**
* Sample sell quotes from Curve.
* @param curveAddress Address of the Curve contract.
* @param fromTokenIdx Index of the taker token (what to sell).
* @param toTokenIdx Index 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 sampleSellsFromCurve(
curveAddress: string,
fromTokenIdx: BigNumber,
toTokenIdx: BigNumber,
takerTokenAmounts: BigNumber[],
): ContractFunctionObj<BigNumber[]> {
const self = (this as any) as IERC20BridgeSamplerContract;
assert.isString('curveAddress', curveAddress);
assert.isBigNumber('fromTokenIdx', fromTokenIdx);
assert.isBigNumber('toTokenIdx', toTokenIdx);
assert.isArray('takerTokenAmounts', takerTokenAmounts);
const functionSignature = 'sampleSellsFromCurve(address,int128,int128,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, [
curveAddress.toLowerCase(),
fromTokenIdx,
toTokenIdx,
takerTokenAmounts,
]);
},
};
}
/**
* Sample sell quotes from Eth2Dai/Oasis.
* @param takerToken Address of the taker token (what to sell).

View File

@@ -1,4 +1,13 @@
[
{
"version": "6.2.0",
"changes": [
{
"note": "Added `CurveBridge` address (null)",
"pr": 2483
}
]
},
{
"version": "6.1.0",
"changes": [

View File

@@ -302,6 +302,7 @@ export async function runMigrationsAsync(
erc20BridgeSampler: constants.NULL_ADDRESS,
chaiBridge: constants.NULL_ADDRESS,
dydxBridge: constants.NULL_ADDRESS,
curveBridge: constants.NULL_ADDRESS,
};
return contractAddresses;
}