integration tests
This commit is contained in:
51
contracts/integrations/contracts/test/TestEth2Dai.sol
Normal file
51
contracts/integrations/contracts/test/TestEth2Dai.sol
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
|
||||
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-asset-proxy/contracts/src/interfaces/IEth2Dai.sol";
|
||||
import "@0x/contracts-erc20/contracts/test/DummyERC20Token.sol";
|
||||
|
||||
|
||||
contract TestEth2Dai is
|
||||
IEth2Dai
|
||||
{
|
||||
function sellAllAmount(
|
||||
address sellTokenAddress,
|
||||
uint256 sellTokenAmount,
|
||||
address buyTokenAddress,
|
||||
uint256 minimumFillAmount
|
||||
)
|
||||
external
|
||||
returns (uint256 fillAmount)
|
||||
{
|
||||
DummyERC20Token(sellTokenAddress).transferFrom(
|
||||
msg.sender,
|
||||
address(this),
|
||||
sellTokenAmount
|
||||
);
|
||||
DummyERC20Token buyToken = DummyERC20Token(buyTokenAddress);
|
||||
buyToken.mint(minimumFillAmount);
|
||||
buyToken.transfer(
|
||||
msg.sender,
|
||||
minimumFillAmount
|
||||
);
|
||||
return minimumFillAmount;
|
||||
}
|
||||
}
|
45
contracts/integrations/contracts/test/TestEth2DaiBridge.sol
Normal file
45
contracts/integrations/contracts/test/TestEth2DaiBridge.sol
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
|
||||
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-asset-proxy/contracts/src/bridges/Eth2DaiBridge.sol";
|
||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IEth2Dai.sol";
|
||||
|
||||
|
||||
contract TestEth2DaiBridge is
|
||||
Eth2DaiBridge
|
||||
{
|
||||
// solhint-disable var-name-mixedcase
|
||||
address public TEST_ETH2DAI_ADDRESS;
|
||||
|
||||
constructor (address testEth2Dai)
|
||||
public
|
||||
{
|
||||
TEST_ETH2DAI_ADDRESS = testEth2Dai;
|
||||
}
|
||||
|
||||
function _getEth2DaiContract()
|
||||
internal
|
||||
view
|
||||
returns (IEth2Dai exchange)
|
||||
{
|
||||
return IEth2Dai(TEST_ETH2DAI_ADDRESS);
|
||||
}
|
||||
}
|
59
contracts/integrations/contracts/test/TestUniswapBridge.sol
Normal file
59
contracts/integrations/contracts/test/TestUniswapBridge.sol
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
|
||||
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-asset-proxy/contracts/src/bridges/UniswapBridge.sol";
|
||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||
|
||||
|
||||
contract TestUniswapBridge is
|
||||
UniswapBridge
|
||||
{
|
||||
// solhint-disable var-name-mixedcase
|
||||
address public TEST_WETH_ADDRESS;
|
||||
address public TEST_UNISWAP_EXCHANGE_FACTORY_ADDRESS;
|
||||
|
||||
constructor (
|
||||
address testWeth,
|
||||
address testUniswapExchangeFactory
|
||||
)
|
||||
public
|
||||
{
|
||||
TEST_WETH_ADDRESS = testWeth;
|
||||
TEST_UNISWAP_EXCHANGE_FACTORY_ADDRESS = testUniswapExchangeFactory;
|
||||
}
|
||||
|
||||
function getWethContract()
|
||||
public
|
||||
view
|
||||
returns (IEtherToken token)
|
||||
{
|
||||
return IEtherToken(TEST_WETH_ADDRESS);
|
||||
}
|
||||
|
||||
function getUniswapExchangeFactoryContract()
|
||||
public
|
||||
view
|
||||
returns (IUniswapExchangeFactory factory)
|
||||
{
|
||||
return IUniswapExchangeFactory(TEST_UNISWAP_EXCHANGE_FACTORY_ADDRESS);
|
||||
}
|
||||
}
|
102
contracts/integrations/contracts/test/TestUniswapExchange.sol
Normal file
102
contracts/integrations/contracts/test/TestUniswapExchange.sol
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
|
||||
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-asset-proxy/contracts/src/interfaces/IUniswapExchange.sol";
|
||||
import "@0x/contracts-erc20/contracts/test/DummyERC20Token.sol";
|
||||
|
||||
|
||||
contract TestUniswapExchange is
|
||||
IUniswapExchange
|
||||
{
|
||||
DummyERC20Token public token;
|
||||
|
||||
constructor(address _tokenAddress) public {
|
||||
token = DummyERC20Token(_tokenAddress);
|
||||
}
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
/// @dev Used to receive ETH for testing.
|
||||
function topUpEth()
|
||||
external
|
||||
payable
|
||||
{}
|
||||
|
||||
function ethToTokenTransferInput(
|
||||
uint256 minTokensBought,
|
||||
uint256, /* deadline */
|
||||
address recipient
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (uint256 tokensBought)
|
||||
{
|
||||
token.mint(minTokensBought);
|
||||
token.transfer(recipient, minTokensBought);
|
||||
return minTokensBought;
|
||||
}
|
||||
|
||||
function tokenToEthSwapInput(
|
||||
uint256 tokensSold,
|
||||
uint256 minEthBought,
|
||||
uint256 /* deadline */
|
||||
)
|
||||
external
|
||||
returns (uint256 ethBought)
|
||||
{
|
||||
token.transferFrom(
|
||||
msg.sender,
|
||||
address(this),
|
||||
tokensSold
|
||||
);
|
||||
msg.sender.transfer(minEthBought);
|
||||
return minEthBought;
|
||||
}
|
||||
|
||||
function tokenToTokenTransferInput(
|
||||
uint256 tokensSold,
|
||||
uint256 minTokensBought,
|
||||
uint256, /* minEthBought */
|
||||
uint256, /* deadline */
|
||||
address recipient,
|
||||
address toTokenAddress
|
||||
)
|
||||
external
|
||||
returns (uint256 tokensBought)
|
||||
{
|
||||
token.transferFrom(
|
||||
msg.sender,
|
||||
address(this),
|
||||
tokensSold
|
||||
);
|
||||
DummyERC20Token toToken = DummyERC20Token(toTokenAddress);
|
||||
toToken.mint(minTokensBought);
|
||||
toToken.transfer(recipient, minTokensBought);
|
||||
return minTokensBought;
|
||||
}
|
||||
|
||||
function toTokenAddress()
|
||||
external
|
||||
view
|
||||
returns (address _tokenAddress)
|
||||
{
|
||||
return address(token);
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
|
||||
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-asset-proxy/contracts/src/bridges/UniswapBridge.sol";
|
||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol";
|
||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchange.sol";
|
||||
|
||||
|
||||
contract TestUniswapExchangeFactory is
|
||||
IUniswapExchangeFactory
|
||||
{
|
||||
// Token address to UniswapExchange address.
|
||||
mapping (address => address) private _testExchanges;
|
||||
|
||||
/// @dev Create a token and exchange (if they don't exist) for a new token
|
||||
/// and sets the exchange revert and fill behavior.
|
||||
/// @param tokenAddress The token address.
|
||||
function addExchange(
|
||||
address tokenAddress,
|
||||
address exchangeAddress
|
||||
)
|
||||
external
|
||||
{
|
||||
_testExchanges[tokenAddress] = exchangeAddress;
|
||||
}
|
||||
|
||||
/// @dev `IUniswapExchangeFactory.getExchange`
|
||||
function getExchange(address tokenAddress)
|
||||
external
|
||||
view
|
||||
returns (IUniswapExchange)
|
||||
{
|
||||
return IUniswapExchange(_testExchanges[tokenAddress]);
|
||||
}
|
||||
}
|
@@ -37,7 +37,7 @@
|
||||
},
|
||||
"config": {
|
||||
"publicInterfaceContracts": "TestFramework",
|
||||
"abis": "./test/generated-artifacts/@(TestFramework).json",
|
||||
"abis": "./test/generated-artifacts/@(TestEth2Dai|TestEth2DaiBridge|TestFramework|TestUniswapBridge|TestUniswapExchange|TestUniswapExchangeFactory).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
|
@@ -5,5 +5,17 @@
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
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';
|
||||
export const artifacts = { TestFramework: TestFramework as ContractArtifact };
|
||||
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';
|
||||
export const artifacts = {
|
||||
TestEth2Dai: TestEth2Dai as ContractArtifact,
|
||||
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
||||
TestFramework: TestFramework as ContractArtifact,
|
||||
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
|
||||
TestUniswapExchange: TestUniswapExchange as ContractArtifact,
|
||||
TestUniswapExchangeFactory: TestUniswapExchangeFactory as ContractArtifact,
|
||||
};
|
||||
|
28
contracts/integrations/test/bridges/deploy_eth2dai_bridge.ts
Normal file
28
contracts/integrations/test/bridges/deploy_eth2dai_bridge.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { artifacts as ERC20Artifacts } from '@0x/contracts-erc20';
|
||||
import { BlockchainTestsEnvironment } from '@0x/contracts-test-utils';
|
||||
|
||||
import { artifacts } from '../artifacts';
|
||||
import { DeploymentManager } from '../framework/deployment_manager';
|
||||
import { TestEth2DaiBridgeContract, TestEth2DaiContract } from '../wrappers';
|
||||
|
||||
export async function deployEth2DaiBridgeAsync(
|
||||
deployment: DeploymentManager,
|
||||
environment: BlockchainTestsEnvironment,
|
||||
): Promise<[TestEth2DaiBridgeContract, TestEth2DaiContract]> {
|
||||
const eth2Dai = await TestEth2DaiContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestEth2Dai,
|
||||
environment.provider,
|
||||
deployment.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
|
||||
const eth2DaiBridge = await TestEth2DaiBridgeContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestEth2DaiBridge,
|
||||
environment.provider,
|
||||
deployment.txDefaults,
|
||||
{ ...ERC20Artifacts, ...artifacts },
|
||||
eth2Dai.address,
|
||||
);
|
||||
|
||||
return [eth2DaiBridge, eth2Dai];
|
||||
}
|
47
contracts/integrations/test/bridges/deploy_uniswap_bridge.ts
Normal file
47
contracts/integrations/test/bridges/deploy_uniswap_bridge.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { artifacts as ERC20Artifacts } from '@0x/contracts-erc20';
|
||||
import { BlockchainTestsEnvironment } from '@0x/contracts-test-utils';
|
||||
|
||||
import { artifacts } from '../artifacts';
|
||||
import { DeploymentManager } from '../framework/deployment_manager';
|
||||
import {
|
||||
TestUniswapBridgeContract,
|
||||
TestUniswapExchangeContract,
|
||||
TestUniswapExchangeFactoryContract,
|
||||
} from '../wrappers';
|
||||
|
||||
export async function deployUniswapBridgeAsync(
|
||||
deployment: DeploymentManager,
|
||||
environment: BlockchainTestsEnvironment,
|
||||
tokenAddresses: string[],
|
||||
): Promise<[TestUniswapBridgeContract, TestUniswapExchangeContract[], TestUniswapExchangeFactoryContract]> {
|
||||
const uniswapExchangeFactory = await TestUniswapExchangeFactoryContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestUniswapExchangeFactory,
|
||||
environment.provider,
|
||||
deployment.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
|
||||
const uniswapExchanges = [];
|
||||
for (const tokenAddress of tokenAddresses) {
|
||||
const uniswapExchange = await TestUniswapExchangeContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestUniswapExchange,
|
||||
environment.provider,
|
||||
deployment.txDefaults,
|
||||
artifacts,
|
||||
tokenAddress,
|
||||
);
|
||||
await uniswapExchangeFactory.addExchange(tokenAddress, uniswapExchange.address).awaitTransactionSuccessAsync();
|
||||
uniswapExchanges.push(uniswapExchange);
|
||||
}
|
||||
|
||||
const uniswapBridge = await TestUniswapBridgeContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestUniswapBridge,
|
||||
environment.provider,
|
||||
deployment.txDefaults,
|
||||
{ ...ERC20Artifacts, ...artifacts },
|
||||
deployment.tokens.weth.address,
|
||||
uniswapExchangeFactory.address,
|
||||
);
|
||||
|
||||
return [uniswapBridge, uniswapExchanges, uniswapExchangeFactory];
|
||||
}
|
@@ -109,7 +109,7 @@ blockchainTests.resets('Coordinator integration tests', env => {
|
||||
msgValue?: BigNumber,
|
||||
): Promise<LocalBalanceStore> {
|
||||
let remainingValue = msgValue || constants.ZERO_AMOUNT;
|
||||
const localBalanceStore = LocalBalanceStore.create(devUtils, balanceStore);
|
||||
const localBalanceStore = LocalBalanceStore.create(balanceStore);
|
||||
// Transaction gas cost
|
||||
localBalanceStore.burnGas(txReceipt.from, DeploymentManager.gasPrice.times(txReceipt.gasUsed));
|
||||
|
||||
|
@@ -112,7 +112,7 @@ blockchainTests.resets('Exchange wrappers', env => {
|
||||
|
||||
await blockchainBalances.updateBalancesAsync();
|
||||
|
||||
initialLocalBalances = LocalBalanceStore.create(deployment.devUtils, blockchainBalances);
|
||||
initialLocalBalances = LocalBalanceStore.create(blockchainBalances);
|
||||
|
||||
wethAssetData = deployment.assetDataEncoder
|
||||
.ERC20Token(deployment.tokens.weth.address)
|
||||
@@ -120,7 +120,7 @@ blockchainTests.resets('Exchange wrappers', env => {
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
localBalances = LocalBalanceStore.create(deployment.devUtils, initialLocalBalances);
|
||||
localBalances = LocalBalanceStore.create(initialLocalBalances);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
|
@@ -72,7 +72,7 @@ export class FillOrderWrapper {
|
||||
initBalanceStore: BalanceStore,
|
||||
opts: { takerAssetFillAmount?: BigNumber } = {},
|
||||
): Promise<[FillResults, FillEventArgs, BalanceStore]> {
|
||||
const balanceStore = LocalBalanceStore.create(this._devUtils, initBalanceStore);
|
||||
const balanceStore = LocalBalanceStore.create(initBalanceStore);
|
||||
const takerAssetFillAmount =
|
||||
opts.takerAssetFillAmount !== undefined ? opts.takerAssetFillAmount : signedOrder.takerAssetAmount;
|
||||
// TODO(jalextowle): Change this if the integration tests take protocol fees into account.
|
||||
|
@@ -118,7 +118,7 @@ blockchainTests.resets('fillOrder integration tests', env => {
|
||||
msgValue?: BigNumber,
|
||||
): Promise<LocalBalanceStore> {
|
||||
let remainingValue = msgValue !== undefined ? msgValue : DeploymentManager.protocolFee;
|
||||
const localBalanceStore = LocalBalanceStore.create(deployment.devUtils, balanceStore);
|
||||
const localBalanceStore = LocalBalanceStore.create(balanceStore);
|
||||
// Transaction gas cost
|
||||
localBalanceStore.burnGas(txReceipt.from, DeploymentManager.gasPrice.times(txReceipt.gasUsed));
|
||||
|
||||
@@ -266,7 +266,7 @@ blockchainTests.resets('fillOrder integration tests', env => {
|
||||
|
||||
// Fetch the current balances
|
||||
await balanceStore.updateBalancesAsync();
|
||||
const expectedBalances = LocalBalanceStore.create(deployment.devUtils, balanceStore);
|
||||
const expectedBalances = LocalBalanceStore.create(balanceStore);
|
||||
|
||||
// End the epoch. This should wrap the staking proxy's ETH balance.
|
||||
const endEpochReceipt = await delegator.endEpochAsync();
|
||||
|
352
contracts/integrations/test/forwarder/bridge_test.ts
Normal file
352
contracts/integrations/test/forwarder/bridge_test.ts
Normal file
@@ -0,0 +1,352 @@
|
||||
import { IAssetDataContract } from '@0x/contracts-asset-proxy';
|
||||
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||
import { ForwarderContract, ForwarderRevertErrors } from '@0x/contracts-exchange-forwarder';
|
||||
import {
|
||||
blockchainTests,
|
||||
constants,
|
||||
getLatestBlockTimestampAsync,
|
||||
hexConcat,
|
||||
toBaseUnitAmount,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { generatePseudoRandomSalt } from '@0x/order-utils';
|
||||
import { SignatureType, SignedOrder } from '@0x/types';
|
||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||
|
||||
import { deployEth2DaiBridgeAsync } from '../bridges/deploy_eth2dai_bridge';
|
||||
import { deployUniswapBridgeAsync } from '../bridges/deploy_uniswap_bridge';
|
||||
import { Actor } from '../framework/actors/base';
|
||||
import { FeeRecipient } from '../framework/actors/fee_recipient';
|
||||
import { Maker } from '../framework/actors/maker';
|
||||
import { Taker } from '../framework/actors/taker';
|
||||
import { actorAddressesByName } from '../framework/actors/utils';
|
||||
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
|
||||
import { DeploymentManager } from '../framework/deployment_manager';
|
||||
|
||||
import { deployForwarderAsync } from './deploy_forwarder';
|
||||
import { ForwarderTestFactory } from './forwarder_test_factory';
|
||||
|
||||
blockchainTests.resets('Forwarder <> ERC20Bridge integration tests', env => {
|
||||
let deployment: DeploymentManager;
|
||||
let forwarder: ForwarderContract;
|
||||
let assetDataEncoder: IAssetDataContract;
|
||||
let balanceStore: BlockchainBalanceStore;
|
||||
let testFactory: ForwarderTestFactory;
|
||||
|
||||
let erc721Token: DummyERC721TokenContract;
|
||||
let nftId: BigNumber;
|
||||
let makerTokenAssetData: string;
|
||||
let makerFeeTokenAssetData: string;
|
||||
let eth2DaiBridgeAssetData: string;
|
||||
let uniswapBridgeAssetData: string;
|
||||
|
||||
let maker: Maker;
|
||||
let taker: Taker;
|
||||
let orderFeeRecipient: FeeRecipient;
|
||||
let forwarderFeeRecipient: FeeRecipient;
|
||||
|
||||
let eth2DaiBridgeOrder: SignedOrder;
|
||||
let uniswapBridgeOrder: SignedOrder;
|
||||
|
||||
before(async () => {
|
||||
assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, env.provider);
|
||||
deployment = await DeploymentManager.deployAsync(env, {
|
||||
numErc20TokensToDeploy: 2,
|
||||
numErc721TokensToDeploy: 1,
|
||||
numErc1155TokensToDeploy: 0,
|
||||
});
|
||||
const [makerToken, makerFeeToken] = deployment.tokens.erc20;
|
||||
[erc721Token] = deployment.tokens.erc721;
|
||||
|
||||
forwarder = await deployForwarderAsync(deployment, env);
|
||||
const [eth2DaiBridge] = await deployEth2DaiBridgeAsync(deployment, env);
|
||||
const [uniswapBridge, [uniswapMakerTokenExchange]] = await deployUniswapBridgeAsync(deployment, env, [
|
||||
makerToken.address,
|
||||
]);
|
||||
|
||||
makerTokenAssetData = assetDataEncoder.ERC20Token(makerToken.address).getABIEncodedTransactionData();
|
||||
makerFeeTokenAssetData = assetDataEncoder.ERC20Token(makerFeeToken.address).getABIEncodedTransactionData();
|
||||
const wethAssetData = assetDataEncoder
|
||||
.ERC20Token(deployment.tokens.weth.address)
|
||||
.getABIEncodedTransactionData();
|
||||
|
||||
const bridgeDataEncoder = AbiEncoder.create([{ name: 'fromTokenAddress', type: 'address' }]);
|
||||
const bridgeData = bridgeDataEncoder.encode([deployment.tokens.weth.address]);
|
||||
eth2DaiBridgeAssetData = assetDataEncoder
|
||||
.ERC20Bridge(makerToken.address, eth2DaiBridge.address, bridgeData)
|
||||
.getABIEncodedTransactionData();
|
||||
uniswapBridgeAssetData = assetDataEncoder
|
||||
.ERC20Bridge(makerToken.address, uniswapBridge.address, bridgeData)
|
||||
.getABIEncodedTransactionData();
|
||||
|
||||
taker = new Taker({ name: 'Taker', deployment });
|
||||
orderFeeRecipient = new FeeRecipient({
|
||||
name: 'Order fee recipient',
|
||||
deployment,
|
||||
});
|
||||
forwarderFeeRecipient = new FeeRecipient({
|
||||
name: 'Forwarder fee recipient',
|
||||
deployment,
|
||||
});
|
||||
|
||||
const fifteenMinutesInSeconds = 15 * 60;
|
||||
const currentBlockTimestamp = await getLatestBlockTimestampAsync();
|
||||
const orderDefaults = {
|
||||
chainId: deployment.chainId,
|
||||
exchangeAddress: deployment.exchange.address,
|
||||
takerAddress: constants.NULL_ADDRESS,
|
||||
feeRecipientAddress: orderFeeRecipient.address,
|
||||
senderAddress: constants.NULL_ADDRESS,
|
||||
makerAssetAmount: toBaseUnitAmount(2),
|
||||
takerAssetAmount: toBaseUnitAmount(1),
|
||||
takerAssetData: wethAssetData,
|
||||
makerFee: constants.ZERO_AMOUNT,
|
||||
takerFee: constants.ZERO_AMOUNT,
|
||||
makerFeeAssetData: makerFeeTokenAssetData,
|
||||
takerFeeAssetData: wethAssetData,
|
||||
expirationTimeSeconds: new BigNumber(currentBlockTimestamp).plus(fifteenMinutesInSeconds),
|
||||
salt: generatePseudoRandomSalt(),
|
||||
signature: hexConcat(SignatureType.Wallet),
|
||||
};
|
||||
eth2DaiBridgeOrder = {
|
||||
...orderDefaults,
|
||||
makerAddress: eth2DaiBridge.address,
|
||||
makerAssetData: eth2DaiBridgeAssetData,
|
||||
};
|
||||
uniswapBridgeOrder = {
|
||||
...orderDefaults,
|
||||
makerAddress: uniswapBridge.address,
|
||||
makerAssetData: uniswapBridgeAssetData,
|
||||
};
|
||||
|
||||
maker = new Maker({
|
||||
name: 'Maker',
|
||||
deployment,
|
||||
orderConfig: { ...orderDefaults, makerFee: toBaseUnitAmount(0.01) },
|
||||
});
|
||||
await maker.configureERC20TokenAsync(makerToken);
|
||||
await maker.configureERC20TokenAsync(makerFeeToken);
|
||||
await forwarder.approveMakerAssetProxy(makerTokenAssetData).awaitTransactionSuccessAsync();
|
||||
[nftId] = await maker.configureERC721TokenAsync(erc721Token);
|
||||
|
||||
// We need to top up the TestUniswapExchange with some ETH so that it can perform tokenToEthSwapInput
|
||||
await uniswapMakerTokenExchange.topUpEth().awaitTransactionSuccessAsync({
|
||||
from: forwarderFeeRecipient.address,
|
||||
value: constants.ONE_ETHER.times(10),
|
||||
});
|
||||
|
||||
const tokenOwners = {
|
||||
...actorAddressesByName([maker, taker, orderFeeRecipient, forwarderFeeRecipient]),
|
||||
Forwarder: forwarder.address,
|
||||
StakingProxy: deployment.staking.stakingProxy.address,
|
||||
};
|
||||
const tokenContracts = {
|
||||
erc20: { makerToken, makerFeeToken, wETH: deployment.tokens.weth },
|
||||
erc721: { erc721Token },
|
||||
};
|
||||
const tokenIds = { erc721: { [erc721Token.address]: [nftId] } };
|
||||
balanceStore = new BlockchainBalanceStore(tokenOwners, tokenContracts, tokenIds);
|
||||
|
||||
testFactory = new ForwarderTestFactory(forwarder, deployment, balanceStore, taker, forwarderFeeRecipient);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
Actor.count = 0;
|
||||
});
|
||||
|
||||
describe('marketSellOrdersWithEth', () => {
|
||||
it('should fully fill a single Eth2DaiBridge order without a taker fee', async () => {
|
||||
await testFactory.marketSellTestAsync([eth2DaiBridgeOrder], 1);
|
||||
});
|
||||
it('should partially fill a single Eth2DaiBridge order without a taker fee', async () => {
|
||||
await testFactory.marketSellTestAsync([eth2DaiBridgeOrder], 0.34);
|
||||
});
|
||||
it('should fill a single Eth2DaiBridge order with a WETH taker fee', async () => {
|
||||
const order = {
|
||||
...eth2DaiBridgeOrder,
|
||||
takerFee: toBaseUnitAmount(0.01),
|
||||
};
|
||||
await testFactory.marketSellTestAsync([order], 0.78);
|
||||
});
|
||||
it('should fill a single Eth2DaiBridge order with a percentage taker fee', async () => {
|
||||
const order = {
|
||||
...eth2DaiBridgeOrder,
|
||||
takerFee: toBaseUnitAmount(0.01),
|
||||
takerFeeAssetData: makerTokenAssetData,
|
||||
};
|
||||
await testFactory.marketSellTestAsync([order], 0.78);
|
||||
});
|
||||
it('should fill an Eth2DaiBridge order along with non-bridge orders, with an affiliate fee', async () => {
|
||||
const orders = [
|
||||
// ERC721 order
|
||||
await maker.signOrderAsync({
|
||||
makerAssetAmount: new BigNumber(1),
|
||||
makerAssetData: assetDataEncoder
|
||||
.ERC721Token(erc721Token.address, nftId)
|
||||
.getABIEncodedTransactionData(),
|
||||
takerFee: toBaseUnitAmount(0.01),
|
||||
}),
|
||||
eth2DaiBridgeOrder,
|
||||
await maker.signOrderAsync({ makerAssetData: makerTokenAssetData }), // Non-bridge order of the same ERC20
|
||||
];
|
||||
await testFactory.marketSellTestAsync(orders, 2.56, { forwarderFeePercentage: 1 });
|
||||
});
|
||||
it('should fully fill a single UniswapBridge order without a taker fee', async () => {
|
||||
await testFactory.marketSellTestAsync([uniswapBridgeOrder], 1);
|
||||
});
|
||||
it('should partially fill a single UniswapBridge order without a taker fee', async () => {
|
||||
await testFactory.marketSellTestAsync([uniswapBridgeOrder], 0.34);
|
||||
});
|
||||
it('should fill a single UniswapBridge order with a WETH taker fee', async () => {
|
||||
const order = {
|
||||
...uniswapBridgeOrder,
|
||||
takerFee: toBaseUnitAmount(0.01),
|
||||
};
|
||||
await testFactory.marketSellTestAsync([order], 0.78);
|
||||
});
|
||||
it('should fill a single UniswapBridge order with a percentage taker fee', async () => {
|
||||
const order = {
|
||||
...uniswapBridgeOrder,
|
||||
takerFee: toBaseUnitAmount(0.01),
|
||||
takerFeeAssetData: makerTokenAssetData,
|
||||
};
|
||||
await testFactory.marketSellTestAsync([order], 0.78);
|
||||
});
|
||||
it('should fill an UniswapBridge order along with non-bridge orders', async () => {
|
||||
const orders = [
|
||||
// ERC721 order
|
||||
await maker.signOrderAsync({
|
||||
makerAssetAmount: new BigNumber(1),
|
||||
makerAssetData: assetDataEncoder
|
||||
.ERC721Token(erc721Token.address, nftId)
|
||||
.getABIEncodedTransactionData(),
|
||||
takerFee: toBaseUnitAmount(0.01),
|
||||
}),
|
||||
uniswapBridgeOrder,
|
||||
await maker.signOrderAsync({ makerAssetData: makerTokenAssetData }), // Non-bridge order of the same ERC20
|
||||
];
|
||||
await testFactory.marketSellTestAsync(orders, 2.56, { forwarderFeePercentage: 1 });
|
||||
});
|
||||
it('should fill multiple bridge orders', async () => {
|
||||
await testFactory.marketSellTestAsync([eth2DaiBridgeOrder, uniswapBridgeOrder], 1.23);
|
||||
});
|
||||
it('should revert if the takerFee is denominated in a different token', async () => {
|
||||
const order = {
|
||||
...eth2DaiBridgeOrder,
|
||||
takerFee: toBaseUnitAmount(0.01),
|
||||
takerFeeAssetData: makerFeeTokenAssetData,
|
||||
};
|
||||
const expectedError = new ForwarderRevertErrors.UnsupportedFeeError(makerFeeTokenAssetData);
|
||||
await testFactory.marketSellTestAsync([order], 1.23, { revertError: expectedError });
|
||||
});
|
||||
});
|
||||
describe('marketBuyOrdersWithEth', () => {
|
||||
it('should fully fill a single Eth2DaiBridge order without a taker fee', async () => {
|
||||
await testFactory.marketBuyTestAsync([eth2DaiBridgeOrder], 1);
|
||||
});
|
||||
it('should partially fill a single Eth2DaiBridge order without a taker fee', async () => {
|
||||
await testFactory.marketBuyTestAsync([eth2DaiBridgeOrder], 0.34);
|
||||
});
|
||||
it('should return excess ETH', async () => {
|
||||
await testFactory.marketBuyTestAsync([eth2DaiBridgeOrder], 1, { ethValueAdjustment: 1 });
|
||||
});
|
||||
it('should fill a single Eth2DaiBridge order with a WETH taker fee', async () => {
|
||||
const order = {
|
||||
...eth2DaiBridgeOrder,
|
||||
takerFee: toBaseUnitAmount(0.01),
|
||||
};
|
||||
await testFactory.marketBuyTestAsync([order], 0.78);
|
||||
});
|
||||
it('should fill a single Eth2DaiBridge order with a percentage taker fee', async () => {
|
||||
const order = {
|
||||
...eth2DaiBridgeOrder,
|
||||
takerFee: toBaseUnitAmount(0.01),
|
||||
takerFeeAssetData: makerTokenAssetData,
|
||||
};
|
||||
await testFactory.marketBuyTestAsync([order], 0.78);
|
||||
});
|
||||
it('should fill an Eth2DaiBridge order along with non-bridge orders, with an affiliate fee', async () => {
|
||||
const orders = [
|
||||
// ERC721 order
|
||||
await maker.signOrderAsync({
|
||||
makerAssetAmount: new BigNumber(1),
|
||||
makerAssetData: assetDataEncoder
|
||||
.ERC721Token(erc721Token.address, nftId)
|
||||
.getABIEncodedTransactionData(),
|
||||
takerFee: toBaseUnitAmount(0.01),
|
||||
}),
|
||||
eth2DaiBridgeOrder,
|
||||
await maker.signOrderAsync({ makerAssetData: makerTokenAssetData }), // Non-bridge order of the same ERC20
|
||||
];
|
||||
await testFactory.marketBuyTestAsync(orders, 2.56, { forwarderFeePercentage: 1 });
|
||||
});
|
||||
it('should revert if the amount of ETH sent is too low to fill the makerAssetAmount (Eth2Dai)', async () => {
|
||||
const expectedError = new ForwarderRevertErrors.CompleteBuyFailedError(
|
||||
eth2DaiBridgeOrder.makerAssetAmount.times(0.5),
|
||||
constants.ZERO_AMOUNT,
|
||||
);
|
||||
await testFactory.marketBuyTestAsync([eth2DaiBridgeOrder], 0.5, {
|
||||
ethValueAdjustment: -2,
|
||||
revertError: expectedError,
|
||||
});
|
||||
});
|
||||
it('should fully fill a single UniswapBridge order without a taker fee', async () => {
|
||||
await testFactory.marketBuyTestAsync([uniswapBridgeOrder], 1);
|
||||
});
|
||||
it('should partially fill a single UniswapBridge order without a taker fee', async () => {
|
||||
await testFactory.marketBuyTestAsync([uniswapBridgeOrder], 0.34);
|
||||
});
|
||||
it('should fill a single UniswapBridge order with a WETH taker fee', async () => {
|
||||
const order = {
|
||||
...uniswapBridgeOrder,
|
||||
takerFee: toBaseUnitAmount(0.01),
|
||||
};
|
||||
await testFactory.marketBuyTestAsync([order], 0.78);
|
||||
});
|
||||
it('should fill a single UniswapBridge order with a percentage taker fee', async () => {
|
||||
const order = {
|
||||
...uniswapBridgeOrder,
|
||||
takerFee: toBaseUnitAmount(0.01),
|
||||
takerFeeAssetData: makerTokenAssetData,
|
||||
};
|
||||
await testFactory.marketBuyTestAsync([order], 0.78);
|
||||
});
|
||||
it('should fill an UniswapBridge order along with non-bridge orders', async () => {
|
||||
const orders = [
|
||||
// ERC721 order
|
||||
await maker.signOrderAsync({
|
||||
makerAssetAmount: new BigNumber(1),
|
||||
makerAssetData: assetDataEncoder
|
||||
.ERC721Token(erc721Token.address, nftId)
|
||||
.getABIEncodedTransactionData(),
|
||||
takerFee: toBaseUnitAmount(0.01),
|
||||
}),
|
||||
uniswapBridgeOrder,
|
||||
await maker.signOrderAsync({ makerAssetData: makerTokenAssetData }), // Non-bridge order of the same ERC20
|
||||
];
|
||||
await testFactory.marketBuyTestAsync(orders, 2.56, { forwarderFeePercentage: 1 });
|
||||
});
|
||||
it('should revert if the amount of ETH sent is too low to fill the makerAssetAmount (Uniswap)', async () => {
|
||||
const expectedError = new ForwarderRevertErrors.CompleteBuyFailedError(
|
||||
uniswapBridgeOrder.makerAssetAmount.times(0.5),
|
||||
constants.ZERO_AMOUNT,
|
||||
);
|
||||
await testFactory.marketBuyTestAsync([uniswapBridgeOrder], 0.5, {
|
||||
ethValueAdjustment: -2,
|
||||
revertError: expectedError,
|
||||
});
|
||||
});
|
||||
it('should fill multiple bridge orders', async () => {
|
||||
await testFactory.marketBuyTestAsync([eth2DaiBridgeOrder, uniswapBridgeOrder], 1.23);
|
||||
});
|
||||
it('should revert if the takerFee is denominated in a different token', async () => {
|
||||
const order = {
|
||||
...eth2DaiBridgeOrder,
|
||||
takerFee: toBaseUnitAmount(0.01),
|
||||
takerFeeAssetData: makerFeeTokenAssetData,
|
||||
};
|
||||
const expectedError = new ForwarderRevertErrors.UnsupportedFeeError(makerFeeTokenAssetData);
|
||||
await testFactory.marketBuyTestAsync([order], 1.23, { revertError: expectedError });
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:disable:max-file-line-count
|
@@ -17,6 +17,6 @@ export async function deployForwarderAsync(
|
||||
deployment.txDefaults,
|
||||
{ ...exchangeArtifacts, ...artifacts },
|
||||
deployment.exchange.address,
|
||||
deployment.assetDataEncoder.ERC20Token(deployment.tokens.weth.address).getABIEncodedTransactionData(),
|
||||
deployment.tokens.weth.address,
|
||||
);
|
||||
}
|
||||
|
@@ -110,11 +110,8 @@ blockchainTests('Forwarder integration tests', env => {
|
||||
forwarder,
|
||||
deployment,
|
||||
balanceStore,
|
||||
maker,
|
||||
taker,
|
||||
orderFeeRecipient,
|
||||
forwarderFeeRecipient,
|
||||
devUtils,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -138,7 +135,7 @@ blockchainTests('Forwarder integration tests', env => {
|
||||
env.txDefaults,
|
||||
{},
|
||||
exchange.address,
|
||||
wethAssetData,
|
||||
deployment.tokens.weth.address,
|
||||
);
|
||||
await expect(deployForwarder).to.revertWith(new ForwarderRevertErrors.UnregisteredAssetProxyError());
|
||||
});
|
||||
@@ -202,7 +199,7 @@ blockchainTests('Forwarder integration tests', env => {
|
||||
from: taker.address,
|
||||
});
|
||||
|
||||
const expectedBalances = LocalBalanceStore.create(devUtils, balanceStore);
|
||||
const expectedBalances = LocalBalanceStore.create(balanceStore);
|
||||
expectedBalances.burnGas(tx.from, DeploymentManager.gasPrice.times(tx.gasUsed));
|
||||
|
||||
// Verify balances
|
||||
@@ -521,7 +518,7 @@ blockchainTests('Forwarder integration tests', env => {
|
||||
});
|
||||
|
||||
// Compute expected balances
|
||||
const expectedBalances = LocalBalanceStore.create(devUtils, balanceStore);
|
||||
const expectedBalances = LocalBalanceStore.create(balanceStore);
|
||||
await expectedBalances.transferAssetAsync(
|
||||
maker.address,
|
||||
taker.address,
|
||||
@@ -578,7 +575,7 @@ blockchainTests('Forwarder integration tests', env => {
|
||||
});
|
||||
|
||||
// Compute expected balances
|
||||
const expectedBalances = LocalBalanceStore.create(devUtils, balanceStore);
|
||||
const expectedBalances = LocalBalanceStore.create(balanceStore);
|
||||
await expectedBalances.transferAssetAsync(
|
||||
maker.address,
|
||||
taker.address,
|
||||
|
@@ -1,12 +1,19 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { IAssetDataContract } from '@0x/contracts-asset-proxy';
|
||||
import { ForwarderContract } from '@0x/contracts-exchange-forwarder';
|
||||
import { constants, expect, getPercentageOfValue, OrderStatus } from '@0x/contracts-test-utils';
|
||||
import { OrderInfo, SignedOrder } from '@0x/types';
|
||||
import {
|
||||
constants,
|
||||
expect,
|
||||
getPercentageOfValue,
|
||||
hexSlice,
|
||||
Numberish,
|
||||
OrderStatus,
|
||||
provider,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { AssetProxyId, OrderInfo, SignedOrder } from '@0x/types';
|
||||
import { BigNumber, RevertError } from '@0x/utils';
|
||||
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
|
||||
import { FeeRecipient } from '../framework/actors/fee_recipient';
|
||||
import { Maker } from '../framework/actors/maker';
|
||||
import { Taker } from '../framework/actors/taker';
|
||||
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
|
||||
import { LocalBalanceStore } from '../framework/balances/local_balance_store';
|
||||
@@ -20,7 +27,7 @@ interface ForwarderFillState {
|
||||
}
|
||||
|
||||
interface MarketSellOptions {
|
||||
forwarderFeePercentage: BigNumber;
|
||||
forwarderFeePercentage: Numberish;
|
||||
revertError: RevertError;
|
||||
}
|
||||
|
||||
@@ -28,16 +35,32 @@ interface MarketBuyOptions extends MarketSellOptions {
|
||||
ethValueAdjustment: number; // Used to provided insufficient/excess ETH
|
||||
}
|
||||
|
||||
function isPercentageFee(takerFeeAssetData: string, makerAssetData: string): boolean {
|
||||
const makerAssetProxyId = hexSlice(makerAssetData, 0, 4);
|
||||
const takerFeeAssetProxyId = hexSlice(takerFeeAssetData, 0, 4);
|
||||
if (makerAssetProxyId === AssetProxyId.ERC20Bridge && takerFeeAssetProxyId === AssetProxyId.ERC20) {
|
||||
const assetDataDecoder = new IAssetDataContract(constants.NULL_ADDRESS, provider);
|
||||
const [makerTokenAddress] = assetDataDecoder.getABIDecodedTransactionData<string>(
|
||||
'ERC20Bridge',
|
||||
makerAssetData,
|
||||
);
|
||||
const takerFeeTokenAddress = assetDataDecoder.getABIDecodedTransactionData<string>(
|
||||
'ERC20Token',
|
||||
takerFeeAssetData,
|
||||
);
|
||||
return makerTokenAddress === takerFeeTokenAddress;
|
||||
} else {
|
||||
return makerAssetData === takerFeeAssetData;
|
||||
}
|
||||
}
|
||||
|
||||
export class ForwarderTestFactory {
|
||||
constructor(
|
||||
private readonly _forwarder: ForwarderContract,
|
||||
private readonly _deployment: DeploymentManager,
|
||||
private readonly _balanceStore: BlockchainBalanceStore,
|
||||
private readonly _maker: Maker,
|
||||
private readonly _taker: Taker,
|
||||
private readonly _orderFeeRecipient: FeeRecipient,
|
||||
private readonly _forwarderFeeRecipient: FeeRecipient,
|
||||
private readonly _devUtils: DevUtilsContract,
|
||||
) {}
|
||||
|
||||
public async marketBuyTestAsync(
|
||||
@@ -164,7 +187,7 @@ export class ForwarderTestFactory {
|
||||
options: Partial<MarketBuyOptions>,
|
||||
): Promise<ForwarderFillState> {
|
||||
await this._balanceStore.updateBalancesAsync();
|
||||
const balances = LocalBalanceStore.create(this._devUtils, this._balanceStore);
|
||||
const balances = LocalBalanceStore.create(this._balanceStore);
|
||||
const currentTotal = {
|
||||
wethSpentAmount: constants.ZERO_AMOUNT,
|
||||
makerAssetAcquiredAmount: constants.ZERO_AMOUNT,
|
||||
@@ -230,7 +253,7 @@ export class ForwarderTestFactory {
|
||||
|
||||
let wethSpentAmount = takerAssetAmount.plus(DeploymentManager.protocolFee);
|
||||
let makerAssetAcquiredAmount = makerAssetAmount;
|
||||
if (order.takerFeeAssetData === order.makerAssetData) {
|
||||
if (isPercentageFee(order.takerFeeAssetData, order.makerAssetData)) {
|
||||
makerAssetAcquiredAmount = makerAssetAcquiredAmount.minus(takerFee);
|
||||
} else if (order.takerFeeAssetData === order.takerAssetData) {
|
||||
wethSpentAmount = wethSpentAmount.plus(takerFee);
|
||||
@@ -244,29 +267,29 @@ export class ForwarderTestFactory {
|
||||
|
||||
// Maker -> Forwarder
|
||||
await balances.transferAssetAsync(
|
||||
this._maker.address,
|
||||
order.makerAddress,
|
||||
this._forwarder.address,
|
||||
makerAssetAmount,
|
||||
order.makerAssetData,
|
||||
);
|
||||
// Maker -> Order fee recipient
|
||||
await balances.transferAssetAsync(
|
||||
this._maker.address,
|
||||
this._orderFeeRecipient.address,
|
||||
order.makerAddress,
|
||||
order.feeRecipientAddress,
|
||||
makerFee,
|
||||
order.makerFeeAssetData,
|
||||
);
|
||||
// Forwarder -> Maker
|
||||
await balances.transferAssetAsync(
|
||||
this._forwarder.address,
|
||||
this._maker.address,
|
||||
order.makerAddress,
|
||||
takerAssetAmount,
|
||||
order.takerAssetData,
|
||||
);
|
||||
// Forwarder -> Order fee recipient
|
||||
await balances.transferAssetAsync(
|
||||
this._forwarder.address,
|
||||
this._orderFeeRecipient.address,
|
||||
order.feeRecipientAddress,
|
||||
takerFee,
|
||||
order.takerFeeAssetData,
|
||||
);
|
||||
|
@@ -36,7 +36,7 @@ export function validStakeAssertion(
|
||||
return new FunctionAssertion(stakingWrapper.stake, {
|
||||
before: async (amount: BigNumber, txData: Partial<TxData>) => {
|
||||
// Simulates the transfer of ZRX from staker to vault
|
||||
const expectedBalances = LocalBalanceStore.create(deployment.devUtils, balanceStore);
|
||||
const expectedBalances = LocalBalanceStore.create(balanceStore);
|
||||
await expectedBalances.transferAssetAsync(
|
||||
txData.from as string,
|
||||
zrxVault.address,
|
||||
|
@@ -36,7 +36,7 @@ export function validUnstakeAssertion(
|
||||
return new FunctionAssertion(stakingWrapper.unstake, {
|
||||
before: async (amount: BigNumber, txData: Partial<TxData>) => {
|
||||
// Simulates the transfer of ZRX from vault to staker
|
||||
const expectedBalances = LocalBalanceStore.create(deployment.devUtils, balanceStore);
|
||||
const expectedBalances = LocalBalanceStore.create(balanceStore);
|
||||
await expectedBalances.transferAssetAsync(
|
||||
zrxVault.address,
|
||||
txData.from as string,
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { constants, Numberish } from '@0x/contracts-test-utils';
|
||||
import { IAssetDataContract } from '@0x/contracts-asset-proxy';
|
||||
import { constants, hexSlice, Numberish, provider } from '@0x/contracts-test-utils';
|
||||
import { AssetProxyId } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
@@ -8,12 +8,14 @@ import { BalanceStore } from './balance_store';
|
||||
import { TokenContractsByName, TokenOwnersByName } from './types';
|
||||
|
||||
export class LocalBalanceStore extends BalanceStore {
|
||||
private _assetDataDecoder: IAssetDataContract;
|
||||
|
||||
/**
|
||||
* Creates a new balance store based on an existing one.
|
||||
* @param sourceBalanceStore Existing balance store whose values should be copied.
|
||||
*/
|
||||
public static create(devUtils: DevUtilsContract, sourceBalanceStore?: BalanceStore): LocalBalanceStore {
|
||||
const localBalanceStore = new LocalBalanceStore(devUtils);
|
||||
public static create(sourceBalanceStore?: BalanceStore): LocalBalanceStore {
|
||||
const localBalanceStore = new LocalBalanceStore();
|
||||
if (sourceBalanceStore !== undefined) {
|
||||
localBalanceStore.cloneFrom(sourceBalanceStore);
|
||||
}
|
||||
@@ -26,11 +28,11 @@ export class LocalBalanceStore extends BalanceStore {
|
||||
* be initialized via `create`.
|
||||
*/
|
||||
protected constructor(
|
||||
private readonly _devUtils: DevUtilsContract,
|
||||
tokenOwnersByName: TokenOwnersByName = {},
|
||||
tokenContractsByName: Partial<TokenContractsByName> = {},
|
||||
) {
|
||||
super(tokenOwnersByName, tokenContractsByName);
|
||||
this._assetDataDecoder = new IAssetDataContract(constants.NULL_ADDRESS, provider);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,25 +80,41 @@ export class LocalBalanceStore extends BalanceStore {
|
||||
amount: BigNumber,
|
||||
assetData: string,
|
||||
): Promise<void> {
|
||||
if (fromAddress === toAddress) {
|
||||
if (fromAddress === toAddress || amount.isZero()) {
|
||||
return;
|
||||
}
|
||||
const assetProxyId = await this._devUtils.decodeAssetProxyId(assetData).callAsync();
|
||||
const assetProxyId = hexSlice(assetData, 0, 4);
|
||||
switch (assetProxyId) {
|
||||
case AssetProxyId.ERC20: {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
const [_proxyId, tokenAddress] = await this._devUtils.decodeERC20AssetData(assetData).callAsync();
|
||||
const tokenAddress = this._assetDataDecoder.getABIDecodedTransactionData<string>(
|
||||
'ERC20Token',
|
||||
assetData,
|
||||
);
|
||||
_.update(this.balances.erc20, [fromAddress, tokenAddress], balance => balance.minus(amount));
|
||||
_.update(this.balances.erc20, [toAddress, tokenAddress], balance =>
|
||||
(balance || constants.ZERO_AMOUNT).plus(amount),
|
||||
);
|
||||
break;
|
||||
}
|
||||
case AssetProxyId.ERC20Bridge: {
|
||||
const [tokenAddress] = this._assetDataDecoder.getABIDecodedTransactionData<[string]>(
|
||||
'ERC20Bridge',
|
||||
assetData,
|
||||
);
|
||||
// The test bridge contract (TestEth2DaiBridge or TestUniswapBridge) will be the
|
||||
// fromAddress in this case, and it simply mints the amount of token it needs to transfer.
|
||||
_.update(this.balances.erc20, [fromAddress, tokenAddress], balance =>
|
||||
(balance || constants.ZERO_AMOUNT).minus(amount),
|
||||
);
|
||||
_.update(this.balances.erc20, [toAddress, tokenAddress], balance =>
|
||||
(balance || constants.ZERO_AMOUNT).plus(amount),
|
||||
);
|
||||
break;
|
||||
}
|
||||
case AssetProxyId.ERC721: {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
const [_proxyId, tokenAddress, tokenId] = await this._devUtils
|
||||
.decodeERC721AssetData(assetData)
|
||||
.callAsync();
|
||||
const [tokenAddress, tokenId] = this._assetDataDecoder.getABIDecodedTransactionData<
|
||||
[string, BigNumber]
|
||||
>('ERC721Token', assetData);
|
||||
const fromTokens = _.get(this.balances.erc721, [fromAddress, tokenAddress], []);
|
||||
const toTokens = _.get(this.balances.erc721, [toAddress, tokenAddress], []);
|
||||
if (amount.gte(1)) {
|
||||
@@ -112,12 +130,9 @@ export class LocalBalanceStore extends BalanceStore {
|
||||
break;
|
||||
}
|
||||
case AssetProxyId.ERC1155: {
|
||||
const [
|
||||
_proxyId, // tslint:disable-line:no-unused-variable
|
||||
tokenAddress,
|
||||
tokenIds,
|
||||
tokenValues,
|
||||
] = await this._devUtils.decodeERC1155AssetData(assetData).callAsync();
|
||||
const [tokenAddress, tokenIds, tokenValues] = this._assetDataDecoder.getABIDecodedTransactionData<
|
||||
[string, BigNumber[], BigNumber[]]
|
||||
>('ERC1155Assets', assetData);
|
||||
const fromBalances = {
|
||||
// tslint:disable-next-line:no-inferred-empty-object-type
|
||||
fungible: _.get(this.balances.erc1155, [fromAddress, tokenAddress, 'fungible'], {}),
|
||||
@@ -154,10 +169,9 @@ export class LocalBalanceStore extends BalanceStore {
|
||||
break;
|
||||
}
|
||||
case AssetProxyId.MultiAsset: {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
const [_proxyId, amounts, nestedAssetData] = await this._devUtils
|
||||
.decodeMultiAssetData(assetData)
|
||||
.callAsync();
|
||||
const [amounts, nestedAssetData] = this._assetDataDecoder.getABIDecodedTransactionData<
|
||||
[BigNumber[], string[]]
|
||||
>('MultiAsset', assetData);
|
||||
for (const [i, amt] of amounts.entries()) {
|
||||
const nestedAmount = amount.times(amt);
|
||||
await this.transferAssetAsync(fromAddress, toAddress, nestedAmount, nestedAssetData[i]);
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
artifacts as assetProxyArtifacts,
|
||||
ERC1155ProxyContract,
|
||||
ERC20BridgeProxyContract,
|
||||
ERC20ProxyContract,
|
||||
ERC721ProxyContract,
|
||||
IAssetDataContract,
|
||||
@@ -85,6 +86,7 @@ interface AssetProxyContracts {
|
||||
erc1155Proxy: ERC1155ProxyContract;
|
||||
multiAssetProxy: MultiAssetProxyContract;
|
||||
staticCallProxy: StaticCallProxyContract;
|
||||
erc20BridgeProxy: ERC20BridgeProxyContract;
|
||||
}
|
||||
|
||||
// Contract wrappers for all of the staking contracts
|
||||
@@ -189,6 +191,7 @@ export class DeploymentManager {
|
||||
assetProxies.erc721Proxy,
|
||||
assetProxies.erc1155Proxy,
|
||||
assetProxies.multiAssetProxy,
|
||||
assetProxies.erc20BridgeProxy,
|
||||
exchange,
|
||||
staking.stakingProxy,
|
||||
]);
|
||||
@@ -232,6 +235,7 @@ export class DeploymentManager {
|
||||
assetProxies.erc1155Proxy.address,
|
||||
assetProxies.multiAssetProxy.address,
|
||||
assetProxies.staticCallProxy.address,
|
||||
assetProxies.erc20BridgeProxy.address,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -244,13 +248,19 @@ export class DeploymentManager {
|
||||
assetProxies.erc721Proxy.address,
|
||||
assetProxies.erc1155Proxy.address,
|
||||
assetProxies.staticCallProxy.address,
|
||||
assetProxies.erc20BridgeProxy.address,
|
||||
],
|
||||
);
|
||||
|
||||
// Add the multi-asset proxy as an authorized address of the token proxies.
|
||||
await batchAddAuthorizedAddressAsync(
|
||||
owner,
|
||||
[assetProxies.erc20Proxy, assetProxies.erc721Proxy, assetProxies.erc1155Proxy],
|
||||
[
|
||||
assetProxies.erc20Proxy,
|
||||
assetProxies.erc721Proxy,
|
||||
assetProxies.erc1155Proxy,
|
||||
assetProxies.erc20BridgeProxy,
|
||||
],
|
||||
[assetProxies.multiAssetProxy.address],
|
||||
);
|
||||
|
||||
@@ -262,6 +272,7 @@ export class DeploymentManager {
|
||||
assetProxies.erc721Proxy,
|
||||
assetProxies.erc1155Proxy,
|
||||
assetProxies.multiAssetProxy,
|
||||
assetProxies.erc20BridgeProxy,
|
||||
],
|
||||
[exchange.address],
|
||||
);
|
||||
@@ -327,12 +338,19 @@ export class DeploymentManager {
|
||||
txDefaults,
|
||||
assetProxyArtifacts,
|
||||
);
|
||||
const erc20BridgeProxy = await ERC20BridgeProxyContract.deployFrom0xArtifactAsync(
|
||||
assetProxyArtifacts.ERC20BridgeProxy,
|
||||
environment.provider,
|
||||
txDefaults,
|
||||
assetProxyArtifacts,
|
||||
);
|
||||
return {
|
||||
erc20Proxy,
|
||||
erc721Proxy,
|
||||
erc1155Proxy,
|
||||
multiAssetProxy,
|
||||
staticCallProxy,
|
||||
erc20BridgeProxy,
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -3,4 +3,9 @@
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
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_uniswap_bridge';
|
||||
export * from '../test/generated-wrappers/test_uniswap_exchange';
|
||||
export * from '../test/generated-wrappers/test_uniswap_exchange_factory';
|
||||
|
@@ -2,5 +2,13 @@
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": ["generated-artifacts/TestFramework.json", "test/generated-artifacts/TestFramework.json"]
|
||||
"files": [
|
||||
"generated-artifacts/TestFramework.json",
|
||||
"test/generated-artifacts/TestEth2Dai.json",
|
||||
"test/generated-artifacts/TestEth2DaiBridge.json",
|
||||
"test/generated-artifacts/TestFramework.json",
|
||||
"test/generated-artifacts/TestUniswapBridge.json",
|
||||
"test/generated-artifacts/TestUniswapExchange.json",
|
||||
"test/generated-artifacts/TestUniswapExchangeFactory.json"
|
||||
]
|
||||
}
|
||||
|
Reference in New Issue
Block a user