Merge pull request #2356 from 0xProject/feature/forwarder/erc20-bridge-buy
`@0x/contracts-exchange-forwarder`: ERC20Bridge buy support in Forwarder
This commit is contained in:
		@@ -2,6 +2,8 @@ export {
 | 
			
		||||
    DummyERC721ReceiverContract,
 | 
			
		||||
    DummyERC721TokenContract,
 | 
			
		||||
    ERC721TokenContract,
 | 
			
		||||
    ERC721TokenEvents,
 | 
			
		||||
    ERC721TokenTransferEventArgs,
 | 
			
		||||
    IERC721ReceiverContract,
 | 
			
		||||
} from './wrappers';
 | 
			
		||||
export { artifacts } from './artifacts';
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,13 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "version": "3.1.0-beta.4",
 | 
			
		||||
        "changes": [
 | 
			
		||||
            {
 | 
			
		||||
                "note": "Added buy support for ERC20Bridge",
 | 
			
		||||
                "pr": 2356
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "version": "3.1.0-beta.3",
 | 
			
		||||
        "changes": [
 | 
			
		||||
 
 | 
			
		||||
@@ -32,13 +32,13 @@ contract Forwarder is
 | 
			
		||||
{
 | 
			
		||||
    constructor (
 | 
			
		||||
        address _exchange,
 | 
			
		||||
        bytes memory _wethAssetData
 | 
			
		||||
        address _weth
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        Ownable()
 | 
			
		||||
        LibConstants(
 | 
			
		||||
            _exchange,
 | 
			
		||||
            _wethAssetData
 | 
			
		||||
            _weth
 | 
			
		||||
        )
 | 
			
		||||
        MixinForwarderCore()
 | 
			
		||||
    {}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,9 +21,9 @@ pragma solidity ^0.5.9;
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
 | 
			
		||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
 | 
			
		||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
 | 
			
		||||
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
 | 
			
		||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
 | 
			
		||||
import "./libs/LibConstants.sol";
 | 
			
		||||
import "./libs/LibForwarderRichErrors.sol";
 | 
			
		||||
import "./interfaces/IAssets.sol";
 | 
			
		||||
@@ -36,13 +36,10 @@ contract MixinAssets is
 | 
			
		||||
{
 | 
			
		||||
    using LibBytes for bytes;
 | 
			
		||||
 | 
			
		||||
    bytes4 constant internal ERC20_TRANSFER_SELECTOR = bytes4(keccak256("transfer(address,uint256)"));
 | 
			
		||||
 | 
			
		||||
    /// @dev Withdraws assets from this contract. The contract formerly required a ZRX balance in order
 | 
			
		||||
    ///      to function optimally, and this function allows the ZRX to be withdrawn by owner.
 | 
			
		||||
    ///      It may also be used to withdraw assets that were accidentally sent to this contract.
 | 
			
		||||
    /// @dev Withdraws assets from this contract. It may be used by the owner to withdraw assets
 | 
			
		||||
    ///      that were accidentally sent to this contract.
 | 
			
		||||
    /// @param assetData Byte array encoded for the respective asset proxy.
 | 
			
		||||
    /// @param amount Amount of ERC20 token to withdraw.
 | 
			
		||||
    /// @param amount Amount of the asset to withdraw.
 | 
			
		||||
    function withdrawAsset(
 | 
			
		||||
        bytes calldata assetData,
 | 
			
		||||
        uint256 amount
 | 
			
		||||
@@ -63,14 +60,16 @@ contract MixinAssets is
 | 
			
		||||
        external
 | 
			
		||||
    {
 | 
			
		||||
        bytes4 proxyId = assetData.readBytes4(0);
 | 
			
		||||
        bytes4 erc20ProxyId = IAssetData(address(0)).ERC20Token.selector;
 | 
			
		||||
 | 
			
		||||
        // For now we only care about ERC20, since percentage fees on ERC721 tokens are invalid.
 | 
			
		||||
        if (proxyId == ERC20_DATA_ID) {
 | 
			
		||||
            address proxyAddress = EXCHANGE.getAssetProxy(ERC20_DATA_ID);
 | 
			
		||||
        if (proxyId == erc20ProxyId) {
 | 
			
		||||
            address proxyAddress = EXCHANGE.getAssetProxy(erc20ProxyId);
 | 
			
		||||
            if (proxyAddress == address(0)) {
 | 
			
		||||
                LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError());
 | 
			
		||||
            }
 | 
			
		||||
            IERC20Token assetToken = IERC20Token(assetData.readAddress(16));
 | 
			
		||||
            assetToken.approve(proxyAddress, MAX_UINT);
 | 
			
		||||
            address token = assetData.readAddress(16);
 | 
			
		||||
            LibERC20Token.approve(token, proxyAddress, MAX_UINT);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -85,9 +84,12 @@ contract MixinAssets is
 | 
			
		||||
    {
 | 
			
		||||
        bytes4 proxyId = assetData.readBytes4(0);
 | 
			
		||||
 | 
			
		||||
        if (proxyId == ERC20_DATA_ID) {
 | 
			
		||||
        if (
 | 
			
		||||
            proxyId == IAssetData(address(0)).ERC20Token.selector ||
 | 
			
		||||
            proxyId == IAssetData(address(0)).ERC20Bridge.selector
 | 
			
		||||
        ) {
 | 
			
		||||
            _transferERC20Token(assetData, amount);
 | 
			
		||||
        } else if (proxyId == ERC721_DATA_ID) {
 | 
			
		||||
        } else if (proxyId == IAssetData(address(0)).ERC721Token.selector) {
 | 
			
		||||
            _transferERC721Token(assetData, amount);
 | 
			
		||||
        } else {
 | 
			
		||||
            LibRichErrors.rrevert(LibForwarderRichErrors.UnsupportedAssetProxyError(
 | 
			
		||||
@@ -96,7 +98,7 @@ contract MixinAssets is
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Decodes ERC20 assetData and transfers given amount to sender.
 | 
			
		||||
    /// @dev Decodes ERC20 or ERC20Bridge assetData and transfers given amount to sender.
 | 
			
		||||
    /// @param assetData Byte array encoded for the respective asset proxy.
 | 
			
		||||
    /// @param amount Amount of asset to transfer to sender.
 | 
			
		||||
    function _transferERC20Token(
 | 
			
		||||
 
 | 
			
		||||
@@ -19,12 +19,15 @@
 | 
			
		||||
pragma solidity ^0.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
 | 
			
		||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
 | 
			
		||||
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
 | 
			
		||||
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
 | 
			
		||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
 | 
			
		||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
 | 
			
		||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
 | 
			
		||||
import "./libs/LibConstants.sol";
 | 
			
		||||
import "./libs/LibForwarderRichErrors.sol";
 | 
			
		||||
import "./MixinAssets.sol";
 | 
			
		||||
@@ -34,6 +37,7 @@ contract MixinExchangeWrapper is
 | 
			
		||||
    LibConstants,
 | 
			
		||||
    MixinAssets
 | 
			
		||||
{
 | 
			
		||||
    using LibBytes for bytes;
 | 
			
		||||
    using LibSafeMath for uint256;
 | 
			
		||||
 | 
			
		||||
    /// @dev Fills the input order.
 | 
			
		||||
@@ -88,7 +92,10 @@ contract MixinExchangeWrapper is
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        // No taker fee or percentage fee
 | 
			
		||||
        if (order.takerFee == 0 || order.takerFeeAssetData.equals(order.makerAssetData)) {
 | 
			
		||||
        if (
 | 
			
		||||
            order.takerFee == 0 ||
 | 
			
		||||
            _areUnderlyingAssetsEqual(order.takerFeeAssetData, order.makerAssetData)
 | 
			
		||||
        ) {
 | 
			
		||||
            // Attempt to sell the remaining amount of WETH
 | 
			
		||||
            LibFillResults.FillResults memory singleFillResults = _fillOrderNoThrow(
 | 
			
		||||
                order,
 | 
			
		||||
@@ -103,7 +110,7 @@ contract MixinExchangeWrapper is
 | 
			
		||||
            makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount
 | 
			
		||||
                .safeSub(singleFillResults.takerFeePaid);
 | 
			
		||||
        // WETH fee
 | 
			
		||||
        } else if (order.takerFeeAssetData.equals(order.takerAssetData)) {
 | 
			
		||||
        } else if (_areUnderlyingAssetsEqual(order.takerFeeAssetData, order.takerAssetData)) {
 | 
			
		||||
 | 
			
		||||
            // We will first sell WETH as the takerAsset, then use it to pay the takerFee.
 | 
			
		||||
            // This ensures that we reserve enough to pay the taker and protocol fees.
 | 
			
		||||
@@ -150,9 +157,10 @@ contract MixinExchangeWrapper is
 | 
			
		||||
            uint256 totalMakerAssetAcquiredAmount
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        uint256 ordersLength = orders.length;
 | 
			
		||||
        uint256 protocolFee = tx.gasprice.safeMul(EXCHANGE.protocolFeeMultiplier());
 | 
			
		||||
        bytes4 erc20BridgeProxyId = IAssetData(address(0)).ERC20Bridge.selector;
 | 
			
		||||
 | 
			
		||||
        uint256 ordersLength = orders.length;
 | 
			
		||||
        for (uint256 i = 0; i != ordersLength; i++) {
 | 
			
		||||
            // Preemptively skip to avoid division by zero in _marketSellSingleOrder
 | 
			
		||||
            if (orders[i].makerAssetAmount == 0 || orders[i].takerAssetAmount == 0) {
 | 
			
		||||
@@ -164,6 +172,15 @@ contract MixinExchangeWrapper is
 | 
			
		||||
                .safeSub(totalWethSpentAmount)
 | 
			
		||||
                .safeSub(protocolFee);
 | 
			
		||||
 | 
			
		||||
            // If the maker asset is ERC20Bridge, take a snapshot of the Forwarder contract's balance.
 | 
			
		||||
            bytes4 makerAssetProxyId = orders[i].makerAssetData.readBytes4(0);
 | 
			
		||||
            address tokenAddress;
 | 
			
		||||
            uint256 balanceBefore;
 | 
			
		||||
            if (makerAssetProxyId == erc20BridgeProxyId) {
 | 
			
		||||
                tokenAddress = orders[i].makerAssetData.readAddress(16);
 | 
			
		||||
                balanceBefore = IERC20Token(tokenAddress).balanceOf(address(this));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            (
 | 
			
		||||
                uint256 wethSpentAmount,
 | 
			
		||||
                uint256 makerAssetAcquiredAmount
 | 
			
		||||
@@ -173,6 +190,15 @@ contract MixinExchangeWrapper is
 | 
			
		||||
                remainingTakerAssetFillAmount
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            // Account for the ERC20Bridge transfering more of the maker asset than expected.
 | 
			
		||||
            if (makerAssetProxyId == erc20BridgeProxyId) {
 | 
			
		||||
                uint256 balanceAfter = IERC20Token(tokenAddress).balanceOf(address(this));
 | 
			
		||||
                makerAssetAcquiredAmount = LibSafeMath.max256(
 | 
			
		||||
                    balanceAfter.safeSub(balanceBefore),
 | 
			
		||||
                    makerAssetAcquiredAmount
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _transferAssetToSender(orders[i].makerAssetData, makerAssetAcquiredAmount);
 | 
			
		||||
 | 
			
		||||
            totalWethSpentAmount = totalWethSpentAmount
 | 
			
		||||
@@ -206,7 +232,10 @@ contract MixinExchangeWrapper is
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        // No taker fee or WETH fee
 | 
			
		||||
        if (order.takerFee == 0 || order.takerFeeAssetData.equals(order.takerAssetData)) {
 | 
			
		||||
        if (
 | 
			
		||||
            order.takerFee == 0 ||
 | 
			
		||||
            _areUnderlyingAssetsEqual(order.takerFeeAssetData, order.takerAssetData)
 | 
			
		||||
        ) {
 | 
			
		||||
            // Calculate the remaining amount of takerAsset to sell
 | 
			
		||||
            uint256 remainingTakerAssetFillAmount = LibMath.getPartialAmountCeil(
 | 
			
		||||
                order.takerAssetAmount,
 | 
			
		||||
@@ -228,7 +257,7 @@ contract MixinExchangeWrapper is
 | 
			
		||||
 | 
			
		||||
            makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount;
 | 
			
		||||
        // Percentage fee
 | 
			
		||||
        } else if (order.takerFeeAssetData.equals(order.makerAssetData)) {
 | 
			
		||||
        } else if (_areUnderlyingAssetsEqual(order.takerFeeAssetData, order.makerAssetData)) {
 | 
			
		||||
            // Calculate the remaining amount of takerAsset to sell
 | 
			
		||||
            uint256 remainingTakerAssetFillAmount = LibMath.getPartialAmountCeil(
 | 
			
		||||
                order.takerAssetAmount,
 | 
			
		||||
@@ -277,6 +306,8 @@ contract MixinExchangeWrapper is
 | 
			
		||||
            uint256 totalMakerAssetAcquiredAmount
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        bytes4 erc20BridgeProxyId = IAssetData(address(0)).ERC20Bridge.selector;
 | 
			
		||||
 | 
			
		||||
        uint256 ordersLength = orders.length;
 | 
			
		||||
        for (uint256 i = 0; i != ordersLength; i++) {
 | 
			
		||||
            // Preemptively skip to avoid division by zero in _marketBuySingleOrder
 | 
			
		||||
@@ -287,6 +318,15 @@ contract MixinExchangeWrapper is
 | 
			
		||||
            uint256 remainingMakerAssetFillAmount = makerAssetBuyAmount
 | 
			
		||||
                .safeSub(totalMakerAssetAcquiredAmount);
 | 
			
		||||
 | 
			
		||||
            // If the maker asset is ERC20Bridge, take a snapshot of the Forwarder contract's balance.
 | 
			
		||||
            bytes4 makerAssetProxyId = orders[i].makerAssetData.readBytes4(0);
 | 
			
		||||
            address tokenAddress;
 | 
			
		||||
            uint256 balanceBefore;
 | 
			
		||||
            if (makerAssetProxyId == erc20BridgeProxyId) {
 | 
			
		||||
                tokenAddress = orders[i].makerAssetData.readAddress(16);
 | 
			
		||||
                balanceBefore = IERC20Token(tokenAddress).balanceOf(address(this));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            (
 | 
			
		||||
                uint256 wethSpentAmount,
 | 
			
		||||
                uint256 makerAssetAcquiredAmount
 | 
			
		||||
@@ -296,6 +336,15 @@ contract MixinExchangeWrapper is
 | 
			
		||||
                remainingMakerAssetFillAmount
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            // Account for the ERC20Bridge transfering more of the maker asset than expected.
 | 
			
		||||
            if (makerAssetProxyId == erc20BridgeProxyId) {
 | 
			
		||||
                uint256 balanceAfter = IERC20Token(tokenAddress).balanceOf(address(this));
 | 
			
		||||
                makerAssetAcquiredAmount = LibSafeMath.max256(
 | 
			
		||||
                    balanceAfter.safeSub(balanceBefore),
 | 
			
		||||
                    makerAssetAcquiredAmount
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _transferAssetToSender(orders[i].makerAssetData, makerAssetAcquiredAmount);
 | 
			
		||||
 | 
			
		||||
            totalWethSpentAmount = totalWethSpentAmount
 | 
			
		||||
@@ -316,4 +365,36 @@ contract MixinExchangeWrapper is
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Checks whether one asset is effectively equal to another asset.
 | 
			
		||||
    ///      This is the case if they have the same ERC20Proxy/ERC20BridgeProxy asset data, or if
 | 
			
		||||
    ///      one is the ERC20Bridge equivalent of the other.
 | 
			
		||||
    /// @param assetData1 Byte array encoded for the takerFee asset proxy.
 | 
			
		||||
    /// @param assetData2 Byte array encoded for the maker asset proxy.
 | 
			
		||||
    /// @return areEqual Whether or not the underlying assets are equal.
 | 
			
		||||
    function _areUnderlyingAssetsEqual(
 | 
			
		||||
        bytes memory assetData1,
 | 
			
		||||
        bytes memory assetData2
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        pure
 | 
			
		||||
        returns (bool)
 | 
			
		||||
    {
 | 
			
		||||
        bytes4 assetProxyId1 = assetData1.readBytes4(0);
 | 
			
		||||
        bytes4 assetProxyId2 = assetData2.readBytes4(0);
 | 
			
		||||
        bytes4 erc20ProxyId = IAssetData(address(0)).ERC20Token.selector;
 | 
			
		||||
        bytes4 erc20BridgeProxyId = IAssetData(address(0)).ERC20Bridge.selector;
 | 
			
		||||
 | 
			
		||||
        if (
 | 
			
		||||
            (assetProxyId1 == erc20ProxyId || assetProxyId1 == erc20BridgeProxyId) &&
 | 
			
		||||
            (assetProxyId2 == erc20ProxyId || assetProxyId2 == erc20BridgeProxyId)
 | 
			
		||||
        ) {
 | 
			
		||||
            // Compare the underlying token addresses.
 | 
			
		||||
            address token1 = assetData1.readAddress(16);
 | 
			
		||||
            address token2 = assetData2.readAddress(16);
 | 
			
		||||
            return (token1 == token2);
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
 | 
			
		||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
 | 
			
		||||
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
 | 
			
		||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
 | 
			
		||||
import "./libs/LibConstants.sol";
 | 
			
		||||
import "./libs/LibForwarderRichErrors.sol";
 | 
			
		||||
import "./interfaces/IAssets.sol";
 | 
			
		||||
@@ -46,7 +47,7 @@ contract MixinForwarderCore is
 | 
			
		||||
    constructor ()
 | 
			
		||||
        public
 | 
			
		||||
    {
 | 
			
		||||
        address proxyAddress = EXCHANGE.getAssetProxy(ERC20_DATA_ID);
 | 
			
		||||
        address proxyAddress = EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC20Token.selector);
 | 
			
		||||
        if (proxyAddress == address(0)) {
 | 
			
		||||
            LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError());
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -27,8 +27,6 @@ contract LibConstants {
 | 
			
		||||
 | 
			
		||||
    using LibBytes for bytes;
 | 
			
		||||
 | 
			
		||||
    bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256("ERC20Token(address)"));
 | 
			
		||||
    bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256("ERC721Token(address,uint256)"));
 | 
			
		||||
    uint256 constant internal MAX_UINT = 2**256 - 1;
 | 
			
		||||
    uint256 constant internal PERCENTAGE_DENOMINATOR = 10**18;
 | 
			
		||||
    uint256 constant internal MAX_FEE_PERCENTAGE = 5 * PERCENTAGE_DENOMINATOR / 100;         // 5%
 | 
			
		||||
@@ -36,19 +34,15 @@ contract LibConstants {
 | 
			
		||||
     // solhint-disable var-name-mixedcase
 | 
			
		||||
    IExchange internal EXCHANGE;
 | 
			
		||||
    IEtherToken internal ETHER_TOKEN;
 | 
			
		||||
    bytes internal WETH_ASSET_DATA;
 | 
			
		||||
    // solhint-enable var-name-mixedcase
 | 
			
		||||
 | 
			
		||||
    constructor (
 | 
			
		||||
        address _exchange,
 | 
			
		||||
        bytes memory _wethAssetData
 | 
			
		||||
        address _weth
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
    {
 | 
			
		||||
        EXCHANGE = IExchange(_exchange);
 | 
			
		||||
        WETH_ASSET_DATA = _wethAssetData;
 | 
			
		||||
 | 
			
		||||
        address etherToken = _wethAssetData.readAddress(16);
 | 
			
		||||
        ETHER_TOKEN = IEtherToken(etherToken);
 | 
			
		||||
        ETHER_TOKEN = IEtherToken(_weth);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,8 +18,6 @@
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.5.9;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
library LibForwarderRichErrors {
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,63 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  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 "../src/MixinExchangeWrapper.sol";
 | 
			
		||||
import "../src/libs/LibConstants.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract TestForwarder is
 | 
			
		||||
    LibConstants,
 | 
			
		||||
    MixinExchangeWrapper
 | 
			
		||||
{
 | 
			
		||||
    // solhint-disable no-empty-blocks
 | 
			
		||||
    constructor ()
 | 
			
		||||
        public
 | 
			
		||||
        LibConstants(
 | 
			
		||||
            address(0),
 | 
			
		||||
            address(0)
 | 
			
		||||
        )
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    function areUnderlyingAssetsEqual(
 | 
			
		||||
        bytes memory assetData1,
 | 
			
		||||
        bytes memory assetData2
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        returns (bool)
 | 
			
		||||
    {
 | 
			
		||||
        return _areUnderlyingAssetsEqual(
 | 
			
		||||
            assetData1,
 | 
			
		||||
            assetData2
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function transferAssetToSender(
 | 
			
		||||
        bytes memory assetData,
 | 
			
		||||
        uint256 amount
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
    {
 | 
			
		||||
        _transferAssetToSender(
 | 
			
		||||
            assetData,
 | 
			
		||||
            amount
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -14,11 +14,12 @@
 | 
			
		||||
        "build:ts": "tsc -b",
 | 
			
		||||
        "build:ci": "yarn build",
 | 
			
		||||
        "pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
 | 
			
		||||
        "test": "echo !!! Tests have been relocated to @0x/contracts-integrations !!!",
 | 
			
		||||
        "test": "yarn run_mocha",
 | 
			
		||||
        "rebuild_and_test": "run-s build test",
 | 
			
		||||
        "test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
 | 
			
		||||
        "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
 | 
			
		||||
        "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
 | 
			
		||||
        "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
 | 
			
		||||
        "compile": "sol-compiler",
 | 
			
		||||
        "watch": "sol-compiler -w",
 | 
			
		||||
        "clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
 | 
			
		||||
@@ -38,7 +39,7 @@
 | 
			
		||||
    },
 | 
			
		||||
    "config": {
 | 
			
		||||
        "publicInterfaceContracts": "Forwarder",
 | 
			
		||||
        "abis": "./test/generated-artifacts/@(Forwarder|IAssets|IForwarder|IForwarderCore|LibConstants|LibForwarderRichErrors|MixinAssets|MixinExchangeWrapper|MixinForwarderCore|MixinWeth).json",
 | 
			
		||||
        "abis": "./test/generated-artifacts/@(Forwarder|IAssets|IForwarder|IForwarderCore|LibConstants|LibForwarderRichErrors|MixinAssets|MixinExchangeWrapper|MixinForwarderCore|MixinWeth|TestForwarder).json",
 | 
			
		||||
        "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
 | 
			
		||||
    },
 | 
			
		||||
    "repository": {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ import * as MixinAssets from '../test/generated-artifacts/MixinAssets.json';
 | 
			
		||||
import * as MixinExchangeWrapper from '../test/generated-artifacts/MixinExchangeWrapper.json';
 | 
			
		||||
import * as MixinForwarderCore from '../test/generated-artifacts/MixinForwarderCore.json';
 | 
			
		||||
import * as MixinWeth from '../test/generated-artifacts/MixinWeth.json';
 | 
			
		||||
import * as TestForwarder from '../test/generated-artifacts/TestForwarder.json';
 | 
			
		||||
export const artifacts = {
 | 
			
		||||
    Forwarder: Forwarder as ContractArtifact,
 | 
			
		||||
    MixinAssets: MixinAssets as ContractArtifact,
 | 
			
		||||
@@ -26,4 +27,5 @@ export const artifacts = {
 | 
			
		||||
    IForwarderCore: IForwarderCore as ContractArtifact,
 | 
			
		||||
    LibConstants: LibConstants as ContractArtifact,
 | 
			
		||||
    LibForwarderRichErrors: LibForwarderRichErrors as ContractArtifact,
 | 
			
		||||
    TestForwarder: TestForwarder as ContractArtifact,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										184
									
								
								contracts/exchange-forwarder/test/asset_test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								contracts/exchange-forwarder/test/asset_test.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,184 @@
 | 
			
		||||
import { IAssetDataContract } from '@0x/contracts-asset-proxy';
 | 
			
		||||
import {
 | 
			
		||||
    artifacts as ERC20Artifacts,
 | 
			
		||||
    DummyERC20TokenContract,
 | 
			
		||||
    ERC20TokenEvents,
 | 
			
		||||
    ERC20TokenTransferEventArgs,
 | 
			
		||||
} from '@0x/contracts-erc20';
 | 
			
		||||
import {
 | 
			
		||||
    artifacts as ERC721Artifacts,
 | 
			
		||||
    DummyERC721TokenContract,
 | 
			
		||||
    ERC721TokenEvents,
 | 
			
		||||
    ERC721TokenTransferEventArgs,
 | 
			
		||||
} from '@0x/contracts-erc721';
 | 
			
		||||
import {
 | 
			
		||||
    blockchainTests,
 | 
			
		||||
    constants,
 | 
			
		||||
    expect,
 | 
			
		||||
    getRandomInteger,
 | 
			
		||||
    hexRandom,
 | 
			
		||||
    hexSlice,
 | 
			
		||||
    randomAddress,
 | 
			
		||||
    verifyEventsFromLogs,
 | 
			
		||||
} from '@0x/contracts-test-utils';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
 | 
			
		||||
import { ForwarderRevertErrors } from '../src';
 | 
			
		||||
 | 
			
		||||
import { artifacts } from './artifacts';
 | 
			
		||||
import { TestForwarderContract } from './wrappers';
 | 
			
		||||
 | 
			
		||||
blockchainTests('Supported asset type unit tests', env => {
 | 
			
		||||
    let forwarder: TestForwarderContract;
 | 
			
		||||
    let assetDataEncoder: IAssetDataContract;
 | 
			
		||||
    let bridgeAddress: string;
 | 
			
		||||
    let bridgeData: string;
 | 
			
		||||
    let receiver: string;
 | 
			
		||||
 | 
			
		||||
    let erc20Token: DummyERC20TokenContract;
 | 
			
		||||
    let erc721Token: DummyERC721TokenContract;
 | 
			
		||||
    let nftId: BigNumber;
 | 
			
		||||
 | 
			
		||||
    let erc20AssetData: string;
 | 
			
		||||
    let erc721AssetData: string;
 | 
			
		||||
    let erc20BridgeAssetData: string;
 | 
			
		||||
 | 
			
		||||
    before(async () => {
 | 
			
		||||
        [receiver] = await env.getAccountAddressesAsync();
 | 
			
		||||
        assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, env.provider);
 | 
			
		||||
 | 
			
		||||
        forwarder = await TestForwarderContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            artifacts.TestForwarder,
 | 
			
		||||
            env.provider,
 | 
			
		||||
            env.txDefaults,
 | 
			
		||||
            { ...artifacts, ...ERC20Artifacts, ...ERC721Artifacts },
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        erc20Token = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            ERC20Artifacts.DummyERC20Token,
 | 
			
		||||
            env.provider,
 | 
			
		||||
            env.txDefaults,
 | 
			
		||||
            ERC20Artifacts,
 | 
			
		||||
            constants.DUMMY_TOKEN_NAME,
 | 
			
		||||
            constants.DUMMY_TOKEN_SYMBOL,
 | 
			
		||||
            constants.DUMMY_TOKEN_DECIMALS,
 | 
			
		||||
            constants.DUMMY_TOKEN_TOTAL_SUPPLY,
 | 
			
		||||
        );
 | 
			
		||||
        erc20AssetData = assetDataEncoder.ERC20Token(erc20Token.address).getABIEncodedTransactionData();
 | 
			
		||||
 | 
			
		||||
        erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            ERC721Artifacts.DummyERC721Token,
 | 
			
		||||
            env.provider,
 | 
			
		||||
            env.txDefaults,
 | 
			
		||||
            ERC721Artifacts,
 | 
			
		||||
            constants.DUMMY_TOKEN_NAME,
 | 
			
		||||
            constants.DUMMY_TOKEN_SYMBOL,
 | 
			
		||||
        );
 | 
			
		||||
        nftId = getRandomInteger(constants.ZERO_AMOUNT, constants.MAX_UINT256);
 | 
			
		||||
        erc721AssetData = assetDataEncoder.ERC721Token(erc721Token.address, nftId).getABIEncodedTransactionData();
 | 
			
		||||
 | 
			
		||||
        bridgeAddress = randomAddress();
 | 
			
		||||
        bridgeData = hexRandom();
 | 
			
		||||
        erc20BridgeAssetData = assetDataEncoder
 | 
			
		||||
            .ERC20Bridge(erc20Token.address, bridgeAddress, bridgeData)
 | 
			
		||||
            .getABIEncodedTransactionData();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe('_areUnderlyingAssetsEqual', () => {
 | 
			
		||||
        it('returns true if assetData1 == assetData2 are ERC20', async () => {
 | 
			
		||||
            const result = await forwarder.areUnderlyingAssetsEqual(erc20AssetData, erc20AssetData).callAsync();
 | 
			
		||||
            expect(result).to.be.true();
 | 
			
		||||
        });
 | 
			
		||||
        it('returns true if assetData1 == assetData2 are ERC20Bridge', async () => {
 | 
			
		||||
            const result = await forwarder
 | 
			
		||||
                .areUnderlyingAssetsEqual(erc20BridgeAssetData, erc20BridgeAssetData)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(result).to.be.true();
 | 
			
		||||
        });
 | 
			
		||||
        it('returns true if assetData2 is the ERC20Bridge equivalent of assetData1', async () => {
 | 
			
		||||
            const result = await forwarder.areUnderlyingAssetsEqual(erc20AssetData, erc20BridgeAssetData).callAsync();
 | 
			
		||||
            expect(result).to.be.true();
 | 
			
		||||
        });
 | 
			
		||||
        it('returns false if assetData1 != assetData2 are ERC20', async () => {
 | 
			
		||||
            const differentERC20AssetData = assetDataEncoder.ERC20Token(randomAddress()).getABIEncodedTransactionData();
 | 
			
		||||
            const result = await forwarder
 | 
			
		||||
                .areUnderlyingAssetsEqual(erc20AssetData, differentERC20AssetData)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(result).to.be.false();
 | 
			
		||||
        });
 | 
			
		||||
        it('returns false if assetData1 is ERC20 and assetData2 is ERC721', async () => {
 | 
			
		||||
            const result = await forwarder.areUnderlyingAssetsEqual(erc20AssetData, erc721AssetData).callAsync();
 | 
			
		||||
            expect(result).to.be.false();
 | 
			
		||||
        });
 | 
			
		||||
        it('returns false if assetData2 is ERC20Bridge, but for a different token than assetData1', async () => {
 | 
			
		||||
            const mismatchedErc20BridgeAssetData = assetDataEncoder
 | 
			
		||||
                .ERC20Bridge(randomAddress(), bridgeAddress, bridgeData)
 | 
			
		||||
                .getABIEncodedTransactionData();
 | 
			
		||||
            const result = await forwarder
 | 
			
		||||
                .areUnderlyingAssetsEqual(erc20AssetData, mismatchedErc20BridgeAssetData)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(result).to.be.false();
 | 
			
		||||
        });
 | 
			
		||||
        it('returns false if assetData1 == assetData2 are ERC721', async () => {
 | 
			
		||||
            const result = await forwarder.areUnderlyingAssetsEqual(erc721AssetData, erc721AssetData).callAsync();
 | 
			
		||||
            expect(result).to.be.false();
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe('_transferAssetToSender', () => {
 | 
			
		||||
        const TRANSFER_AMOUNT = new BigNumber(1);
 | 
			
		||||
        before(async () => {
 | 
			
		||||
            await erc20Token
 | 
			
		||||
                .setBalance(forwarder.address, constants.INITIAL_ERC20_BALANCE)
 | 
			
		||||
                .awaitTransactionSuccessAsync();
 | 
			
		||||
            await erc721Token.mint(forwarder.address, nftId).awaitTransactionSuccessAsync();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('transfers an ERC20 token given ERC20 assetData', async () => {
 | 
			
		||||
            const txReceipt = await forwarder
 | 
			
		||||
                .transferAssetToSender(erc20AssetData, TRANSFER_AMOUNT)
 | 
			
		||||
                .awaitTransactionSuccessAsync({ from: receiver });
 | 
			
		||||
            verifyEventsFromLogs<ERC20TokenTransferEventArgs>(
 | 
			
		||||
                txReceipt.logs,
 | 
			
		||||
                [{ _from: forwarder.address, _to: receiver, _value: TRANSFER_AMOUNT }],
 | 
			
		||||
                ERC20TokenEvents.Transfer,
 | 
			
		||||
            );
 | 
			
		||||
        });
 | 
			
		||||
        it('transfers an ERC721 token given ERC721 assetData and amount == 1', async () => {
 | 
			
		||||
            const txReceipt = await forwarder
 | 
			
		||||
                .transferAssetToSender(erc721AssetData, TRANSFER_AMOUNT)
 | 
			
		||||
                .awaitTransactionSuccessAsync({ from: receiver });
 | 
			
		||||
            verifyEventsFromLogs<ERC721TokenTransferEventArgs>(
 | 
			
		||||
                txReceipt.logs,
 | 
			
		||||
                [{ _from: forwarder.address, _to: receiver, _tokenId: nftId }],
 | 
			
		||||
                ERC721TokenEvents.Transfer,
 | 
			
		||||
            );
 | 
			
		||||
        });
 | 
			
		||||
        it('reverts if attempting to transfer an ERC721 token with amount != 1', async () => {
 | 
			
		||||
            const invalidAmount = new BigNumber(2);
 | 
			
		||||
            const tx = forwarder
 | 
			
		||||
                .transferAssetToSender(erc721AssetData, invalidAmount)
 | 
			
		||||
                .awaitTransactionSuccessAsync({ from: receiver });
 | 
			
		||||
            const expectedError = new ForwarderRevertErrors.Erc721AmountMustEqualOneError(invalidAmount);
 | 
			
		||||
            return expect(tx).to.revertWith(expectedError);
 | 
			
		||||
        });
 | 
			
		||||
        it('transfers an ERC20 token given ERC20Bridge assetData', async () => {
 | 
			
		||||
            const txReceipt = await forwarder
 | 
			
		||||
                .transferAssetToSender(erc20BridgeAssetData, TRANSFER_AMOUNT)
 | 
			
		||||
                .awaitTransactionSuccessAsync({ from: receiver });
 | 
			
		||||
            verifyEventsFromLogs<ERC20TokenTransferEventArgs>(
 | 
			
		||||
                txReceipt.logs,
 | 
			
		||||
                [{ _from: forwarder.address, _to: receiver, _value: TRANSFER_AMOUNT }],
 | 
			
		||||
                ERC20TokenEvents.Transfer,
 | 
			
		||||
            );
 | 
			
		||||
        });
 | 
			
		||||
        it('reverts if assetData is unsupported', async () => {
 | 
			
		||||
            const randomBytes = hexRandom();
 | 
			
		||||
            const tx = forwarder
 | 
			
		||||
                .transferAssetToSender(randomBytes, TRANSFER_AMOUNT)
 | 
			
		||||
                .awaitTransactionSuccessAsync({ from: receiver });
 | 
			
		||||
            const expectedError = new ForwarderRevertErrors.UnsupportedAssetProxyError(hexSlice(randomBytes, 0, 4));
 | 
			
		||||
            return expect(tx).to.revertWith(expectedError);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -13,3 +13,4 @@ export * from '../test/generated-wrappers/mixin_assets';
 | 
			
		||||
export * from '../test/generated-wrappers/mixin_exchange_wrapper';
 | 
			
		||||
export * from '../test/generated-wrappers/mixin_forwarder_core';
 | 
			
		||||
export * from '../test/generated-wrappers/mixin_weth';
 | 
			
		||||
export * from '../test/generated-wrappers/test_forwarder';
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,8 @@
 | 
			
		||||
        "test/generated-artifacts/MixinAssets.json",
 | 
			
		||||
        "test/generated-artifacts/MixinExchangeWrapper.json",
 | 
			
		||||
        "test/generated-artifacts/MixinForwarderCore.json",
 | 
			
		||||
        "test/generated-artifacts/MixinWeth.json"
 | 
			
		||||
        "test/generated-artifacts/MixinWeth.json",
 | 
			
		||||
        "test/generated-artifacts/TestForwarder.json"
 | 
			
		||||
    ],
 | 
			
		||||
    "exclude": ["./deploy/solc/solc_bin"]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,13 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "version": "1.0.3-beta.2",
 | 
			
		||||
        "changes": [
 | 
			
		||||
            {
 | 
			
		||||
                "note": "Forwader <> ERC20Bridge integration tests",
 | 
			
		||||
                "pr": 2356
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "version": "1.0.3-beta.1",
 | 
			
		||||
        "changes": [
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										59
									
								
								contracts/integrations/contracts/test/TestEth2Dai.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								contracts/integrations/contracts/test/TestEth2Dai.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/interfaces/IEth2Dai.sol";
 | 
			
		||||
import "@0x/contracts-erc20/contracts/test/DummyERC20Token.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract TestEth2Dai is
 | 
			
		||||
    IEth2Dai
 | 
			
		||||
{
 | 
			
		||||
    uint256 private _excessBuyAmount;
 | 
			
		||||
 | 
			
		||||
    function setExcessBuyAmount(uint256 amount)
 | 
			
		||||
        external
 | 
			
		||||
    {
 | 
			
		||||
        _excessBuyAmount = amount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 + _excessBuyAmount);
 | 
			
		||||
        buyToken.transfer(
 | 
			
		||||
            msg.sender,
 | 
			
		||||
            minimumFillAmount + _excessBuyAmount
 | 
			
		||||
        );
 | 
			
		||||
        return minimumFillAmount + _excessBuyAmount;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										109
									
								
								contracts/integrations/contracts/test/TestUniswapExchange.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								contracts/integrations/contracts/test/TestUniswapExchange.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
    uint256 private _excessBuyAmount;
 | 
			
		||||
 | 
			
		||||
    constructor(address _tokenAddress) public {
 | 
			
		||||
        token = DummyERC20Token(_tokenAddress);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // solhint-disable no-empty-blocks
 | 
			
		||||
    /// @dev Used to receive ETH for testing.
 | 
			
		||||
    function topUpEth()
 | 
			
		||||
        external
 | 
			
		||||
        payable
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    function setExcessBuyAmount(uint256 amount)
 | 
			
		||||
        external
 | 
			
		||||
    {
 | 
			
		||||
        _excessBuyAmount = amount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function ethToTokenTransferInput(
 | 
			
		||||
        uint256 minTokensBought,
 | 
			
		||||
        uint256, /* deadline */
 | 
			
		||||
        address recipient
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        payable
 | 
			
		||||
        returns (uint256 tokensBought)
 | 
			
		||||
    {
 | 
			
		||||
        token.mint(minTokensBought + _excessBuyAmount);
 | 
			
		||||
        token.transfer(recipient, minTokensBought + _excessBuyAmount);
 | 
			
		||||
        return minTokensBought + _excessBuyAmount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function tokenToEthSwapInput(
 | 
			
		||||
        uint256 tokensSold,
 | 
			
		||||
        uint256 minEthBought,
 | 
			
		||||
        uint256 /* deadline */
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256 ethBought)
 | 
			
		||||
    {
 | 
			
		||||
        token.transferFrom(
 | 
			
		||||
            msg.sender,
 | 
			
		||||
            address(this),
 | 
			
		||||
            tokensSold
 | 
			
		||||
        );
 | 
			
		||||
        msg.sender.transfer(minEthBought + _excessBuyAmount);
 | 
			
		||||
        return minEthBought + _excessBuyAmount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 + _excessBuyAmount);
 | 
			
		||||
        toToken.transfer(recipient, minTokensBought + _excessBuyAmount);
 | 
			
		||||
        return minTokensBought + _excessBuyAmount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function toTokenAddress()
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (address _tokenAddress)
 | 
			
		||||
    {
 | 
			
		||||
        return address(token);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,52 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  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";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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 (address)
 | 
			
		||||
    {
 | 
			
		||||
        return _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,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										31
									
								
								contracts/integrations/test/bridges/deploy_eth2dai_bridge.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								contracts/integrations/test/bridges/deploy_eth2dai_bridge.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
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';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Deploys test Eth2Dai exchange and bridge contracts configured to work alongside the provided `deployment`.
 | 
			
		||||
 */
 | 
			
		||||
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];
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										51
									
								
								contracts/integrations/test/bridges/deploy_uniswap_bridge.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								contracts/integrations/test/bridges/deploy_uniswap_bridge.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
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';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Deploys test Uniswap exchanges for the given tokens, a test UniswapExchangeFactory, and a test
 | 
			
		||||
 * bridge contract configured to work alongside the provided `deployment`.
 | 
			
		||||
 */
 | 
			
		||||
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];
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
import { CoordinatorContract, CoordinatorRevertErrors, SignedCoordinatorApproval } from '@0x/contracts-coordinator';
 | 
			
		||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
 | 
			
		||||
import {
 | 
			
		||||
    ExchangeCancelEventArgs,
 | 
			
		||||
    ExchangeCancelUpToEventArgs,
 | 
			
		||||
@@ -15,7 +14,6 @@ import {
 | 
			
		||||
    hexConcat,
 | 
			
		||||
    hexSlice,
 | 
			
		||||
    orderHashUtils,
 | 
			
		||||
    provider,
 | 
			
		||||
    transactionHashUtils,
 | 
			
		||||
    verifyEvents,
 | 
			
		||||
} from '@0x/contracts-test-utils';
 | 
			
		||||
@@ -38,7 +36,6 @@ blockchainTests.resets('Coordinator integration tests', env => {
 | 
			
		||||
    let deployment: DeploymentManager;
 | 
			
		||||
    let coordinator: CoordinatorContract;
 | 
			
		||||
    let balanceStore: BlockchainBalanceStore;
 | 
			
		||||
    let devUtils: DevUtilsContract;
 | 
			
		||||
 | 
			
		||||
    let maker: Maker;
 | 
			
		||||
    let taker: Actor;
 | 
			
		||||
@@ -51,7 +48,6 @@ blockchainTests.resets('Coordinator integration tests', env => {
 | 
			
		||||
            numErc1155TokensToDeploy: 0,
 | 
			
		||||
        });
 | 
			
		||||
        coordinator = await deployCoordinatorAsync(deployment, env);
 | 
			
		||||
        devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
 | 
			
		||||
 | 
			
		||||
        const [makerToken, takerToken, makerFeeToken, takerFeeToken] = deployment.tokens.erc20;
 | 
			
		||||
 | 
			
		||||
@@ -109,7 +105,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));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -221,7 +221,6 @@ blockchainTests.resets('Exchange core', () => {
 | 
			
		||||
        };
 | 
			
		||||
        fillOrderWrapper = new FillOrderWrapper(
 | 
			
		||||
            exchange,
 | 
			
		||||
            devUtils,
 | 
			
		||||
            { makerAddress, takerAddress, feeRecipientAddress },
 | 
			
		||||
            tokenContracts,
 | 
			
		||||
            tokenIds,
 | 
			
		||||
 
 | 
			
		||||
@@ -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 () => {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
 | 
			
		||||
import { ExchangeContract } from '@0x/contracts-exchange';
 | 
			
		||||
import { ReferenceFunctions as LibReferenceFunctions } from '@0x/contracts-exchange-libs';
 | 
			
		||||
import {
 | 
			
		||||
@@ -56,65 +55,6 @@ export class FillOrderWrapper {
 | 
			
		||||
        return events.map(event => _.pick(event, fieldsOfInterest)) as FillEventArgs[];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Locally simulates filling an order.
 | 
			
		||||
     * @param txReceipt Transaction receipt from the actual fill, needed to update eth balance
 | 
			
		||||
     * @param signedOrder The order being filled.
 | 
			
		||||
     * @param takerAddress Address of taker (the address who matched the two orders)
 | 
			
		||||
     * @param opts Optionally specifies the amount to fill.
 | 
			
		||||
     * @param initBalanceStore Account balances prior to the fill.
 | 
			
		||||
     * @return The expected account balances, fill results, and fill events.
 | 
			
		||||
     */
 | 
			
		||||
    public async simulateFillOrderAsync(
 | 
			
		||||
        txReceipt: TransactionReceiptWithDecodedLogs,
 | 
			
		||||
        signedOrder: SignedOrder,
 | 
			
		||||
        takerAddress: string,
 | 
			
		||||
        initBalanceStore: BalanceStore,
 | 
			
		||||
        opts: { takerAssetFillAmount?: BigNumber } = {},
 | 
			
		||||
    ): Promise<[FillResults, FillEventArgs, BalanceStore]> {
 | 
			
		||||
        const balanceStore = LocalBalanceStore.create(this._devUtils, initBalanceStore);
 | 
			
		||||
        const takerAssetFillAmount =
 | 
			
		||||
            opts.takerAssetFillAmount !== undefined ? opts.takerAssetFillAmount : signedOrder.takerAssetAmount;
 | 
			
		||||
        // TODO(jalextowle): Change this if the integration tests take protocol fees into account.
 | 
			
		||||
        const fillResults = LibReferenceFunctions.calculateFillResults(
 | 
			
		||||
            signedOrder,
 | 
			
		||||
            takerAssetFillAmount,
 | 
			
		||||
            constants.ZERO_AMOUNT,
 | 
			
		||||
            constants.ZERO_AMOUNT,
 | 
			
		||||
        );
 | 
			
		||||
        const fillEvent = FillOrderWrapper.simulateFillEvent(signedOrder, takerAddress, fillResults);
 | 
			
		||||
        // Taker -> Maker
 | 
			
		||||
        await balanceStore.transferAssetAsync(
 | 
			
		||||
            takerAddress,
 | 
			
		||||
            signedOrder.makerAddress,
 | 
			
		||||
            fillResults.takerAssetFilledAmount,
 | 
			
		||||
            signedOrder.takerAssetData,
 | 
			
		||||
        );
 | 
			
		||||
        // Maker -> Taker
 | 
			
		||||
        await balanceStore.transferAssetAsync(
 | 
			
		||||
            signedOrder.makerAddress,
 | 
			
		||||
            takerAddress,
 | 
			
		||||
            fillResults.makerAssetFilledAmount,
 | 
			
		||||
            signedOrder.makerAssetData,
 | 
			
		||||
        );
 | 
			
		||||
        // Taker -> Fee Recipient
 | 
			
		||||
        await balanceStore.transferAssetAsync(
 | 
			
		||||
            takerAddress,
 | 
			
		||||
            signedOrder.feeRecipientAddress,
 | 
			
		||||
            fillResults.takerFeePaid,
 | 
			
		||||
            signedOrder.takerFeeAssetData,
 | 
			
		||||
        );
 | 
			
		||||
        // Maker -> Fee Recipient
 | 
			
		||||
        await balanceStore.transferAssetAsync(
 | 
			
		||||
            signedOrder.makerAddress,
 | 
			
		||||
            signedOrder.feeRecipientAddress,
 | 
			
		||||
            fillResults.makerFeePaid,
 | 
			
		||||
            signedOrder.makerFeeAssetData,
 | 
			
		||||
        );
 | 
			
		||||
        balanceStore.burnGas(txReceipt.from, constants.DEFAULT_GAS_PRICE * txReceipt.gasUsed);
 | 
			
		||||
        return [fillResults, fillEvent, balanceStore];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor.
 | 
			
		||||
     * @param exchangeContract Instance of the deployed exchange contract.
 | 
			
		||||
@@ -124,7 +64,6 @@ export class FillOrderWrapper {
 | 
			
		||||
     */
 | 
			
		||||
    public constructor(
 | 
			
		||||
        private readonly _exchange: ExchangeContract,
 | 
			
		||||
        private readonly _devUtils: DevUtilsContract,
 | 
			
		||||
        tokenOwnersByName: TokenOwnersByName,
 | 
			
		||||
        tokenContractsByName: Partial<TokenContractsByName>,
 | 
			
		||||
        tokenIds: Partial<TokenIds>,
 | 
			
		||||
@@ -160,11 +99,13 @@ export class FillOrderWrapper {
 | 
			
		||||
        await this._assertOrderStateAsync(signedOrder, initTakerAssetFilledAmount);
 | 
			
		||||
        // Simulate and execute fill then assert outputs
 | 
			
		||||
        const [fillResults, fillEvent, txReceipt] = await this._fillOrderAsync(signedOrder, from, opts);
 | 
			
		||||
        const [
 | 
			
		||||
            simulatedFillResults,
 | 
			
		||||
            simulatedFillEvent,
 | 
			
		||||
            simulatedFinalBalanceStore,
 | 
			
		||||
        ] = await this.simulateFillOrderAsync(txReceipt, signedOrder, from, this._blockchainBalanceStore, opts);
 | 
			
		||||
        const [simulatedFillResults, simulatedFillEvent, simulatedFinalBalanceStore] = await simulateFillOrderAsync(
 | 
			
		||||
            txReceipt,
 | 
			
		||||
            signedOrder,
 | 
			
		||||
            from,
 | 
			
		||||
            this._blockchainBalanceStore,
 | 
			
		||||
            opts,
 | 
			
		||||
        );
 | 
			
		||||
        // Assert state transition
 | 
			
		||||
        expect(simulatedFillResults, 'Fill Results').to.be.deep.equal(fillResults);
 | 
			
		||||
        expect(simulatedFillEvent, 'Fill Events').to.be.deep.equal(fillEvent);
 | 
			
		||||
@@ -218,3 +159,62 @@ export class FillOrderWrapper {
 | 
			
		||||
        expect(actualStatus, 'order status').to.equal(expectedStatus);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Locally simulates filling an order.
 | 
			
		||||
 * @param txReceipt Transaction receipt from the actual fill, needed to update eth balance
 | 
			
		||||
 * @param signedOrder The order being filled.
 | 
			
		||||
 * @param takerAddress Address of taker (the address who matched the two orders)
 | 
			
		||||
 * @param opts Optionally specifies the amount to fill.
 | 
			
		||||
 * @param initBalanceStore Account balances prior to the fill.
 | 
			
		||||
 * @return The expected account balances, fill results, and fill events.
 | 
			
		||||
 */
 | 
			
		||||
async function simulateFillOrderAsync(
 | 
			
		||||
    txReceipt: TransactionReceiptWithDecodedLogs,
 | 
			
		||||
    signedOrder: SignedOrder,
 | 
			
		||||
    takerAddress: string,
 | 
			
		||||
    initBalanceStore: BalanceStore,
 | 
			
		||||
    opts: { takerAssetFillAmount?: BigNumber } = {},
 | 
			
		||||
): Promise<[FillResults, FillEventArgs, BalanceStore]> {
 | 
			
		||||
    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.
 | 
			
		||||
    const fillResults = LibReferenceFunctions.calculateFillResults(
 | 
			
		||||
        signedOrder,
 | 
			
		||||
        takerAssetFillAmount,
 | 
			
		||||
        constants.ZERO_AMOUNT,
 | 
			
		||||
        constants.ZERO_AMOUNT,
 | 
			
		||||
    );
 | 
			
		||||
    const fillEvent = FillOrderWrapper.simulateFillEvent(signedOrder, takerAddress, fillResults);
 | 
			
		||||
    // Taker -> Maker
 | 
			
		||||
    await balanceStore.transferAssetAsync(
 | 
			
		||||
        takerAddress,
 | 
			
		||||
        signedOrder.makerAddress,
 | 
			
		||||
        fillResults.takerAssetFilledAmount,
 | 
			
		||||
        signedOrder.takerAssetData,
 | 
			
		||||
    );
 | 
			
		||||
    // Maker -> Taker
 | 
			
		||||
    await balanceStore.transferAssetAsync(
 | 
			
		||||
        signedOrder.makerAddress,
 | 
			
		||||
        takerAddress,
 | 
			
		||||
        fillResults.makerAssetFilledAmount,
 | 
			
		||||
        signedOrder.makerAssetData,
 | 
			
		||||
    );
 | 
			
		||||
    // Taker -> Fee Recipient
 | 
			
		||||
    await balanceStore.transferAssetAsync(
 | 
			
		||||
        takerAddress,
 | 
			
		||||
        signedOrder.feeRecipientAddress,
 | 
			
		||||
        fillResults.takerFeePaid,
 | 
			
		||||
        signedOrder.takerFeeAssetData,
 | 
			
		||||
    );
 | 
			
		||||
    // Maker -> Fee Recipient
 | 
			
		||||
    await balanceStore.transferAssetAsync(
 | 
			
		||||
        signedOrder.makerAddress,
 | 
			
		||||
        signedOrder.feeRecipientAddress,
 | 
			
		||||
        fillResults.makerFeePaid,
 | 
			
		||||
        signedOrder.makerFeeAssetData,
 | 
			
		||||
    );
 | 
			
		||||
    balanceStore.burnGas(txReceipt.from, constants.DEFAULT_GAS_PRICE * txReceipt.gasUsed);
 | 
			
		||||
    return [fillResults, fillEvent, balanceStore];
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
 
 | 
			
		||||
@@ -168,7 +168,7 @@ export class MatchOrderTester {
 | 
			
		||||
        // Update the blockchain balance store and create a new local balance store
 | 
			
		||||
        // with the same initial balances.
 | 
			
		||||
        await this._blockchainBalanceStore.updateBalancesAsync();
 | 
			
		||||
        const localBalanceStore = LocalBalanceStore.create(this._deployment.devUtils, this._blockchainBalanceStore);
 | 
			
		||||
        const localBalanceStore = LocalBalanceStore.create(this._blockchainBalanceStore);
 | 
			
		||||
 | 
			
		||||
        // Execute `batchMatchOrders()`
 | 
			
		||||
        let actualBatchMatchResults;
 | 
			
		||||
@@ -253,7 +253,7 @@ export class MatchOrderTester {
 | 
			
		||||
        // Update the blockchain balance store and create a new local balance store
 | 
			
		||||
        // with the same initial balances.
 | 
			
		||||
        await this._blockchainBalanceStore.updateBalancesAsync();
 | 
			
		||||
        const localBalanceStore = LocalBalanceStore.create(this._deployment.devUtils, this._blockchainBalanceStore);
 | 
			
		||||
        const localBalanceStore = LocalBalanceStore.create(this._blockchainBalanceStore);
 | 
			
		||||
 | 
			
		||||
        // Execute `matchOrders()`
 | 
			
		||||
        let actualMatchResults;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										378
									
								
								contracts/integrations/test/forwarder/bridge_test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										378
									
								
								contracts/integrations/test/forwarder/bridge_test.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,378 @@
 | 
			
		||||
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 { TestEth2DaiContract, TestUniswapExchangeContract } from '../wrappers';
 | 
			
		||||
 | 
			
		||||
import { deployForwarderAsync } from './deploy_forwarder';
 | 
			
		||||
import { ForwarderTestFactory } from './forwarder_test_factory';
 | 
			
		||||
 | 
			
		||||
blockchainTests.resets('Forwarder <> ERC20Bridge integration tests', env => {
 | 
			
		||||
    let deployment: DeploymentManager;
 | 
			
		||||
    let balanceStore: BlockchainBalanceStore;
 | 
			
		||||
    let testFactory: ForwarderTestFactory;
 | 
			
		||||
 | 
			
		||||
    let forwarder: ForwarderContract;
 | 
			
		||||
    let assetDataEncoder: IAssetDataContract;
 | 
			
		||||
    let eth2Dai: TestEth2DaiContract;
 | 
			
		||||
    let uniswapExchange: TestUniswapExchangeContract;
 | 
			
		||||
 | 
			
		||||
    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 eth2DaiContracts = await deployEth2DaiBridgeAsync(deployment, env);
 | 
			
		||||
        const [eth2DaiBridge] = eth2DaiContracts;
 | 
			
		||||
        [, eth2Dai] = eth2DaiContracts;
 | 
			
		||||
        const uniswapContracts = await deployUniswapBridgeAsync(deployment, env, [makerToken.address]);
 | 
			
		||||
        const [uniswapBridge] = uniswapContracts;
 | 
			
		||||
        [, [uniswapExchange]] = uniswapContracts;
 | 
			
		||||
 | 
			
		||||
        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 uniswapExchange.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 correctly handle excess maker asset acquired from Eth2Dai', async () => {
 | 
			
		||||
            const bridgeExcessBuyAmount = new BigNumber(1);
 | 
			
		||||
            await eth2Dai.setExcessBuyAmount(bridgeExcessBuyAmount).awaitTransactionSuccessAsync();
 | 
			
		||||
            await testFactory.marketSellTestAsync([eth2DaiBridgeOrder], 0.34, { bridgeExcessBuyAmount });
 | 
			
		||||
        });
 | 
			
		||||
        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 correctly handle excess maker asset acquired from Uniswap', async () => {
 | 
			
		||||
            const bridgeExcessBuyAmount = new BigNumber(1);
 | 
			
		||||
            await uniswapExchange.setExcessBuyAmount(bridgeExcessBuyAmount).awaitTransactionSuccessAsync();
 | 
			
		||||
            await testFactory.marketSellTestAsync([uniswapBridgeOrder], 0.34, { bridgeExcessBuyAmount });
 | 
			
		||||
        });
 | 
			
		||||
        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 correctly handle excess maker asset acquired from Eth2Dai', async () => {
 | 
			
		||||
            const bridgeExcessBuyAmount = new BigNumber(1);
 | 
			
		||||
            await eth2Dai.setExcessBuyAmount(bridgeExcessBuyAmount).awaitTransactionSuccessAsync();
 | 
			
		||||
            await testFactory.marketBuyTestAsync([eth2DaiBridgeOrder], 0.34, { bridgeExcessBuyAmount });
 | 
			
		||||
        });
 | 
			
		||||
        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 correctly handle excess maker asset acquired from Uniswap', async () => {
 | 
			
		||||
            const bridgeExcessBuyAmount = new BigNumber(1);
 | 
			
		||||
            await uniswapExchange.setExcessBuyAmount(bridgeExcessBuyAmount).awaitTransactionSuccessAsync();
 | 
			
		||||
            await testFactory.marketBuyTestAsync([uniswapBridgeOrder], 0.34, { bridgeExcessBuyAmount });
 | 
			
		||||
        });
 | 
			
		||||
        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,
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
 | 
			
		||||
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
 | 
			
		||||
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
 | 
			
		||||
import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange';
 | 
			
		||||
@@ -9,7 +8,6 @@ import {
 | 
			
		||||
    expect,
 | 
			
		||||
    getLatestBlockTimestampAsync,
 | 
			
		||||
    getPercentageOfValue,
 | 
			
		||||
    provider,
 | 
			
		||||
    toBaseUnitAmount,
 | 
			
		||||
} from '@0x/contracts-test-utils';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
@@ -26,8 +24,6 @@ import { DeploymentManager } from '../framework/deployment_manager';
 | 
			
		||||
import { deployForwarderAsync } from './deploy_forwarder';
 | 
			
		||||
import { ForwarderTestFactory } from './forwarder_test_factory';
 | 
			
		||||
 | 
			
		||||
const devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
 | 
			
		||||
 | 
			
		||||
blockchainTests('Forwarder integration tests', env => {
 | 
			
		||||
    let deployment: DeploymentManager;
 | 
			
		||||
    let forwarder: ForwarderContract;
 | 
			
		||||
@@ -106,16 +102,7 @@ blockchainTests('Forwarder integration tests', env => {
 | 
			
		||||
        const tokenIds = { erc721: { [erc721Token.address]: [nftId] } };
 | 
			
		||||
        balanceStore = new BlockchainBalanceStore(tokenOwners, tokenContracts, tokenIds);
 | 
			
		||||
 | 
			
		||||
        testFactory = new ForwarderTestFactory(
 | 
			
		||||
            forwarder,
 | 
			
		||||
            deployment,
 | 
			
		||||
            balanceStore,
 | 
			
		||||
            maker,
 | 
			
		||||
            taker,
 | 
			
		||||
            orderFeeRecipient,
 | 
			
		||||
            forwarderFeeRecipient,
 | 
			
		||||
            devUtils,
 | 
			
		||||
        );
 | 
			
		||||
        testFactory = new ForwarderTestFactory(forwarder, deployment, balanceStore, taker, forwarderFeeRecipient);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    after(async () => {
 | 
			
		||||
@@ -138,7 +125,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 +189,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 +508,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 +565,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,24 +27,44 @@ interface ForwarderFillState {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface MarketSellOptions {
 | 
			
		||||
    forwarderFeePercentage: BigNumber;
 | 
			
		||||
    forwarderFeePercentage: Numberish;
 | 
			
		||||
    revertError: RevertError;
 | 
			
		||||
    bridgeExcessBuyAmount: BigNumber;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface MarketBuyOptions extends MarketSellOptions {
 | 
			
		||||
    ethValueAdjustment: number; // Used to provided insufficient/excess ETH
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function areUnderlyingAssetsEqual(assetData1: string, assetData2: string): boolean {
 | 
			
		||||
    const assetProxyId1 = hexSlice(assetData1, 0, 4);
 | 
			
		||||
    const assetProxyId2 = hexSlice(assetData2, 0, 4);
 | 
			
		||||
    if (
 | 
			
		||||
        (assetProxyId1 === AssetProxyId.ERC20 || assetProxyId1 === AssetProxyId.ERC20Bridge) &&
 | 
			
		||||
        (assetProxyId2 === AssetProxyId.ERC20 || assetProxyId2 === AssetProxyId.ERC20Bridge)
 | 
			
		||||
    ) {
 | 
			
		||||
        const assetDataDecoder = new IAssetDataContract(constants.NULL_ADDRESS, provider);
 | 
			
		||||
        const tokenAddress1 =
 | 
			
		||||
            assetProxyId1 === AssetProxyId.ERC20
 | 
			
		||||
                ? assetDataDecoder.getABIDecodedTransactionData<string>('ERC20Token', assetData1)
 | 
			
		||||
                : assetDataDecoder.getABIDecodedTransactionData<[string]>('ERC20Bridge', assetData1)[0];
 | 
			
		||||
        const tokenAddress2 =
 | 
			
		||||
            assetProxyId2 === AssetProxyId.ERC20
 | 
			
		||||
                ? assetDataDecoder.getABIDecodedTransactionData<string>('ERC20Token', assetData2)
 | 
			
		||||
                : assetDataDecoder.getABIDecodedTransactionData<[string]>('ERC20Bridge', assetData2)[0];
 | 
			
		||||
        return tokenAddress2 === tokenAddress1;
 | 
			
		||||
    } else {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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(
 | 
			
		||||
@@ -69,7 +96,7 @@ export class ForwarderTestFactory {
 | 
			
		||||
        const tx = this._forwarder
 | 
			
		||||
            .marketBuyOrdersWithEth(
 | 
			
		||||
                orders,
 | 
			
		||||
                makerAssetAcquiredAmount,
 | 
			
		||||
                makerAssetAcquiredAmount.minus(options.bridgeExcessBuyAmount || 0),
 | 
			
		||||
                orders.map(signedOrder => signedOrder.signature),
 | 
			
		||||
                feePercentage,
 | 
			
		||||
                this._forwarderFeeRecipient.address,
 | 
			
		||||
@@ -164,7 +191,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,
 | 
			
		||||
@@ -185,6 +212,7 @@ export class ForwarderTestFactory {
 | 
			
		||||
                order,
 | 
			
		||||
                ordersInfoBefore[i].orderTakerAssetFilledAmount,
 | 
			
		||||
                Math.min(remainingOrdersToFill, 1),
 | 
			
		||||
                options.bridgeExcessBuyAmount || constants.ZERO_AMOUNT,
 | 
			
		||||
            );
 | 
			
		||||
            remainingOrdersToFill = Math.max(remainingOrdersToFill - 1, 0);
 | 
			
		||||
 | 
			
		||||
@@ -209,6 +237,7 @@ export class ForwarderTestFactory {
 | 
			
		||||
        order: SignedOrder,
 | 
			
		||||
        takerAssetFilled: BigNumber,
 | 
			
		||||
        fillFraction: number,
 | 
			
		||||
        bridgeExcessBuyAmount: BigNumber,
 | 
			
		||||
    ): Promise<ForwarderFillState> {
 | 
			
		||||
        let { makerAssetAmount, takerAssetAmount, makerFee, takerFee } = order;
 | 
			
		||||
        makerAssetAmount = makerAssetAmount.times(fillFraction).integerValue(BigNumber.ROUND_CEIL);
 | 
			
		||||
@@ -228,9 +257,10 @@ export class ForwarderTestFactory {
 | 
			
		||||
        const takerFeeFilled = takerAssetFilled.times(order.takerFee).dividedToIntegerBy(order.takerAssetAmount);
 | 
			
		||||
        takerFee = BigNumber.max(takerFee.minus(takerFeeFilled), 0);
 | 
			
		||||
 | 
			
		||||
        makerAssetAmount = makerAssetAmount.plus(bridgeExcessBuyAmount);
 | 
			
		||||
        let wethSpentAmount = takerAssetAmount.plus(DeploymentManager.protocolFee);
 | 
			
		||||
        let makerAssetAcquiredAmount = makerAssetAmount;
 | 
			
		||||
        if (order.takerFeeAssetData === order.makerAssetData) {
 | 
			
		||||
        if (areUnderlyingAssetsEqual(order.takerFeeAssetData, order.makerAssetData)) {
 | 
			
		||||
            makerAssetAcquiredAmount = makerAssetAcquiredAmount.minus(takerFee);
 | 
			
		||||
        } else if (order.takerFeeAssetData === order.takerAssetData) {
 | 
			
		||||
            wethSpentAmount = wethSpentAmount.plus(takerFee);
 | 
			
		||||
@@ -244,29 +274,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 readonly _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