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:
parent
0571a96cea
commit
ff2cc8c887
@ -5,6 +5,10 @@
|
||||
{
|
||||
"note": "Integration tests for DydxBridge with (i) Exchange v3 and (ii) Mainnet dYdX SoloMargin contract.",
|
||||
"pr": 2401
|
||||
},
|
||||
{
|
||||
"note": "Add aggregator mainnet tests.",
|
||||
"pr": 2407
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@
|
||||
},
|
||||
"config": {
|
||||
"publicInterfaceContracts": "TestFramework",
|
||||
"abis": "./test/generated-artifacts/@(TestDydxUser|TestEth2Dai|TestEth2DaiBridge|TestFramework|TestUniswapBridge|TestUniswapExchange|TestUniswapExchangeFactory).json",
|
||||
"abis": "./test/generated-artifacts/@(TestDydxUser|TestEth2Dai|TestEth2DaiBridge|TestFramework|TestMainnetAggregatorFills|TestUniswapBridge|TestUniswapExchange|TestUniswapExchangeFactory).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
@ -89,6 +89,7 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/asset-swapper": "^3.0.1",
|
||||
"@0x/base-contract": "^6.0.2",
|
||||
"@0x/contracts-asset-proxy": "^3.0.2",
|
||||
"@0x/contracts-erc1155": "^2.0.2",
|
||||
|
239
contracts/integrations/test/aggregation/fill_test.ts
Normal file
239
contracts/integrations/test/aggregation/fill_test.ts
Normal file
@ -0,0 +1,239 @@
|
||||
import { MarketBuySwapQuote, MarketSellSwapQuote, Orderbook, SwapQuoter } from '@0x/asset-swapper';
|
||||
import { blockchainTests, expect, Numberish } from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { FillResults, SignedOrder } from '@0x/types';
|
||||
import { BigNumber, logUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { TestMainnetAggregatorFillsContract } from '../wrappers';
|
||||
|
||||
import { tokens } from './tokens';
|
||||
|
||||
blockchainTests.live('Aggregator Mainnet Tests', env => {
|
||||
// Mainnet address of the `TestMainnetAggregatorFills` contract.
|
||||
const TEST_CONTRACT_ADDRESS = '0x37Ca306F42748b7fe105F89FCBb2CD03D27c8146';
|
||||
const TAKER_ADDRESS = '0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B'; // Vitalik
|
||||
const ORDERBOOK_POLLING_MS = 1000;
|
||||
const GAS_PRICE = new BigNumber(1);
|
||||
const TAKER_ASSET_ETH_VALUE = 500e18;
|
||||
const MIN_BALANCE = 500.1e18;
|
||||
const SYMBOLS = ['ETH', 'DAI', 'USDC', 'FOAM'];
|
||||
const TEST_PAIRS = _.flatten(SYMBOLS.map(m => SYMBOLS.filter(t => t !== m).map(t => [m, t])));
|
||||
const FILL_VALUES = [1, 10, 1e2, 1e3, 1e4, 2.5e4, 5e4];
|
||||
|
||||
let testContract: TestMainnetAggregatorFillsContract;
|
||||
let swapQuoter: SwapQuoter;
|
||||
let takerEthBalance: BigNumber;
|
||||
const orderbooks: { [name: string]: Orderbook } = {};
|
||||
|
||||
async function getTakerOrdersAsync(takerAssetSymbol: string): Promise<SignedOrder[]> {
|
||||
if (takerAssetSymbol === 'ETH') {
|
||||
return [];
|
||||
}
|
||||
return getOrdersAsync(takerAssetSymbol, 'ETH');
|
||||
}
|
||||
|
||||
// Fetches ETH -> taker asset orders for the forwarder contract.
|
||||
async function getOrdersAsync(makerAssetSymbol: string, takerAssetSymbol: string): Promise<SignedOrder[]> {
|
||||
const takerTokenAddress = tokens[takerAssetSymbol].address;
|
||||
const makerTokenAddress = tokens[makerAssetSymbol].address;
|
||||
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerTokenAddress);
|
||||
const takerAssetData = assetDataUtils.encodeERC20AssetData(takerTokenAddress);
|
||||
const orders = _.flatten(
|
||||
await Promise.all(
|
||||
Object.keys(orderbooks).map(async name =>
|
||||
getOrdersFromOrderBookAsync(name, makerAssetData, takerAssetData),
|
||||
),
|
||||
),
|
||||
);
|
||||
const uniqueOrders: SignedOrder[] = [];
|
||||
for (const order of orders) {
|
||||
if (!order.makerFee.eq(0) || !order.takerFee.eq(0)) {
|
||||
continue;
|
||||
}
|
||||
if (uniqueOrders.findIndex(o => isSameOrder(order, o)) === -1) {
|
||||
uniqueOrders.push(order);
|
||||
}
|
||||
}
|
||||
return uniqueOrders;
|
||||
}
|
||||
|
||||
async function getOrdersFromOrderBookAsync(
|
||||
name: string,
|
||||
makerAssetData: string,
|
||||
takerAssetData: string,
|
||||
): Promise<SignedOrder[]> {
|
||||
try {
|
||||
return (await orderbooks[name].getOrdersAsync(makerAssetData, takerAssetData)).map(r => r.order);
|
||||
} catch (err) {
|
||||
logUtils.warn(`Failed to retrieve orders from orderbook "${name}".`);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function isSameOrder(a: SignedOrder, b: SignedOrder): boolean {
|
||||
for (const [k, v] of Object.entries(a)) {
|
||||
if (k in (b as any)) {
|
||||
if (BigNumber.isBigNumber(v) && !v.eq((b as any)[k])) {
|
||||
return false;
|
||||
}
|
||||
if (v !== (b as any)[k]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function toTokenUnits(symbol: string, weis: Numberish): BigNumber {
|
||||
return new BigNumber(weis).div(new BigNumber(10).pow(tokens[symbol].decimals));
|
||||
}
|
||||
|
||||
function fromTokenUnits(symbol: string, units: Numberish): BigNumber {
|
||||
return new BigNumber(units)
|
||||
.times(new BigNumber(10).pow(tokens[symbol].decimals))
|
||||
.integerValue(BigNumber.ROUND_DOWN);
|
||||
}
|
||||
|
||||
interface MarketOperationResult {
|
||||
makerAssetBalanceBefore: BigNumber;
|
||||
takerAssetBalanceBefore: BigNumber;
|
||||
makerAssetBalanceAfter: BigNumber;
|
||||
takerAssetBalanceAfter: BigNumber;
|
||||
fillResults: FillResults;
|
||||
}
|
||||
|
||||
// Liquidity is low right now so it's possible we didn't have
|
||||
// enough taker assets to cover the orders, so occasionally we'll get incomplete
|
||||
// fills. This function will catch those cases.
|
||||
// TODO(dorothy-zbornak): Remove this special case when liquidity is up.
|
||||
function checkHadEnoughTakerAsset(
|
||||
quote: MarketBuySwapQuote | MarketSellSwapQuote,
|
||||
result: MarketOperationResult,
|
||||
): boolean {
|
||||
if (result.takerAssetBalanceBefore.gte(quote.worstCaseQuoteInfo.takerAssetAmount)) {
|
||||
return true;
|
||||
}
|
||||
const takerAssetPct = result.takerAssetBalanceBefore
|
||||
.div(quote.worstCaseQuoteInfo.takerAssetAmount)
|
||||
.times(100)
|
||||
.toNumber()
|
||||
.toFixed(1);
|
||||
logUtils.warn(`Could not acquire enough taker asset to complete the fill: ${takerAssetPct}%`);
|
||||
expect(result.fillResults.makerAssetFilledAmount).to.bignumber.lt(quote.worstCaseQuoteInfo.makerAssetAmount);
|
||||
return false;
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
testContract = new TestMainnetAggregatorFillsContract(TEST_CONTRACT_ADDRESS, env.provider, {
|
||||
...env.txDefaults,
|
||||
gasPrice: GAS_PRICE,
|
||||
gas: 10e6,
|
||||
});
|
||||
swapQuoter = SwapQuoter.getSwapQuoterForStandardRelayerAPIUrl(env.provider, 'https://api.0x.org/sra');
|
||||
// Pool orderbooks because we're desperate for liquidity.
|
||||
orderbooks.swapQuoter = swapQuoter.orderbook;
|
||||
orderbooks.bamboo = Orderbook.getOrderbookForPollingProvider({
|
||||
httpEndpoint: 'https://sra.bamboorelay.com/0x/v3',
|
||||
pollingIntervalMs: ORDERBOOK_POLLING_MS,
|
||||
});
|
||||
// TODO(dorothy-zbornak): Uncomment when radar's SRA is up.
|
||||
// orderbooks.radar = Orderbook.getOrderbookForPollingProvider({
|
||||
// httpEndpoint: 'https://api-v3.radarrelay.com/v3',
|
||||
// pollingIntervalMs: ORDERBOOK_POLLING_MS,
|
||||
// });
|
||||
takerEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(TAKER_ADDRESS);
|
||||
});
|
||||
|
||||
it('taker has minimum ETH', async () => {
|
||||
expect(takerEthBalance).to.bignumber.gte(MIN_BALANCE);
|
||||
});
|
||||
|
||||
describe('market sells', () => {
|
||||
for (const [makerSymbol, takerSymbol] of TEST_PAIRS) {
|
||||
for (const fillValue of FILL_VALUES) {
|
||||
const fillAmount = fromTokenUnits(takerSymbol, new BigNumber(fillValue).div(tokens[takerSymbol].price));
|
||||
it(`sell ${toTokenUnits(takerSymbol, fillAmount)} ${takerSymbol} for ${makerSymbol}`, async () => {
|
||||
const [quote, takerOrders] = await Promise.all([
|
||||
swapQuoter.getMarketSellSwapQuoteAsync(
|
||||
tokens[makerSymbol].address,
|
||||
tokens[takerSymbol].address,
|
||||
fillAmount,
|
||||
{ gasPrice: GAS_PRICE },
|
||||
),
|
||||
getTakerOrdersAsync(takerSymbol),
|
||||
]);
|
||||
// Buy taker assets from `takerOrders` and and perform a
|
||||
// market sell on the bridge orders.
|
||||
const fill = await testContract
|
||||
.marketSell(
|
||||
tokens[makerSymbol].address,
|
||||
tokens[takerSymbol].address,
|
||||
quote.orders,
|
||||
takerOrders,
|
||||
quote.orders.map(o => o.signature),
|
||||
takerOrders.map(o => o.signature),
|
||||
quote.takerAssetFillAmount,
|
||||
)
|
||||
.callAsync({
|
||||
value: quote.worstCaseQuoteInfo.protocolFeeInWeiAmount.plus(TAKER_ASSET_ETH_VALUE),
|
||||
from: TAKER_ADDRESS,
|
||||
gasPrice: quote.gasPrice,
|
||||
});
|
||||
if (checkHadEnoughTakerAsset(quote, fill)) {
|
||||
expect(fill.fillResults.makerAssetFilledAmount, 'makerAssetFilledAmount').to.bignumber.gte(
|
||||
quote.worstCaseQuoteInfo.makerAssetAmount,
|
||||
);
|
||||
expect(fill.fillResults.takerAssetFilledAmount, 'takerAssetFilledAmount').to.bignumber.lte(
|
||||
quote.takerAssetFillAmount,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
describe('market buys', () => {
|
||||
for (const [makerSymbol, takerSymbol] of TEST_PAIRS) {
|
||||
for (const fillValue of FILL_VALUES) {
|
||||
const fillAmount = fromTokenUnits(makerSymbol, new BigNumber(fillValue).div(tokens[makerSymbol].price));
|
||||
it(`buy ${toTokenUnits(makerSymbol, fillAmount)} ${makerSymbol} with ${takerSymbol}`, async () => {
|
||||
const [quote, takerOrders] = await Promise.all([
|
||||
swapQuoter.getMarketBuySwapQuoteAsync(
|
||||
tokens[makerSymbol].address,
|
||||
tokens[takerSymbol].address,
|
||||
fillAmount,
|
||||
{ gasPrice: GAS_PRICE },
|
||||
),
|
||||
getTakerOrdersAsync(takerSymbol),
|
||||
]);
|
||||
// Buy taker assets from `takerOrders` and and perform a
|
||||
// market buy on the bridge orders.
|
||||
const fill = await testContract
|
||||
.marketBuy(
|
||||
tokens[makerSymbol].address,
|
||||
tokens[takerSymbol].address,
|
||||
quote.orders,
|
||||
takerOrders,
|
||||
quote.orders.map(o => o.signature),
|
||||
takerOrders.map(o => o.signature),
|
||||
quote.makerAssetFillAmount,
|
||||
)
|
||||
.callAsync({
|
||||
value: quote.worstCaseQuoteInfo.protocolFeeInWeiAmount.plus(TAKER_ASSET_ETH_VALUE),
|
||||
from: TAKER_ADDRESS,
|
||||
gasPrice: quote.gasPrice,
|
||||
});
|
||||
if (checkHadEnoughTakerAsset(quote, fill)) {
|
||||
expect(fill.fillResults.takerAssetFilledAmount, 'takerAssetFilledAmount').to.bignumber.lte(
|
||||
quote.worstCaseQuoteInfo.takerAssetAmount,
|
||||
);
|
||||
expect(fill.fillResults.makerAssetFilledAmount, 'makerAssetFilledAmount').to.bignumber.gte(
|
||||
quote.makerAssetFillAmount,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
77
contracts/integrations/test/aggregation/tokens.ts
Normal file
77
contracts/integrations/test/aggregation/tokens.ts
Normal file
@ -0,0 +1,77 @@
|
||||
export const tokens: { [symbol: string]: { address: string; decimals: number; price: number } } = {
|
||||
ETH: {
|
||||
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
|
||||
decimals: 18,
|
||||
price: 133,
|
||||
},
|
||||
SAI: {
|
||||
address: '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359',
|
||||
decimals: 18,
|
||||
price: 1,
|
||||
},
|
||||
DAI: {
|
||||
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
|
||||
decimals: 18,
|
||||
price: 1,
|
||||
},
|
||||
USDC: {
|
||||
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
||||
decimals: 6,
|
||||
price: 1,
|
||||
},
|
||||
WBTC: {
|
||||
address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
|
||||
decimals: 8,
|
||||
price: 6900,
|
||||
},
|
||||
MKR: {
|
||||
address: '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2',
|
||||
decimals: 18,
|
||||
price: 454,
|
||||
},
|
||||
BAT: {
|
||||
address: '0x0D8775F648430679A709E98d2b0Cb6250d2887EF',
|
||||
decimals: 18,
|
||||
price: 0.17,
|
||||
},
|
||||
OMG: {
|
||||
address: '0xd26114cd6EE289AccF82350c8d8487fedB8A0C07',
|
||||
decimals: 18,
|
||||
price: 0.65,
|
||||
},
|
||||
ZRX: {
|
||||
address: '0xE41d2489571d322189246DaFA5ebDe1F4699F498',
|
||||
decimals: 18,
|
||||
price: 0.19,
|
||||
},
|
||||
ZIL: {
|
||||
address: '0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27',
|
||||
decimals: 12,
|
||||
price: 0.004,
|
||||
},
|
||||
FOAM: {
|
||||
address: '0x4946Fcea7C692606e8908002e55A582af44AC121',
|
||||
decimals: 18,
|
||||
price: 0.004,
|
||||
},
|
||||
USDT: {
|
||||
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
||||
decimals: 6,
|
||||
price: 0.019,
|
||||
},
|
||||
REP: {
|
||||
address: '0x1985365e9f78359a9B6AD760e32412f4a445E862',
|
||||
decimals: 18,
|
||||
price: 8.9,
|
||||
},
|
||||
MANA: {
|
||||
address: '0x0F5D2fB29fb7d3CFeE444a200298f468908cC942',
|
||||
decimals: 18,
|
||||
price: 0.025,
|
||||
},
|
||||
LINK: {
|
||||
address: '0x514910771AF9Ca656af840dff83E8264EcF986CA',
|
||||
decimals: 18,
|
||||
price: 1.8,
|
||||
},
|
||||
};
|
@ -9,6 +9,7 @@ import * as TestDydxUser from '../test/generated-artifacts/TestDydxUser.json';
|
||||
import * as TestEth2Dai from '../test/generated-artifacts/TestEth2Dai.json';
|
||||
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json';
|
||||
import * as TestFramework from '../test/generated-artifacts/TestFramework.json';
|
||||
import * as TestMainnetAggregatorFills from '../test/generated-artifacts/TestMainnetAggregatorFills.json';
|
||||
import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
|
||||
import * as TestUniswapExchange from '../test/generated-artifacts/TestUniswapExchange.json';
|
||||
import * as TestUniswapExchangeFactory from '../test/generated-artifacts/TestUniswapExchangeFactory.json';
|
||||
@ -17,6 +18,7 @@ export const artifacts = {
|
||||
TestEth2Dai: TestEth2Dai as ContractArtifact,
|
||||
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
||||
TestFramework: TestFramework as ContractArtifact,
|
||||
TestMainnetAggregatorFills: TestMainnetAggregatorFills as ContractArtifact,
|
||||
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
|
||||
TestUniswapExchange: TestUniswapExchange as ContractArtifact,
|
||||
TestUniswapExchangeFactory: TestUniswapExchangeFactory as ContractArtifact,
|
||||
|
@ -14,7 +14,7 @@ import { contractAddresses, dydxAccountOwner } from '../mainnet_fork_utils';
|
||||
|
||||
import { dydxEvents } from './abi/dydxEvents';
|
||||
|
||||
blockchainTests.resets.fork('Mainnet dydx bridge tests', env => {
|
||||
blockchainTests.fork.resets('Mainnet dydx bridge tests', env => {
|
||||
let testContract: DydxBridgeContract;
|
||||
// random account to receive tokens from dydx
|
||||
const receiver = '0x986ccf5234d9cfbb25246f1a5bfa51f4ccfcb308';
|
||||
|
@ -1,12 +1,19 @@
|
||||
import { ContractWrappers } from '@0x/contract-wrappers';
|
||||
import { ERC20ProxyContract, MultiAssetProxyContract } from '@0x/contracts-asset-proxy';
|
||||
import { StakingProxyContract, ZrxVaultContract } from '@0x/contracts-staking';
|
||||
import { blockchainTests, describe, expect } from '@0x/contracts-test-utils';
|
||||
import { AssetProxyId } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { contractAddresses, contractWrappers } from './mainnet_fork_utils';
|
||||
import { contractAddresses, getContractwrappers } from './mainnet_fork_utils';
|
||||
|
||||
blockchainTests.fork.resets('Mainnet configs tests', env => {
|
||||
let contractWrappers: ContractWrappers;
|
||||
|
||||
before(async () => {
|
||||
contractWrappers = getContractwrappers(env.provider);
|
||||
});
|
||||
|
||||
blockchainTests.resets.fork('Mainnet configs tests', env => {
|
||||
describe('Exchange', () => {
|
||||
it('should be owned by the ZeroExGovernor ', async () => {
|
||||
const owner = await contractWrappers.exchange.owner().callAsync();
|
||||
|
@ -1,10 +1,14 @@
|
||||
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||
import { ContractWrappers } from '@0x/contract-wrappers';
|
||||
import { provider } from '@0x/contracts-test-utils';
|
||||
import { Web3ProviderEngine } from '@0x/dev-utils';
|
||||
|
||||
const chainId = 1;
|
||||
const contractAddresses = getContractAddressesForChainOrThrow(chainId);
|
||||
const contractWrappers = new ContractWrappers(provider, { chainId, contractAddresses });
|
||||
const dydxAccountOwner = '0xeb58c2caa96f39626dcceb74fdbb7a9a8b54ec18';
|
||||
export const dydxAccountOwner = '0xeb58c2caa96f39626dcceb74fdbb7a9a8b54ec18';
|
||||
export const contractAddresses = getContractAddressesForChainOrThrow(chainId);
|
||||
|
||||
export { contractAddresses, contractWrappers, dydxAccountOwner };
|
||||
/**
|
||||
* Create contract wrappers for the mainnet given a mainnet/forked provider.
|
||||
*/
|
||||
export function getContractwrappers(provider: Web3ProviderEngine): ContractWrappers {
|
||||
return new ContractWrappers(provider, { chainId, contractAddresses });
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ export * from '../test/generated-wrappers/test_dydx_user';
|
||||
export * from '../test/generated-wrappers/test_eth2_dai';
|
||||
export * from '../test/generated-wrappers/test_eth2_dai_bridge';
|
||||
export * from '../test/generated-wrappers/test_framework';
|
||||
export * from '../test/generated-wrappers/test_mainnet_aggregator_fills';
|
||||
export * from '../test/generated-wrappers/test_uniswap_bridge';
|
||||
export * from '../test/generated-wrappers/test_uniswap_exchange';
|
||||
export * from '../test/generated-wrappers/test_uniswap_exchange_factory';
|
||||
|
@ -8,6 +8,7 @@
|
||||
"test/generated-artifacts/TestEth2Dai.json",
|
||||
"test/generated-artifacts/TestEth2DaiBridge.json",
|
||||
"test/generated-artifacts/TestFramework.json",
|
||||
"test/generated-artifacts/TestMainnetAggregatorFills.json",
|
||||
"test/generated-artifacts/TestUniswapBridge.json",
|
||||
"test/generated-artifacts/TestUniswapExchange.json",
|
||||
"test/generated-artifacts/TestUniswapExchangeFactory.json"
|
||||
|
@ -5,6 +5,14 @@
|
||||
{
|
||||
"note": "Added ERC20BridgeProxy to list of unlocked accounts on mainnet fork",
|
||||
"pr": 2401
|
||||
},
|
||||
{
|
||||
"note": "Add `blockchainTests.live()` for live network tests.",
|
||||
"pr": 2407
|
||||
},
|
||||
{
|
||||
"note": "Add modifiers to `blockchainTests.fork()`.",
|
||||
"pr": 2407
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { Web3ProviderEngine } from '@0x/subproviders';
|
||||
import { BlockchainLifecycle, web3Factory } from '@0x/dev-utils';
|
||||
import { RPCSubprovider, Web3ProviderEngine } from '@0x/subproviders';
|
||||
import { providerUtils } from '@0x/utils';
|
||||
import { TxData, Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as _ from 'lodash';
|
||||
import * as mocha from 'mocha';
|
||||
import * as process from 'process';
|
||||
|
||||
import { provider, txDefaults, web3Wrapper } from './web3_wrapper';
|
||||
import { provider, providerConfigs, txDefaults, web3Wrapper } from './web3_wrapper';
|
||||
|
||||
// tslint:disable: no-namespace only-arrow-functions no-unbound-method
|
||||
// tslint:disable: no-namespace only-arrow-functions no-unbound-method max-classes-per-file
|
||||
|
||||
export type ISuite = mocha.ISuite;
|
||||
export type ISuiteCallbackContext = mocha.ISuiteCallbackContext;
|
||||
@ -18,22 +18,32 @@ export type BlockchainSuiteCallback = (this: ISuiteCallbackContext, env: Blockch
|
||||
export type BlockchainContextDefinitionCallback<T> = (description: string, callback: BlockchainSuiteCallback) => T;
|
||||
export interface ContextDefinition extends mocha.IContextDefinition {
|
||||
optional: ContextDefinitionCallback<ISuite | void>;
|
||||
fork: ContextDefinitionCallback<ISuite | void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for `blockchainTests()`.
|
||||
*/
|
||||
export interface BlockchainContextDefinition extends BlockchainContextDefinitionPartial {
|
||||
resets: BlockchainContextDefinitionPartial;
|
||||
}
|
||||
|
||||
interface BlockchainContextDefinitionPartial {
|
||||
export interface BlockchainContextDefinition {
|
||||
(description: string, callback: BlockchainSuiteCallback): ISuite;
|
||||
only: BlockchainContextDefinitionCallback<ISuite>;
|
||||
skip: BlockchainContextDefinitionCallback<void>;
|
||||
optional: BlockchainContextDefinitionCallback<ISuite | void>;
|
||||
fork: BlockchainContextDefinitionCallback<ISuite | void>;
|
||||
(description: string, callback: BlockchainSuiteCallback): ISuite;
|
||||
resets: BlockchainContextDefinitionCallback<ISuite | void> & {
|
||||
only: BlockchainContextDefinitionCallback<ISuite>;
|
||||
skip: BlockchainContextDefinitionCallback<void>;
|
||||
optional: BlockchainContextDefinitionCallback<ISuite | void>;
|
||||
};
|
||||
fork: BlockchainContextDefinitionCallback<ISuite | void> & {
|
||||
only: BlockchainContextDefinitionCallback<ISuite>;
|
||||
skip: BlockchainContextDefinitionCallback<void>;
|
||||
optional: BlockchainContextDefinitionCallback<ISuite | void>;
|
||||
resets: BlockchainContextDefinitionCallback<ISuite | void>;
|
||||
};
|
||||
live: BlockchainContextDefinitionCallback<ISuite | void> & {
|
||||
only: BlockchainContextDefinitionCallback<ISuite>;
|
||||
skip: BlockchainContextDefinitionCallback<void>;
|
||||
optional: BlockchainContextDefinitionCallback<ISuite | void>;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,29 +58,11 @@ export interface BlockchainTestsEnvironment {
|
||||
getAccountAddressesAsync(): Promise<string[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concret implementation of `BlockchainTestsEnvironment`.
|
||||
*/
|
||||
export class BlockchainTestsEnvironmentSingleton {
|
||||
private static _instance: BlockchainTestsEnvironmentSingleton | undefined;
|
||||
|
||||
public blockchainLifecycle: BlockchainLifecycle;
|
||||
public provider: Web3ProviderEngine;
|
||||
public txDefaults: Partial<TxData>;
|
||||
public web3Wrapper: Web3Wrapper;
|
||||
|
||||
// Create or retrieve the singleton instance of this class.
|
||||
public static create(): BlockchainTestsEnvironmentSingleton {
|
||||
if (BlockchainTestsEnvironmentSingleton._instance === undefined) {
|
||||
BlockchainTestsEnvironmentSingleton._instance = new BlockchainTestsEnvironmentSingleton();
|
||||
}
|
||||
return BlockchainTestsEnvironmentSingleton._instance;
|
||||
}
|
||||
|
||||
// Get the singleton instance of this class.
|
||||
public static getInstance(): BlockchainTestsEnvironmentSingleton | undefined {
|
||||
return BlockchainTestsEnvironmentSingleton._instance;
|
||||
}
|
||||
class BlockchainTestsEnvironmentBase {
|
||||
public blockchainLifecycle!: BlockchainLifecycle;
|
||||
public provider!: Web3ProviderEngine;
|
||||
public txDefaults!: Partial<TxData>;
|
||||
public web3Wrapper!: Web3Wrapper;
|
||||
|
||||
public async getChainIdAsync(): Promise<number> {
|
||||
return providerUtils.getChainIdAsync(this.provider);
|
||||
@ -79,8 +71,33 @@ export class BlockchainTestsEnvironmentSingleton {
|
||||
public async getAccountAddressesAsync(): Promise<string[]> {
|
||||
return this.web3Wrapper.getAvailableAddressesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
interface BlockchainEnvironmentFactory {
|
||||
create(): BlockchainTestsEnvironment;
|
||||
}
|
||||
|
||||
/**
|
||||
* `BlockchainTestsEnvironment` that uses the default ganache provider.
|
||||
*/
|
||||
export class StandardBlockchainTestsEnvironmentSingleton extends BlockchainTestsEnvironmentBase {
|
||||
private static _instance: StandardBlockchainTestsEnvironmentSingleton | undefined;
|
||||
|
||||
// Create or retrieve the singleton instance of this class.
|
||||
public static create(): StandardBlockchainTestsEnvironmentSingleton {
|
||||
if (StandardBlockchainTestsEnvironmentSingleton._instance === undefined) {
|
||||
StandardBlockchainTestsEnvironmentSingleton._instance = new StandardBlockchainTestsEnvironmentSingleton();
|
||||
}
|
||||
return StandardBlockchainTestsEnvironmentSingleton._instance;
|
||||
}
|
||||
|
||||
// Get the singleton instance of this class.
|
||||
public static getInstance(): StandardBlockchainTestsEnvironmentSingleton | undefined {
|
||||
return StandardBlockchainTestsEnvironmentSingleton._instance;
|
||||
}
|
||||
|
||||
protected constructor() {
|
||||
super();
|
||||
this.blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
this.provider = provider;
|
||||
this.txDefaults = txDefaults;
|
||||
@ -88,6 +105,89 @@ export class BlockchainTestsEnvironmentSingleton {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `BlockchainTestsEnvironment` that uses a forked ganache provider.
|
||||
*/
|
||||
export class ForkedBlockchainTestsEnvironmentSingleton extends BlockchainTestsEnvironmentBase {
|
||||
private static _instance: ForkedBlockchainTestsEnvironmentSingleton | undefined;
|
||||
|
||||
// Create or retrieve the singleton instance of this class.
|
||||
public static create(): ForkedBlockchainTestsEnvironmentSingleton {
|
||||
if (ForkedBlockchainTestsEnvironmentSingleton._instance === undefined) {
|
||||
ForkedBlockchainTestsEnvironmentSingleton._instance = new ForkedBlockchainTestsEnvironmentSingleton();
|
||||
}
|
||||
return ForkedBlockchainTestsEnvironmentSingleton._instance;
|
||||
}
|
||||
|
||||
protected static _createWeb3Provider(forkHost: string): Web3ProviderEngine {
|
||||
return web3Factory.getRpcProvider({
|
||||
...providerConfigs,
|
||||
fork: forkHost,
|
||||
blockTime: 0,
|
||||
});
|
||||
}
|
||||
|
||||
// Get the singleton instance of this class.
|
||||
public static getInstance(): ForkedBlockchainTestsEnvironmentSingleton | undefined {
|
||||
return ForkedBlockchainTestsEnvironmentSingleton._instance;
|
||||
}
|
||||
|
||||
protected constructor() {
|
||||
super();
|
||||
this.txDefaults = txDefaults;
|
||||
this.provider = process.env.FORK_RPC_URL
|
||||
? ForkedBlockchainTestsEnvironmentSingleton._createWeb3Provider(process.env.FORK_RPC_URL)
|
||||
: // Create a dummy provider if no RPC backend supplied.
|
||||
createDummyProvider();
|
||||
this.web3Wrapper = new Web3Wrapper(this.provider);
|
||||
this.blockchainLifecycle = new BlockchainLifecycle(this.web3Wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `BlockchainTestsEnvironment` that uses a live web3 provider.
|
||||
*/
|
||||
export class LiveBlockchainTestsEnvironmentSingleton extends BlockchainTestsEnvironmentBase {
|
||||
private static _instance: LiveBlockchainTestsEnvironmentSingleton | undefined;
|
||||
|
||||
// Create or retrieve the singleton instance of this class.
|
||||
public static create(): LiveBlockchainTestsEnvironmentSingleton {
|
||||
if (LiveBlockchainTestsEnvironmentSingleton._instance === undefined) {
|
||||
LiveBlockchainTestsEnvironmentSingleton._instance = new LiveBlockchainTestsEnvironmentSingleton();
|
||||
}
|
||||
return LiveBlockchainTestsEnvironmentSingleton._instance;
|
||||
}
|
||||
|
||||
protected static _createWeb3Provider(rpcHost: string): Web3ProviderEngine {
|
||||
const providerEngine = new Web3ProviderEngine();
|
||||
providerEngine.addProvider(new RPCSubprovider(rpcHost));
|
||||
providerUtils.startProviderEngine(providerEngine);
|
||||
return providerEngine;
|
||||
}
|
||||
|
||||
// Get the singleton instance of this class.
|
||||
public static getInstance(): LiveBlockchainTestsEnvironmentSingleton | undefined {
|
||||
return LiveBlockchainTestsEnvironmentSingleton._instance;
|
||||
}
|
||||
|
||||
protected constructor() {
|
||||
super();
|
||||
this.txDefaults = txDefaults;
|
||||
this.provider = process.env.LIVE_RPC_URL
|
||||
? LiveBlockchainTestsEnvironmentSingleton._createWeb3Provider(process.env.LIVE_RPC_URL)
|
||||
: // Create a dummy provider if no RPC backend supplied.
|
||||
createDummyProvider();
|
||||
this.web3Wrapper = new Web3Wrapper(this.provider);
|
||||
const snapshotHandlerAsync = async (): Promise<void> => {
|
||||
throw new Error('Snapshots are not supported with a live provider.');
|
||||
};
|
||||
this.blockchainLifecycle = {
|
||||
startAsync: snapshotHandlerAsync,
|
||||
revertAsync: snapshotHandlerAsync,
|
||||
} as any;
|
||||
}
|
||||
}
|
||||
|
||||
// The original `describe()` global provided by mocha.
|
||||
const mochaDescribe = (global as any).describe as mocha.IContextDefinition;
|
||||
|
||||
@ -99,77 +199,153 @@ export const describe = _.assign(mochaDescribe, {
|
||||
const describeCall = process.env.TEST_ALL ? mochaDescribe : mochaDescribe.skip;
|
||||
return describeCall(description, callback);
|
||||
},
|
||||
fork(description: string, callback: SuiteCallback): ISuite | void {
|
||||
const describeCall = process.env.FORK_RPC_URL ? mochaDescribe.only : mochaDescribe.skip;
|
||||
return describeCall(description, callback);
|
||||
},
|
||||
}) as ContextDefinition;
|
||||
|
||||
/**
|
||||
* Like mocha's `describe()`, but sets up a blockchain environment on first call.
|
||||
* Like mocha's `describe()`, but sets up a blockchain environment for you.
|
||||
*/
|
||||
export const blockchainTests: BlockchainContextDefinition = _.assign(
|
||||
function(description: string, callback: BlockchainSuiteCallback): ISuite {
|
||||
return defineBlockchainSuite(description, callback, describe);
|
||||
return defineBlockchainSuite(StandardBlockchainTestsEnvironmentSingleton, description, callback, describe);
|
||||
},
|
||||
{
|
||||
only(description: string, callback: BlockchainSuiteCallback): ISuite {
|
||||
return defineBlockchainSuite(description, callback, describe.only);
|
||||
},
|
||||
skip(description: string, callback: BlockchainSuiteCallback): void {
|
||||
return defineBlockchainSuite(description, callback, describe.skip);
|
||||
},
|
||||
optional(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(description, callback, process.env.TEST_ALL ? describe : describe.skip);
|
||||
},
|
||||
fork(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(
|
||||
StandardBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.FORK_RPC_URL ? describe.only : describe.skip,
|
||||
describe.only,
|
||||
);
|
||||
},
|
||||
skip(description: string, callback: BlockchainSuiteCallback): void {
|
||||
return defineBlockchainSuite(
|
||||
StandardBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
describe.skip,
|
||||
);
|
||||
},
|
||||
optional(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(
|
||||
StandardBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.TEST_ALL ? describe : describe.skip,
|
||||
);
|
||||
},
|
||||
fork: _.assign(
|
||||
function(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(
|
||||
ForkedBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.FORK_RPC_URL ? describe : describe.skip,
|
||||
);
|
||||
},
|
||||
{
|
||||
only(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(
|
||||
ForkedBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.FORK_RPC_URL ? describe.only : describe.skip,
|
||||
);
|
||||
},
|
||||
skip(description: string, callback: BlockchainSuiteCallback): void {
|
||||
return defineBlockchainSuite(
|
||||
ForkedBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
describe.skip,
|
||||
);
|
||||
},
|
||||
optional(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(
|
||||
ForkedBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.FORK_RPC_URL ? describe.optional : describe.skip,
|
||||
);
|
||||
},
|
||||
resets(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineResetsBlockchainSuite(
|
||||
ForkedBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.FORK_RPC_URL ? describe : describe.skip,
|
||||
);
|
||||
},
|
||||
},
|
||||
),
|
||||
live: _.assign(
|
||||
function(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(
|
||||
LiveBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.LIVE_RPC_URL ? describe : describe.skip,
|
||||
);
|
||||
},
|
||||
{
|
||||
only(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(
|
||||
LiveBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.LIVE_RPC_URL ? describe.only : describe.skip,
|
||||
);
|
||||
},
|
||||
skip(description: string, callback: BlockchainSuiteCallback): void {
|
||||
return defineBlockchainSuite(
|
||||
LiveBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
describe.skip,
|
||||
);
|
||||
},
|
||||
optional(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(
|
||||
LiveBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.LIVE_RPC_URL ? describe.optional : describe.skip,
|
||||
);
|
||||
},
|
||||
},
|
||||
),
|
||||
resets: _.assign(
|
||||
function(description: string, callback: BlockchainSuiteCallback): ISuite {
|
||||
return defineBlockchainSuite(description, callback, function(
|
||||
_description: string,
|
||||
_callback: SuiteCallback,
|
||||
): ISuite {
|
||||
return defineResetsSuite(_description, _callback, describe);
|
||||
});
|
||||
return defineResetsBlockchainSuite(
|
||||
StandardBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
describe,
|
||||
);
|
||||
},
|
||||
{
|
||||
only(description: string, callback: BlockchainSuiteCallback): ISuite {
|
||||
return defineBlockchainSuite(description, callback, function(
|
||||
_description: string,
|
||||
_callback: SuiteCallback,
|
||||
): ISuite {
|
||||
return defineResetsSuite(_description, _callback, describe.only);
|
||||
});
|
||||
return defineResetsBlockchainSuite(
|
||||
StandardBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
describe.only,
|
||||
);
|
||||
},
|
||||
skip(description: string, callback: BlockchainSuiteCallback): void {
|
||||
return defineBlockchainSuite(description, callback, function(
|
||||
_description: string,
|
||||
_callback: SuiteCallback,
|
||||
): void {
|
||||
return defineResetsSuite(_description, _callback, describe.skip);
|
||||
});
|
||||
return defineResetsBlockchainSuite(
|
||||
StandardBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
describe.skip,
|
||||
);
|
||||
},
|
||||
optional(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(description, callback, function(
|
||||
_description: string,
|
||||
_callback: SuiteCallback,
|
||||
): ISuite | void {
|
||||
return defineResetsSuite(_description, _callback, describe.optional);
|
||||
});
|
||||
},
|
||||
fork(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(description, callback, function(
|
||||
_description: string,
|
||||
_callback: SuiteCallback,
|
||||
): ISuite | void {
|
||||
return defineResetsSuite(_description, _callback, describe.fork);
|
||||
});
|
||||
return defineResetsBlockchainSuite(
|
||||
StandardBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
describe.optional,
|
||||
);
|
||||
},
|
||||
},
|
||||
),
|
||||
@ -177,29 +353,37 @@ export const blockchainTests: BlockchainContextDefinition = _.assign(
|
||||
) as BlockchainContextDefinition;
|
||||
|
||||
function defineBlockchainSuite<T>(
|
||||
envFactory: BlockchainEnvironmentFactory,
|
||||
description: string,
|
||||
callback: BlockchainSuiteCallback,
|
||||
describeCall: ContextDefinitionCallback<T>,
|
||||
): T {
|
||||
const env = BlockchainTestsEnvironmentSingleton.create();
|
||||
return describeCall(description, function(this: ISuiteCallbackContext): void {
|
||||
before(async () => env.blockchainLifecycle.startAsync());
|
||||
after(async () => env.blockchainLifecycle.revertAsync());
|
||||
callback.call(this, envFactory.create());
|
||||
});
|
||||
}
|
||||
|
||||
function defineResetsBlockchainSuite<T>(
|
||||
envFactory: BlockchainEnvironmentFactory,
|
||||
description: string,
|
||||
callback: BlockchainSuiteCallback,
|
||||
describeCall: ContextDefinitionCallback<T>,
|
||||
): T {
|
||||
return describeCall(description, function(this: ISuiteCallbackContext): void {
|
||||
const env = envFactory.create();
|
||||
beforeEach(async () => env.blockchainLifecycle.startAsync());
|
||||
afterEach(async () => env.blockchainLifecycle.revertAsync());
|
||||
callback.call(this, env);
|
||||
});
|
||||
}
|
||||
|
||||
function defineResetsSuite<T>(
|
||||
description: string,
|
||||
callback: SuiteCallback,
|
||||
describeCall: ContextDefinitionCallback<T>,
|
||||
): T {
|
||||
return describeCall(description, function(this: ISuiteCallbackContext): void {
|
||||
const env = BlockchainTestsEnvironmentSingleton.getInstance();
|
||||
if (env !== undefined) {
|
||||
beforeEach(async () => env.blockchainLifecycle.startAsync());
|
||||
afterEach(async () => env.blockchainLifecycle.revertAsync());
|
||||
}
|
||||
callback.call(this);
|
||||
});
|
||||
function createDummyProvider(): Web3ProviderEngine {
|
||||
return {
|
||||
addProvider: _.noop,
|
||||
on: _.noop,
|
||||
send: _.noop,
|
||||
sendAsync: _.noop,
|
||||
start: _.noop,
|
||||
stop: _.noop,
|
||||
};
|
||||
}
|
||||
|
@ -15,28 +15,12 @@ export const txDefaults = {
|
||||
gasPrice: constants.DEFAULT_GAS_PRICE,
|
||||
};
|
||||
|
||||
let providerConfigs: Web3Config = {
|
||||
export let providerConfigs: Web3Config = {
|
||||
total_accounts: constants.NUM_TEST_ACCOUNTS,
|
||||
shouldUseInProcessGanache: true,
|
||||
shouldAllowUnlimitedContractSize: true,
|
||||
};
|
||||
|
||||
if (process.env.FORK_RPC_URL !== undefined) {
|
||||
providerConfigs = {
|
||||
...providerConfigs,
|
||||
fork: process.env.FORK_RPC_URL,
|
||||
blockTime: 0,
|
||||
unlocked_accounts: [
|
||||
// ZeroExGovernor signer addresses
|
||||
'0x257619b7155d247e43c8b6d90c8c17278ae481f0',
|
||||
'0x5ee2a00f8f01d099451844af7f894f26a57fcbf2',
|
||||
'0x894d623e0e0e8ed12c4a73dada999e275684a37d',
|
||||
// ERC20BridgeProxy
|
||||
'0x8ed95d1746bf1e4dab58d8ed4724f1ef95b20db0',
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export const provider: Web3ProviderEngine = web3Factory.getRpcProvider(providerConfigs);
|
||||
provider.stop();
|
||||
const isCoverageEnabled = env.parseBoolean(EnvVars.SolidityCoverage);
|
||||
|
@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "3.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add `locked` `Web3Config` option.",
|
||||
"pr": 2407
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1576540892,
|
||||
"version": "3.0.2",
|
||||
|
@ -23,6 +23,7 @@ export interface Web3Config {
|
||||
shouldAllowUnlimitedContractSize?: boolean;
|
||||
fork?: string;
|
||||
blockTime?: number;
|
||||
locked?: boolean;
|
||||
unlocked_accounts?: string[];
|
||||
}
|
||||
|
||||
@ -78,6 +79,7 @@ export const web3Factory = {
|
||||
mnemonic: 'concert load couple harbor equip island argue ramp clarify fence smart topic',
|
||||
fork: config.fork,
|
||||
blockTime: config.blockTime,
|
||||
locked: config.locked,
|
||||
unlocked_accounts: config.unlocked_accounts,
|
||||
} as any), // TODO remove any once types are merged in DefinitelyTyped
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user