@0x/contracts-erc20-bridge-sampler
: Increase gas forwarded to kyber and eth2dai.
`@0x/contracts-erc20-bridge-sampler`: Bail as soon as any quote from a DEX fails. `@0x/contracts-erc20-bridge-sampler`: Fix broken tests.
This commit is contained in:
committed by
Jacob Evans
parent
7f56091fbd
commit
0cf3ff8209
@@ -5,6 +5,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Add batch functions to query quotes",
|
"note": "Add batch functions to query quotes",
|
||||||
"pr": 2427
|
"pr": 2427
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Early exit if a DEX sample fails",
|
||||||
|
"pr": 2427
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@@ -37,9 +37,9 @@ contract ERC20BridgeSampler is
|
|||||||
DeploymentConstants
|
DeploymentConstants
|
||||||
{
|
{
|
||||||
bytes4 constant internal ERC20_PROXY_ID = 0xf47261b0; // bytes4(keccak256("ERC20Token(address)"));
|
bytes4 constant internal ERC20_PROXY_ID = 0xf47261b0; // bytes4(keccak256("ERC20Token(address)"));
|
||||||
uint256 constant internal KYBER_SAMPLE_CALL_GAS = 600e3;
|
uint256 constant internal KYBER_SAMPLE_CALL_GAS = 1500e3;
|
||||||
uint256 constant internal UNISWAP_SAMPLE_CALL_GAS = 150e3;
|
uint256 constant internal UNISWAP_SAMPLE_CALL_GAS = 150e3;
|
||||||
uint256 constant internal ETH2DAI_SAMPLE_CALL_GAS = 250e3;
|
uint256 constant internal ETH2DAI_SAMPLE_CALL_GAS = 1000e3;
|
||||||
|
|
||||||
/// @dev Query batches of native orders and sample sell quotes on multiple DEXes at once.
|
/// @dev Query batches of native orders and sample sell quotes on multiple DEXes at once.
|
||||||
/// @param orders Batches of Native orders to query.
|
/// @param orders Batches of Native orders to query.
|
||||||
@@ -105,7 +105,6 @@ contract ERC20BridgeSampler is
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
||||||
/// @param orders Native orders to query.
|
/// @param orders Native orders to query.
|
||||||
/// @param orderSignatures Signatures for each respective order in `orders`.
|
/// @param orderSignatures Signatures for each respective order in `orders`.
|
||||||
@@ -346,6 +345,8 @@ contract ERC20BridgeSampler is
|
|||||||
uint256 rate = 0;
|
uint256 rate = 0;
|
||||||
if (didSucceed) {
|
if (didSucceed) {
|
||||||
rate = abi.decode(resultData, (uint256));
|
rate = abi.decode(resultData, (uint256));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
makerTokenAmounts[i] =
|
makerTokenAmounts[i] =
|
||||||
rate *
|
rate *
|
||||||
@@ -386,6 +387,8 @@ contract ERC20BridgeSampler is
|
|||||||
uint256 buyAmount = 0;
|
uint256 buyAmount = 0;
|
||||||
if (didSucceed) {
|
if (didSucceed) {
|
||||||
buyAmount = abi.decode(resultData, (uint256));
|
buyAmount = abi.decode(resultData, (uint256));
|
||||||
|
} else{
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
makerTokenAmounts[i] = buyAmount;
|
makerTokenAmounts[i] = buyAmount;
|
||||||
}
|
}
|
||||||
@@ -421,6 +424,8 @@ contract ERC20BridgeSampler is
|
|||||||
uint256 sellAmount = 0;
|
uint256 sellAmount = 0;
|
||||||
if (didSucceed) {
|
if (didSucceed) {
|
||||||
sellAmount = abi.decode(resultData, (uint256));
|
sellAmount = abi.decode(resultData, (uint256));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
takerTokenAmounts[i] = sellAmount;
|
takerTokenAmounts[i] = sellAmount;
|
||||||
}
|
}
|
||||||
@@ -449,26 +454,28 @@ contract ERC20BridgeSampler is
|
|||||||
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
|
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
|
||||||
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
bool didSucceed = true;
|
||||||
if (makerToken == _getWethAddress()) {
|
if (makerToken == _getWethAddress()) {
|
||||||
makerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(takerTokenExchange),
|
address(takerTokenExchange),
|
||||||
takerTokenExchange.getTokenToEthInputPrice.selector,
|
takerTokenExchange.getTokenToEthInputPrice.selector,
|
||||||
takerTokenAmounts[i]
|
takerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
} else if (takerToken == _getWethAddress()) {
|
} else if (takerToken == _getWethAddress()) {
|
||||||
makerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(makerTokenExchange),
|
address(makerTokenExchange),
|
||||||
makerTokenExchange.getEthToTokenInputPrice.selector,
|
makerTokenExchange.getEthToTokenInputPrice.selector,
|
||||||
takerTokenAmounts[i]
|
takerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
uint256 ethBought = _callUniswapExchangePriceFunction(
|
uint256 ethBought;
|
||||||
|
(ethBought, didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(takerTokenExchange),
|
address(takerTokenExchange),
|
||||||
takerTokenExchange.getTokenToEthInputPrice.selector,
|
takerTokenExchange.getTokenToEthInputPrice.selector,
|
||||||
takerTokenAmounts[i]
|
takerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
if (ethBought != 0) {
|
if (ethBought != 0) {
|
||||||
makerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(makerTokenExchange),
|
address(makerTokenExchange),
|
||||||
makerTokenExchange.getEthToTokenInputPrice.selector,
|
makerTokenExchange.getEthToTokenInputPrice.selector,
|
||||||
ethBought
|
ethBought
|
||||||
@@ -477,6 +484,9 @@ contract ERC20BridgeSampler is
|
|||||||
makerTokenAmounts[i] = 0;
|
makerTokenAmounts[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!didSucceed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,26 +513,28 @@ contract ERC20BridgeSampler is
|
|||||||
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
|
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
|
||||||
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
bool didSucceed = true;
|
||||||
if (makerToken == _getWethAddress()) {
|
if (makerToken == _getWethAddress()) {
|
||||||
takerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(takerTokenExchange),
|
address(takerTokenExchange),
|
||||||
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
||||||
makerTokenAmounts[i]
|
makerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
} else if (takerToken == _getWethAddress()) {
|
} else if (takerToken == _getWethAddress()) {
|
||||||
takerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(makerTokenExchange),
|
address(makerTokenExchange),
|
||||||
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
||||||
makerTokenAmounts[i]
|
makerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
uint256 ethSold = _callUniswapExchangePriceFunction(
|
uint256 ethSold;
|
||||||
|
(ethSold, didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(makerTokenExchange),
|
address(makerTokenExchange),
|
||||||
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
||||||
makerTokenAmounts[i]
|
makerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
if (ethSold != 0) {
|
if (ethSold != 0) {
|
||||||
takerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(takerTokenExchange),
|
address(takerTokenExchange),
|
||||||
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
||||||
ethSold
|
ethSold
|
||||||
@@ -531,6 +543,9 @@ contract ERC20BridgeSampler is
|
|||||||
takerTokenAmounts[i] = 0;
|
takerTokenAmounts[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!didSucceed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -558,12 +573,13 @@ contract ERC20BridgeSampler is
|
|||||||
)
|
)
|
||||||
private
|
private
|
||||||
view
|
view
|
||||||
returns (uint256 outputAmount)
|
returns (uint256 outputAmount, bool didSucceed)
|
||||||
{
|
{
|
||||||
if (uniswapExchangeAddress == address(0)) {
|
if (uniswapExchangeAddress == address(0)) {
|
||||||
return 0;
|
return (outputAmount, didSucceed);
|
||||||
}
|
}
|
||||||
(bool didSucceed, bytes memory resultData) =
|
bytes memory resultData;
|
||||||
|
(didSucceed, resultData) =
|
||||||
uniswapExchangeAddress.staticcall.gas(UNISWAP_SAMPLE_CALL_GAS)(
|
uniswapExchangeAddress.staticcall.gas(UNISWAP_SAMPLE_CALL_GAS)(
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
functionSelector,
|
functionSelector,
|
||||||
|
@@ -338,7 +338,11 @@ contract TestERC20BridgeSampler is
|
|||||||
bytes32 orderHash = keccak256(abi.encode(order.salt));
|
bytes32 orderHash = keccak256(abi.encode(order.salt));
|
||||||
// Everything else is derived from the hash.
|
// Everything else is derived from the hash.
|
||||||
orderInfo.orderHash = orderHash;
|
orderInfo.orderHash = orderHash;
|
||||||
orderInfo.orderStatus = LibOrder.OrderStatus(uint256(orderHash) % MAX_ORDER_STATUS);
|
if (uint256(orderHash) % 100 > 90) {
|
||||||
|
orderInfo.orderStatus = LibOrder.OrderStatus.FULLY_FILLED;
|
||||||
|
} else {
|
||||||
|
orderInfo.orderStatus = LibOrder.OrderStatus.FILLABLE;
|
||||||
|
}
|
||||||
orderInfo.orderTakerAssetFilledAmount = uint256(orderHash) % order.takerAssetAmount;
|
orderInfo.orderTakerAssetFilledAmount = uint256(orderHash) % order.takerAssetAmount;
|
||||||
fillableTakerAssetAmount =
|
fillableTakerAssetAmount =
|
||||||
order.takerAssetAmount - orderInfo.orderTakerAssetFilledAmount;
|
order.takerAssetAmount - orderInfo.orderTakerAssetFilledAmount;
|
||||||
|
@@ -195,7 +195,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
|
|
||||||
function getDeterministicFillableTakerAssetAmount(order: Order): BigNumber {
|
function getDeterministicFillableTakerAssetAmount(order: Order): BigNumber {
|
||||||
const hash = getPackedHash(hexUtils.toHex(order.salt, 32));
|
const hash = getPackedHash(hexUtils.toHex(order.salt, 32));
|
||||||
const orderStatus = new BigNumber(hash).mod(255).toNumber();
|
const orderStatus = new BigNumber(hash).mod(100).toNumber() > 90 ? 5 : 3;
|
||||||
const isValidSignature = !!new BigNumber(hash).mod(2).toNumber();
|
const isValidSignature = !!new BigNumber(hash).mod(2).toNumber();
|
||||||
if (orderStatus !== 3 || !isValidSignature) {
|
if (orderStatus !== 3 || !isValidSignature) {
|
||||||
return constants.ZERO_AMOUNT;
|
return constants.ZERO_AMOUNT;
|
||||||
@@ -208,7 +208,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
return order.makerAssetAmount
|
return order.makerAssetAmount
|
||||||
.times(takerAmount)
|
.times(takerAmount)
|
||||||
.div(order.takerAssetAmount)
|
.div(order.takerAssetAmount)
|
||||||
.integerValue(BigNumber.ROUND_DOWN);
|
.integerValue(BigNumber.ROUND_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getERC20AssetData(tokenAddress: string): string {
|
function getERC20AssetData(tokenAddress: string): string {
|
||||||
@@ -255,7 +255,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
describe('getOrderFillableTakerAssetAmounts()', () => {
|
describe('getOrderFillableTakerAssetAmounts()', () => {
|
||||||
it('returns the expected amount for each order', async () => {
|
it('returns the expected amount for each order', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
|
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
|
||||||
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq(expected);
|
expect(actual).to.deep.eq(expected);
|
||||||
@@ -269,7 +269,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('returns zero for an order with zero maker asset amount', async () => {
|
it('returns zero for an order with zero maker asset amount', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
|
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
});
|
});
|
||||||
@@ -277,7 +277,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('returns zero for an order with zero taker asset amount', async () => {
|
it('returns zero for an order with zero taker asset amount', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
|
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
});
|
});
|
||||||
@@ -293,7 +293,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
describe('getOrderFillableMakerAssetAmounts()', () => {
|
describe('getOrderFillableMakerAssetAmounts()', () => {
|
||||||
it('returns the expected amount for each order', async () => {
|
it('returns the expected amount for each order', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const expected = orders.map(getDeterministicFillableMakerAssetAmount);
|
const expected = orders.map(getDeterministicFillableMakerAssetAmount);
|
||||||
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq(expected);
|
expect(actual).to.deep.eq(expected);
|
||||||
@@ -307,7 +307,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('returns zero for an order with zero maker asset amount', async () => {
|
it('returns zero for an order with zero maker asset amount', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
|
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
});
|
});
|
||||||
@@ -315,7 +315,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('returns zero for an order with zero taker asset amount', async () => {
|
it('returns zero for an order with zero taker asset amount', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
|
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
});
|
});
|
||||||
@@ -330,7 +330,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
|
|
||||||
describe('queryOrdersAndSampleSells()', () => {
|
describe('queryOrdersAndSampleSells()', () => {
|
||||||
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
const SIGNATURES: string[] = _.times(ORDERS.length, hexUtils.random);
|
const SIGNATURES: string[] = _.times(ORDERS.length, i => hexUtils.random());
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
@@ -436,7 +436,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
|
|
||||||
describe('queryOrdersAndSampleBuys()', () => {
|
describe('queryOrdersAndSampleBuys()', () => {
|
||||||
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
const SIGNATURES: string[] = _.times(ORDERS.length, hexUtils.random);
|
const SIGNATURES: string[] = _.times(ORDERS.length, i => hexUtils.random());
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
|
Reference in New Issue
Block a user