Add aggregator mainnet tests (#2407)
* `@0x/contracts-erc20-bridge-sampler`: Add gas limits to external quote calls. `@0x/contract-addresses`: Point `erc20BridgeSampler` to new version. * `@0x/contracts-utils`: Add kovan addresses to `DeploymentConstants`. `@0x/contract-addresses`: Add kovan `ERC20BridgeSampler` address. * `@0x/contracts-erc20-bridge-sampler`: Fix changelog. * `@0x/asset-swapper`: Ignore zero sample results from the sampler contract. `@0x/asset-swapper`: Allow skipping Uniswap when dealing with low precision amounts with `minUniswapDecimals` option. `@0x/asset-swapper`: Increase default `runLimit` from `1024` to `4096`. `@0x/asset-swapper`: Increase default `numSamples` from `8` to `10` `@0x/asset-swapper`: Fix ordering of optimized orders. `@0x/asset-swapper`: Fix best and worst quotes being reversed sometimes. `@0x/asset-swapper`: Fix rounding of quoted asset amounts. * `@0x/asset-swapper`: Change default `minUniswapDecimals` option from 8 to 7. * `@0x/asset-swapper`: Revert uniswap decimals fix. * `@0x/contracts-test-utils`: Add `blockchainTests.live()` for live network tests. `@0x/contracts-test-utils`: Add modifiers to `blockchainTests.fork()`. `@0x/contracts-integrations`: Add aggregator mainnet tests. * `@0x/contracts-integrations`: Fix `fork/resets` modifier ordering on dydx tests. `@0x/contracts-integrations`: Move and tweak aggregation tests. * `@0x/contracts-integrations`: Handle non-responsive third-party SRA ordebooks with a little more grace. * `@0x/contracts-integrations`: Fix linter error. * `@0x/contracts-test-utils`: Consolidate fork provider logic into `mocha_blockchain.ts`. * `@0x/contracts-integrations`: Run prettier on aggregation fill tests. * `@0x/dev-utils`: Add `locked` to `Web3Config`. * `@0x/contracts-integrations`: Update mainnet fork tests. `@0x/contracts-test-utils`: Fix forked tests being skipped. `@0x/contracts-erc20-bridge-sampler`: Regenerate artifacts. * `@0x/contracts-test-utils`: Remove unecessary `locked` option when creating forked ganache provider. * Fix redundant zero check * Set fee amount in fillable amounts test Co-authored-by: Jacob Evans <dekz@dekz.net>
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
|
||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||
|
||||
|
||||
/// @dev A forwarder contract for filling 0x asset-swapper aggregated orders.
|
||||
/// The forwarder is necessary to purchase taker assets and set up
|
||||
/// approvals in one transaction. Only call the functions on this contract
|
||||
/// in an `eth_call` context or you will lose money!
|
||||
contract TestMainnetAggregatorFills is
|
||||
DeploymentConstants
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
address constant internal EXCHANGE_ADDRESS = 0x61935CbDd02287B511119DDb11Aeb42F1593b7Ef;
|
||||
bytes4 constant internal ERC20_PROXY_ID = 0xf47261b0; // bytes4(keccak256("ERC20Token(address)"));
|
||||
|
||||
struct SimulatedMarketFillResults {
|
||||
uint256 makerAssetBalanceBefore;
|
||||
uint256 takerAssetBalanceBefore;
|
||||
uint256 makerAssetBalanceAfter;
|
||||
uint256 takerAssetBalanceAfter;
|
||||
LibFillResults.FillResults fillResults;
|
||||
}
|
||||
|
||||
// solhint-disable-next-line no-empty-blocks
|
||||
function() external payable {}
|
||||
|
||||
/// @dev Buy taker assets with ETH from `takerOrders` and then perform a
|
||||
/// market buy on `makerOrders`.
|
||||
function marketBuy(
|
||||
address makerTokenAddress,
|
||||
address takerTokenAddress,
|
||||
LibOrder.Order[] memory makerOrders,
|
||||
LibOrder.Order[] memory takerOrders,
|
||||
bytes[] memory makerOrderSignatures,
|
||||
bytes[] memory takerOrderSignatures,
|
||||
uint256 makerAssetBuyAmount
|
||||
)
|
||||
public
|
||||
payable
|
||||
returns (SimulatedMarketFillResults memory results)
|
||||
{
|
||||
_prepareFunds(takerTokenAddress, makerOrders, takerOrders, takerOrderSignatures);
|
||||
results.makerAssetBalanceBefore = IERC20Token(makerTokenAddress).balanceOf(address(this));
|
||||
results.takerAssetBalanceBefore = IERC20Token(takerTokenAddress).balanceOf(address(this));
|
||||
results.fillResults = IExchange(EXCHANGE_ADDRESS)
|
||||
.marketBuyOrdersNoThrow
|
||||
.value(address(this).balance)(
|
||||
makerOrders,
|
||||
makerAssetBuyAmount,
|
||||
makerOrderSignatures
|
||||
);
|
||||
results.makerAssetBalanceAfter = IERC20Token(makerTokenAddress).balanceOf(address(this));
|
||||
results.takerAssetBalanceAfter = IERC20Token(takerTokenAddress).balanceOf(address(this));
|
||||
}
|
||||
|
||||
/// @dev Buy taker assets with ETH from `takerOrders` and then perform a
|
||||
/// market sell on `makerOrders`.
|
||||
function marketSell(
|
||||
address makerTokenAddress,
|
||||
address takerTokenAddress,
|
||||
LibOrder.Order[] memory makerOrders,
|
||||
LibOrder.Order[] memory takerOrders,
|
||||
bytes[] memory makerOrderSignatures,
|
||||
bytes[] memory takerOrderSignatures,
|
||||
uint256 takerAssetSellAmount
|
||||
)
|
||||
public
|
||||
payable
|
||||
returns (SimulatedMarketFillResults memory results)
|
||||
{
|
||||
_prepareFunds(takerTokenAddress, makerOrders, takerOrders, takerOrderSignatures);
|
||||
results.makerAssetBalanceBefore = IERC20Token(makerTokenAddress).balanceOf(address(this));
|
||||
results.takerAssetBalanceBefore = IERC20Token(takerTokenAddress).balanceOf(address(this));
|
||||
results.fillResults = IExchange(EXCHANGE_ADDRESS)
|
||||
.marketSellOrdersNoThrow
|
||||
.value(address(this).balance)(
|
||||
makerOrders,
|
||||
takerAssetSellAmount,
|
||||
makerOrderSignatures
|
||||
);
|
||||
results.makerAssetBalanceAfter = IERC20Token(makerTokenAddress).balanceOf(address(this));
|
||||
results.takerAssetBalanceAfter = IERC20Token(takerTokenAddress).balanceOf(address(this));
|
||||
}
|
||||
|
||||
/// @dev Like `marketSell`, but calls `fillOrder()` individually to detect
|
||||
/// errors.
|
||||
function fillOrders(
|
||||
address makerTokenAddress,
|
||||
address takerTokenAddress,
|
||||
LibOrder.Order[] memory makerOrders,
|
||||
LibOrder.Order[] memory takerOrders,
|
||||
bytes[] memory makerOrderSignatures,
|
||||
bytes[] memory takerOrderSignatures,
|
||||
uint256 takerAssetSellAmount
|
||||
)
|
||||
public
|
||||
payable
|
||||
returns (SimulatedMarketFillResults memory results)
|
||||
{
|
||||
_prepareFunds(takerTokenAddress, makerOrders, takerOrders, takerOrderSignatures);
|
||||
results.makerAssetBalanceBefore = IERC20Token(makerTokenAddress).balanceOf(address(this));
|
||||
results.takerAssetBalanceBefore = IERC20Token(takerTokenAddress).balanceOf(address(this));
|
||||
for (uint256 i = 0; i < makerOrders.length; i++) {
|
||||
if (takerAssetSellAmount == 0) {
|
||||
break;
|
||||
}
|
||||
LibFillResults.FillResults memory fillResults = IExchange(EXCHANGE_ADDRESS)
|
||||
.fillOrder
|
||||
.value(address(this).balance)(
|
||||
makerOrders[i],
|
||||
takerAssetSellAmount,
|
||||
makerOrderSignatures[i]
|
||||
);
|
||||
results.fillResults = LibFillResults.addFillResults(results.fillResults, fillResults);
|
||||
takerAssetSellAmount = takerAssetSellAmount.safeSub(fillResults.takerAssetFilledAmount);
|
||||
}
|
||||
results.makerAssetBalanceAfter = IERC20Token(makerTokenAddress).balanceOf(address(this));
|
||||
results.takerAssetBalanceAfter = IERC20Token(takerTokenAddress).balanceOf(address(this));
|
||||
}
|
||||
|
||||
function _approveAssetProxy(address tokenAddress) private {
|
||||
address assetProxyAddress = IExchange(EXCHANGE_ADDRESS).getAssetProxy(ERC20_PROXY_ID);
|
||||
LibERC20Token.approve(tokenAddress, assetProxyAddress, uint256(-1));
|
||||
}
|
||||
|
||||
/// @dev Buys as much of `takerOrders` as possible with the ETH transferred
|
||||
/// to this contract, leaving enough ETH behind for protocol fees.
|
||||
function _prepareFunds(
|
||||
address takerTokenAddress,
|
||||
LibOrder.Order[] memory makerOrders,
|
||||
LibOrder.Order[] memory takerOrders,
|
||||
bytes[] memory takerOrderSignatures
|
||||
)
|
||||
private
|
||||
{
|
||||
_approveAssetProxy(_getWethAddress());
|
||||
uint256 protocolFee = IExchange(EXCHANGE_ADDRESS).protocolFeeMultiplier() * tx.gasprice;
|
||||
uint256 maxProtocolFees = protocolFee * (takerOrders.length + makerOrders.length);
|
||||
uint256 ethSellAmount = msg.value.safeSub(maxProtocolFees);
|
||||
IEtherToken(_getWethAddress()).deposit.value(ethSellAmount)();
|
||||
if (takerTokenAddress != _getWethAddress()) {
|
||||
IExchange(EXCHANGE_ADDRESS)
|
||||
.marketSellOrdersNoThrow
|
||||
.value(maxProtocolFees)(
|
||||
takerOrders,
|
||||
ethSellAmount,
|
||||
takerOrderSignatures
|
||||
);
|
||||
_approveAssetProxy(takerTokenAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user