@0x/contracts-asset-proxy: Add ERC20BridgeProxy and tests.
				
					
				
			This commit is contained in:
		@@ -17,6 +17,10 @@
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                "note": "Remove unused dependency on IAuthorizable in IAssetProxy",
 | 
					                "note": "Remove unused dependency on IAuthorizable in IAssetProxy",
 | 
				
			||||||
                "pr": 1910
 | 
					                "pr": 1910
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                "note": "Add `ERC20BridgeProxy`",
 | 
				
			||||||
 | 
					                "pr": "TODO"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										128
									
								
								contracts/asset-proxy/contracts/src/ERC20BridgeProxy.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								contracts/asset-proxy/contracts/src/ERC20BridgeProxy.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,128 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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-utils/contracts/src/LibBytes.sol";
 | 
				
			||||||
 | 
					import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
 | 
				
			||||||
 | 
					import "@0x/contracts-utils/contracts/src/Authorizable.sol";
 | 
				
			||||||
 | 
					import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
 | 
				
			||||||
 | 
					import "./interfaces/IAssetProxy.sol";
 | 
				
			||||||
 | 
					import "./interfaces/IERC20Bridge.sol";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					contract ERC20BridgeProxy is
 | 
				
			||||||
 | 
					    IAssetProxy,
 | 
				
			||||||
 | 
					    Authorizable
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    using LibBytes for bytes;
 | 
				
			||||||
 | 
					    using LibSafeMath for uint256;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // @dev Result of a successful bridge call.
 | 
				
			||||||
 | 
					    bytes4 constant public BRIDGE_SUCCESS = 0xb5d40d78;
 | 
				
			||||||
 | 
					    // @dev Id of this proxy.
 | 
				
			||||||
 | 
					    //      bytes4(keccak256("ERC20BridgeProxy(address,address,bytes)"))
 | 
				
			||||||
 | 
					    bytes4 constant private PROXY_ID = 0x37708e9b;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// @dev Calls a bridge contract to transfer `amount` of ERC20 from `from`
 | 
				
			||||||
 | 
					    ///      to `to`. Asserts that the balance of `to` has increased by `amount`.
 | 
				
			||||||
 | 
					    /// @param assetData Abi-encoded data for this asset proxy encoded as:
 | 
				
			||||||
 | 
					    ///          abi.encodeWithSelector(
 | 
				
			||||||
 | 
					    ///             bytes4 PROXY_ID,
 | 
				
			||||||
 | 
					    ///             address tokenAddress,
 | 
				
			||||||
 | 
					    ///             address bridgeAddress,
 | 
				
			||||||
 | 
					    ///             bytes bridgeData
 | 
				
			||||||
 | 
					    ///          )
 | 
				
			||||||
 | 
					    /// @param from Address to transfer asset from.
 | 
				
			||||||
 | 
					    /// @param to Address to transfer asset to.
 | 
				
			||||||
 | 
					    /// @param amount Amount of asset to transfer.
 | 
				
			||||||
 | 
					    function transferFrom(
 | 
				
			||||||
 | 
					        bytes calldata assetData,
 | 
				
			||||||
 | 
					        address from,
 | 
				
			||||||
 | 
					        address to,
 | 
				
			||||||
 | 
					        uint256 amount
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					        external
 | 
				
			||||||
 | 
					        onlyAuthorized
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Extract asset data fields.
 | 
				
			||||||
 | 
					        (
 | 
				
			||||||
 | 
					            address tokenAddress,
 | 
				
			||||||
 | 
					            address bridgeAddress,
 | 
				
			||||||
 | 
					            bytes memory bridgeData
 | 
				
			||||||
 | 
					        ) = abi.decode(
 | 
				
			||||||
 | 
					            assetData.sliceDestructive(4, assetData.length),
 | 
				
			||||||
 | 
					            (address, address, bytes)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Remember the balance of `to` before calling the bridge.
 | 
				
			||||||
 | 
					        uint256 balanceBefore = balanceOf(tokenAddress, to);
 | 
				
			||||||
 | 
					        // Call the bridge, who should transfer `amount` of `tokenAddress` to
 | 
				
			||||||
 | 
					        // `to`.
 | 
				
			||||||
 | 
					        bytes4 success = IERC20Bridge(bridgeAddress).transfer(
 | 
				
			||||||
 | 
					            bridgeData,
 | 
				
			||||||
 | 
					            tokenAddress,
 | 
				
			||||||
 | 
					            from,
 | 
				
			||||||
 | 
					            to,
 | 
				
			||||||
 | 
					            amount
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        // Bridge must return the magic bytes to indicate success.
 | 
				
			||||||
 | 
					        require(success == BRIDGE_SUCCESS, "BRIDGE_FAILED");
 | 
				
			||||||
 | 
					        // Ensure that the balance of `to` has increased by at least `amount`.
 | 
				
			||||||
 | 
					        require(
 | 
				
			||||||
 | 
					            balanceBefore.safeAdd(amount) <= balanceOf(tokenAddress, to),
 | 
				
			||||||
 | 
					            "BRIDGE_UNDERPAY"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// @dev Gets the proxy id associated with this asset proxy.
 | 
				
			||||||
 | 
					    /// @return proxyId The proxy id.
 | 
				
			||||||
 | 
					    function getProxyId()
 | 
				
			||||||
 | 
					        external
 | 
				
			||||||
 | 
					        pure
 | 
				
			||||||
 | 
					        returns (bytes4 proxyId)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return PROXY_ID;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// @dev Retrieves the balance of `owner` for this asset.
 | 
				
			||||||
 | 
					    /// @return balance The balance of the ERC20 token being transferred by this
 | 
				
			||||||
 | 
					    ///         asset proxy.
 | 
				
			||||||
 | 
					    function balanceOf(bytes calldata assetData, address owner)
 | 
				
			||||||
 | 
					        external
 | 
				
			||||||
 | 
					        view
 | 
				
			||||||
 | 
					        returns (uint256 balance)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        (address tokenAddress, ,) = abi.decode(
 | 
				
			||||||
 | 
					            assetData.sliceDestructive(4, assetData.length),
 | 
				
			||||||
 | 
					            (address, address, bytes)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        return balanceOf(tokenAddress, owner);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// @dev Retrieves the balance of `owner` given an ERC20 address.
 | 
				
			||||||
 | 
					    /// @return balance The balance of the ERC20 token for `owner`.
 | 
				
			||||||
 | 
					    function balanceOf(address tokenAddress, address owner)
 | 
				
			||||||
 | 
					        private
 | 
				
			||||||
 | 
					        view
 | 
				
			||||||
 | 
					        returns (uint256 balance)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return IERC20Token(tokenAddress).balanceOf(owner);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					contract IERC20Bridge {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
 | 
				
			||||||
 | 
					    /// @param bridgeData Arbitrary asset data needed by the bridge contract.
 | 
				
			||||||
 | 
					    /// @param tokenAddress The address of the ERC20 token to transfer.
 | 
				
			||||||
 | 
					    /// @param from Address to transfer asset from.
 | 
				
			||||||
 | 
					    /// @param to Address to transfer asset to.
 | 
				
			||||||
 | 
					    /// @param amount Amount of asset to transfer.
 | 
				
			||||||
 | 
					    /// @return success The magic bytes `0xb5d40d78` if successful.
 | 
				
			||||||
 | 
					    function transfer(
 | 
				
			||||||
 | 
					        bytes calldata bridgeData,
 | 
				
			||||||
 | 
					        address tokenAddress,
 | 
				
			||||||
 | 
					        address from,
 | 
				
			||||||
 | 
					        address to,
 | 
				
			||||||
 | 
					        uint256 amount
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					        external
 | 
				
			||||||
 | 
					        returns (bytes4 success);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										108
									
								
								contracts/asset-proxy/contracts/test/TestERC20Bridge.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								contracts/asset-proxy/contracts/test/TestERC20Bridge.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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/interfaces/IERC20Bridge.sol";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// @dev Test bridge token
 | 
				
			||||||
 | 
					contract TestERC20BridgeToken {
 | 
				
			||||||
 | 
					    mapping (address => uint256) private _balances;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function addBalance(address owner, int256 amount)
 | 
				
			||||||
 | 
					        external
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        setBalance(owner, uint256(int256(balanceOf(owner)) + amount));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function setBalance(address owner, uint256 balance)
 | 
				
			||||||
 | 
					        public
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        _balances[owner] = balance;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function balanceOf(address owner)
 | 
				
			||||||
 | 
					        public
 | 
				
			||||||
 | 
					        view
 | 
				
			||||||
 | 
					        returns (uint256)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return _balances[owner];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// @dev Test bridge contract.
 | 
				
			||||||
 | 
					contract TestERC20Bridge is
 | 
				
			||||||
 | 
					    IERC20Bridge
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    TestERC20BridgeToken public testToken;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    event BridgeTransfer(
 | 
				
			||||||
 | 
					        bytes bridgeData,
 | 
				
			||||||
 | 
					        address tokenAddress,
 | 
				
			||||||
 | 
					        address from,
 | 
				
			||||||
 | 
					        address to,
 | 
				
			||||||
 | 
					        uint256 amount
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor() public {
 | 
				
			||||||
 | 
					        testToken = new TestERC20BridgeToken();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function setTestTokenBalance(address owner, uint256 balance)
 | 
				
			||||||
 | 
					        external
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        testToken.setBalance(owner, balance);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function transfer(
 | 
				
			||||||
 | 
					        bytes calldata bridgeData,
 | 
				
			||||||
 | 
					        address tokenAddress,
 | 
				
			||||||
 | 
					        address from,
 | 
				
			||||||
 | 
					        address to,
 | 
				
			||||||
 | 
					        uint256 amount
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					        external
 | 
				
			||||||
 | 
					        returns (bytes4)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        emit BridgeTransfer(
 | 
				
			||||||
 | 
					            bridgeData,
 | 
				
			||||||
 | 
					            tokenAddress,
 | 
				
			||||||
 | 
					            from,
 | 
				
			||||||
 | 
					            to,
 | 
				
			||||||
 | 
					            amount
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        // Unpack the bridgeData.
 | 
				
			||||||
 | 
					        (
 | 
				
			||||||
 | 
					            int256 transferAmount,
 | 
				
			||||||
 | 
					            bytes memory revertData,
 | 
				
			||||||
 | 
					            bytes memory returnData
 | 
				
			||||||
 | 
					        ) = abi.decode(bridgeData, (int256, bytes, bytes));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // If `revertData` is set, revert.
 | 
				
			||||||
 | 
					        if (revertData.length != 0) {
 | 
				
			||||||
 | 
					            assembly { revert(add(revertData, 0x20), mload(revertData)) }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // Increase `to`'s balance by `transferAmount`.
 | 
				
			||||||
 | 
					        TestERC20BridgeToken(tokenAddress).addBalance(to, transferAmount);
 | 
				
			||||||
 | 
					        // Return `returnData`.
 | 
				
			||||||
 | 
					        assembly { return(add(returnData, 0x20), mload(returnData)) }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -35,7 +35,7 @@
 | 
				
			|||||||
        "compile:truffle": "truffle compile"
 | 
					        "compile:truffle": "truffle compile"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "config": {
 | 
					    "config": {
 | 
				
			||||||
        "abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestStaticCallTarget).json",
 | 
					        "abis": "./generated-artifacts/@(ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IERC20Bridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestStaticCallTarget).json",
 | 
				
			||||||
        "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
 | 
					        "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "repository": {
 | 
					    "repository": {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,23 +6,27 @@
 | 
				
			|||||||
import { ContractArtifact } from 'ethereum-types';
 | 
					import { ContractArtifact } from 'ethereum-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
 | 
					import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
 | 
				
			||||||
 | 
					import * as ERC20BridgeProxy from '../generated-artifacts/ERC20BridgeProxy.json';
 | 
				
			||||||
import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
 | 
					import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
 | 
				
			||||||
import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
 | 
					import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
 | 
				
			||||||
import * as IAssetData from '../generated-artifacts/IAssetData.json';
 | 
					import * as IAssetData from '../generated-artifacts/IAssetData.json';
 | 
				
			||||||
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
 | 
					import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
 | 
				
			||||||
import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json';
 | 
					import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json';
 | 
				
			||||||
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
 | 
					import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
 | 
				
			||||||
 | 
					import * as IERC20Bridge from '../generated-artifacts/IERC20Bridge.json';
 | 
				
			||||||
import * as MixinAssetProxyDispatcher from '../generated-artifacts/MixinAssetProxyDispatcher.json';
 | 
					import * as MixinAssetProxyDispatcher from '../generated-artifacts/MixinAssetProxyDispatcher.json';
 | 
				
			||||||
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
 | 
					import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
 | 
				
			||||||
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
 | 
					import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
 | 
				
			||||||
import * as Ownable from '../generated-artifacts/Ownable.json';
 | 
					import * as Ownable from '../generated-artifacts/Ownable.json';
 | 
				
			||||||
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
 | 
					import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
 | 
				
			||||||
 | 
					import * as TestERC20Bridge from '../generated-artifacts/TestERC20Bridge.json';
 | 
				
			||||||
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
 | 
					import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
 | 
				
			||||||
export const artifacts = {
 | 
					export const artifacts = {
 | 
				
			||||||
    MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact,
 | 
					    MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact,
 | 
				
			||||||
    MixinAuthorizable: MixinAuthorizable as ContractArtifact,
 | 
					    MixinAuthorizable: MixinAuthorizable as ContractArtifact,
 | 
				
			||||||
    Ownable: Ownable as ContractArtifact,
 | 
					    Ownable: Ownable as ContractArtifact,
 | 
				
			||||||
    ERC1155Proxy: ERC1155Proxy as ContractArtifact,
 | 
					    ERC1155Proxy: ERC1155Proxy as ContractArtifact,
 | 
				
			||||||
 | 
					    ERC20BridgeProxy: ERC20BridgeProxy as ContractArtifact,
 | 
				
			||||||
    ERC20Proxy: ERC20Proxy as ContractArtifact,
 | 
					    ERC20Proxy: ERC20Proxy as ContractArtifact,
 | 
				
			||||||
    ERC721Proxy: ERC721Proxy as ContractArtifact,
 | 
					    ERC721Proxy: ERC721Proxy as ContractArtifact,
 | 
				
			||||||
    MultiAssetProxy: MultiAssetProxy as ContractArtifact,
 | 
					    MultiAssetProxy: MultiAssetProxy as ContractArtifact,
 | 
				
			||||||
@@ -31,5 +35,7 @@ export const artifacts = {
 | 
				
			|||||||
    IAssetProxy: IAssetProxy as ContractArtifact,
 | 
					    IAssetProxy: IAssetProxy as ContractArtifact,
 | 
				
			||||||
    IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
 | 
					    IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
 | 
				
			||||||
    IAuthorizable: IAuthorizable as ContractArtifact,
 | 
					    IAuthorizable: IAuthorizable as ContractArtifact,
 | 
				
			||||||
 | 
					    IERC20Bridge: IERC20Bridge as ContractArtifact,
 | 
				
			||||||
 | 
					    TestERC20Bridge: TestERC20Bridge as ContractArtifact,
 | 
				
			||||||
    TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
 | 
					    TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,15 +4,18 @@
 | 
				
			|||||||
 * -----------------------------------------------------------------------------
 | 
					 * -----------------------------------------------------------------------------
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export * from '../generated-wrappers/erc1155_proxy';
 | 
					export * from '../generated-wrappers/erc1155_proxy';
 | 
				
			||||||
 | 
					export * from '../generated-wrappers/erc20_bridge_proxy';
 | 
				
			||||||
export * from '../generated-wrappers/erc20_proxy';
 | 
					export * from '../generated-wrappers/erc20_proxy';
 | 
				
			||||||
export * from '../generated-wrappers/erc721_proxy';
 | 
					export * from '../generated-wrappers/erc721_proxy';
 | 
				
			||||||
export * from '../generated-wrappers/i_asset_data';
 | 
					export * from '../generated-wrappers/i_asset_data';
 | 
				
			||||||
export * from '../generated-wrappers/i_asset_proxy';
 | 
					export * from '../generated-wrappers/i_asset_proxy';
 | 
				
			||||||
export * from '../generated-wrappers/i_asset_proxy_dispatcher';
 | 
					export * from '../generated-wrappers/i_asset_proxy_dispatcher';
 | 
				
			||||||
export * from '../generated-wrappers/i_authorizable';
 | 
					export * from '../generated-wrappers/i_authorizable';
 | 
				
			||||||
 | 
					export * from '../generated-wrappers/i_erc20_bridge';
 | 
				
			||||||
export * from '../generated-wrappers/mixin_asset_proxy_dispatcher';
 | 
					export * from '../generated-wrappers/mixin_asset_proxy_dispatcher';
 | 
				
			||||||
export * from '../generated-wrappers/mixin_authorizable';
 | 
					export * from '../generated-wrappers/mixin_authorizable';
 | 
				
			||||||
export * from '../generated-wrappers/multi_asset_proxy';
 | 
					export * from '../generated-wrappers/multi_asset_proxy';
 | 
				
			||||||
export * from '../generated-wrappers/ownable';
 | 
					export * from '../generated-wrappers/ownable';
 | 
				
			||||||
export * from '../generated-wrappers/static_call_proxy';
 | 
					export * from '../generated-wrappers/static_call_proxy';
 | 
				
			||||||
 | 
					export * from '../generated-wrappers/test_erc20_bridge';
 | 
				
			||||||
export * from '../generated-wrappers/test_static_call_target';
 | 
					export * from '../generated-wrappers/test_static_call_target';
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										296
									
								
								contracts/asset-proxy/test/erc20bridge_proxy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								contracts/asset-proxy/test/erc20bridge_proxy.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,296 @@
 | 
				
			|||||||
 | 
					import {
 | 
				
			||||||
 | 
					    blockchainTests,
 | 
				
			||||||
 | 
					    constants,
 | 
				
			||||||
 | 
					    expect,
 | 
				
			||||||
 | 
					    getRandomInteger,
 | 
				
			||||||
 | 
					    hexLeftPad,
 | 
				
			||||||
 | 
					    hexRightPad,
 | 
				
			||||||
 | 
					    hexSlice,
 | 
				
			||||||
 | 
					    Numberish,
 | 
				
			||||||
 | 
					    randomAddress,
 | 
				
			||||||
 | 
					} from '@0x/contracts-test-utils';
 | 
				
			||||||
 | 
					import { AbiEncoder, AuthorizableRevertErrors, BigNumber, StringRevertError } from '@0x/utils';
 | 
				
			||||||
 | 
					import { DecodedLogs } from 'ethereum-types';
 | 
				
			||||||
 | 
					import * as _ from 'lodash';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    artifacts,
 | 
				
			||||||
 | 
					    ERC20BridgeProxyContract,
 | 
				
			||||||
 | 
					    TestERC20BridgeBridgeTransferEventArgs,
 | 
				
			||||||
 | 
					    TestERC20BridgeContract,
 | 
				
			||||||
 | 
					} from '../src';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					blockchainTests.resets.only('ERC20BridgeProxy unit tests', env => {
 | 
				
			||||||
 | 
					    const BRIDGE_SUCCESS_RETURN_DATA = hexRightPad('0xb5d40d78');
 | 
				
			||||||
 | 
					    let owner: string;
 | 
				
			||||||
 | 
					    let badCaller: string;
 | 
				
			||||||
 | 
					    let assetProxy: ERC20BridgeProxyContract;
 | 
				
			||||||
 | 
					    let bridgeContract: TestERC20BridgeContract;
 | 
				
			||||||
 | 
					    let testTokenAddress: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    before(async () => {
 | 
				
			||||||
 | 
					        [owner, badCaller] = await env.getAccountAddressesAsync();
 | 
				
			||||||
 | 
					        assetProxy = await ERC20BridgeProxyContract.deployFrom0xArtifactAsync(
 | 
				
			||||||
 | 
					            artifacts.ERC20BridgeProxy,
 | 
				
			||||||
 | 
					            env.provider,
 | 
				
			||||||
 | 
					            env.txDefaults,
 | 
				
			||||||
 | 
					            artifacts,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        bridgeContract = await TestERC20BridgeContract.deployFrom0xArtifactAsync(
 | 
				
			||||||
 | 
					            artifacts.TestERC20Bridge,
 | 
				
			||||||
 | 
					            env.provider,
 | 
				
			||||||
 | 
					            env.txDefaults,
 | 
				
			||||||
 | 
					            artifacts,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        testTokenAddress = await bridgeContract.testToken.callAsync();
 | 
				
			||||||
 | 
					        await assetProxy.addAuthorizedAddress.awaitTransactionSuccessAsync(owner);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    interface AssetDataOpts {
 | 
				
			||||||
 | 
					        tokenAddress: string;
 | 
				
			||||||
 | 
					        bridgeAddress: string;
 | 
				
			||||||
 | 
					        bridgeData: BridgeDataOpts;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    interface BridgeDataOpts {
 | 
				
			||||||
 | 
					        transferAmount: Numberish;
 | 
				
			||||||
 | 
					        revertError?: string;
 | 
				
			||||||
 | 
					        returnData: string;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function createAssetData(opts?: Partial<AssetDataOpts>): AssetDataOpts {
 | 
				
			||||||
 | 
					        return _.merge(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                tokenAddress: testTokenAddress,
 | 
				
			||||||
 | 
					                bridgeAddress: bridgeContract.address,
 | 
				
			||||||
 | 
					                bridgeData: createBridgeData(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            opts,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function createBridgeData(opts?: Partial<BridgeDataOpts>): BridgeDataOpts {
 | 
				
			||||||
 | 
					        return _.merge(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                transferAmount: constants.ZERO_AMOUNT,
 | 
				
			||||||
 | 
					                returnData: BRIDGE_SUCCESS_RETURN_DATA,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            opts,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function encodeAssetData(opts: AssetDataOpts): string {
 | 
				
			||||||
 | 
					        const encoder = AbiEncoder.createMethod('ERC20BridgeProxy', [
 | 
				
			||||||
 | 
					            { name: 'tokenAddress', type: 'address' },
 | 
				
			||||||
 | 
					            { name: 'bridgeAddress', type: 'address' },
 | 
				
			||||||
 | 
					            { name: 'bridgeData', type: 'bytes' },
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        return encoder.encode([opts.tokenAddress, opts.bridgeAddress, encodeBridgeData(opts.bridgeData)]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function encodeBridgeData(opts: BridgeDataOpts): string {
 | 
				
			||||||
 | 
					        const encoder = AbiEncoder.create([
 | 
				
			||||||
 | 
					            { name: 'transferAmount', type: 'int256' },
 | 
				
			||||||
 | 
					            { name: 'revertData', type: 'bytes' },
 | 
				
			||||||
 | 
					            { name: 'returnData', type: 'bytes' },
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        const revertErrorBytes =
 | 
				
			||||||
 | 
					            opts.revertError !== undefined ? new StringRevertError(opts.revertError).encode() : '0x';
 | 
				
			||||||
 | 
					        return encoder.encode([new BigNumber(opts.transferAmount), revertErrorBytes, opts.returnData]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async function setTestTokenBalanceAsync(_owner: string, balance: Numberish): Promise<void> {
 | 
				
			||||||
 | 
					        await bridgeContract.setTestTokenBalance.awaitTransactionSuccessAsync(_owner, new BigNumber(balance));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    describe('transferFrom()', () => {
 | 
				
			||||||
 | 
					        interface TransferFromOpts {
 | 
				
			||||||
 | 
					            assetData: AssetDataOpts;
 | 
				
			||||||
 | 
					            from: string;
 | 
				
			||||||
 | 
					            to: string;
 | 
				
			||||||
 | 
					            amount: Numberish;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts {
 | 
				
			||||||
 | 
					            const transferAmount = _.get(opts, ['amount'], getRandomInteger(1, 100e18)) as BigNumber;
 | 
				
			||||||
 | 
					            return _.merge(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    assetData: createAssetData({
 | 
				
			||||||
 | 
					                        bridgeData: createBridgeData({
 | 
				
			||||||
 | 
					                            transferAmount,
 | 
				
			||||||
 | 
					                        }),
 | 
				
			||||||
 | 
					                    }),
 | 
				
			||||||
 | 
					                    from: randomAddress(),
 | 
				
			||||||
 | 
					                    to: randomAddress(),
 | 
				
			||||||
 | 
					                    amount: transferAmount,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                opts,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        async function transferFromAsync(opts?: Partial<TransferFromOpts>, caller?: string): Promise<DecodedLogs> {
 | 
				
			||||||
 | 
					            const _opts = createTransferFromOpts(opts);
 | 
				
			||||||
 | 
					            const { logs } = await assetProxy.transferFrom.awaitTransactionSuccessAsync(
 | 
				
			||||||
 | 
					                encodeAssetData(_opts.assetData),
 | 
				
			||||||
 | 
					                _opts.from,
 | 
				
			||||||
 | 
					                _opts.to,
 | 
				
			||||||
 | 
					                new BigNumber(_opts.amount),
 | 
				
			||||||
 | 
					                { from: caller },
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            return (logs as any) as DecodedLogs;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it('succeeds if the bridge succeeds and balance increases', async () => {
 | 
				
			||||||
 | 
					            const tx = transferFromAsync();
 | 
				
			||||||
 | 
					            return expect(tx).to.be.fulfilled('');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it('succeeds if balance increases more than `amount`', async () => {
 | 
				
			||||||
 | 
					            const amount = getRandomInteger(1, 100e18);
 | 
				
			||||||
 | 
					            const tx = transferFromAsync({
 | 
				
			||||||
 | 
					                amount,
 | 
				
			||||||
 | 
					                assetData: createAssetData({
 | 
				
			||||||
 | 
					                    bridgeData: createBridgeData({
 | 
				
			||||||
 | 
					                        transferAmount: amount.plus(1),
 | 
				
			||||||
 | 
					                    }),
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            return expect(tx).to.be.fulfilled('');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it('passes the correct arguments to the bridge contract', async () => {
 | 
				
			||||||
 | 
					            const opts = createTransferFromOpts();
 | 
				
			||||||
 | 
					            const logs = await transferFromAsync(opts);
 | 
				
			||||||
 | 
					            expect(logs.length).to.eq(1);
 | 
				
			||||||
 | 
					            const args = logs[0].args as TestERC20BridgeBridgeTransferEventArgs;
 | 
				
			||||||
 | 
					            expect(args.bridgeData).to.eq(encodeBridgeData(opts.assetData.bridgeData));
 | 
				
			||||||
 | 
					            expect(args.tokenAddress).to.eq(opts.assetData.tokenAddress);
 | 
				
			||||||
 | 
					            expect(args.from).to.eq(opts.from);
 | 
				
			||||||
 | 
					            expect(args.to).to.eq(opts.to);
 | 
				
			||||||
 | 
					            expect(args.amount).to.bignumber.eq(opts.amount);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it('fails if not called by an authorized address', async () => {
 | 
				
			||||||
 | 
					            const tx = transferFromAsync({}, badCaller);
 | 
				
			||||||
 | 
					            return expect(tx).to.revertWith(new AuthorizableRevertErrors.SenderNotAuthorizedError(badCaller));
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it('fails if asset data is truncated', async () => {
 | 
				
			||||||
 | 
					            const opts = createTransferFromOpts();
 | 
				
			||||||
 | 
					            const truncatedAssetData = hexSlice(encodeAssetData(opts.assetData), 0, -1);
 | 
				
			||||||
 | 
					            const tx = assetProxy.transferFrom.awaitTransactionSuccessAsync(
 | 
				
			||||||
 | 
					                truncatedAssetData,
 | 
				
			||||||
 | 
					                opts.from,
 | 
				
			||||||
 | 
					                opts.to,
 | 
				
			||||||
 | 
					                new BigNumber(opts.amount),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            return expect(tx).to.be.rejected();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it('fails if bridge returns nothing', async () => {
 | 
				
			||||||
 | 
					            const tx = transferFromAsync({
 | 
				
			||||||
 | 
					                assetData: createAssetData({
 | 
				
			||||||
 | 
					                    bridgeData: createBridgeData({
 | 
				
			||||||
 | 
					                        returnData: '0x',
 | 
				
			||||||
 | 
					                    }),
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            // This will actually revert when the AP tries to decode the return
 | 
				
			||||||
 | 
					            // value.
 | 
				
			||||||
 | 
					            return expect(tx).to.be.rejected();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it('fails if bridge returns true', async () => {
 | 
				
			||||||
 | 
					            const tx = transferFromAsync({
 | 
				
			||||||
 | 
					                assetData: createAssetData({
 | 
				
			||||||
 | 
					                    bridgeData: createBridgeData({
 | 
				
			||||||
 | 
					                        returnData: hexLeftPad('0x1'),
 | 
				
			||||||
 | 
					                    }),
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            // This will actually revert when the AP tries to decode the return
 | 
				
			||||||
 | 
					            // value.
 | 
				
			||||||
 | 
					            return expect(tx).to.be.rejected();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it('fails if bridge returns 0x1', async () => {
 | 
				
			||||||
 | 
					            const tx = transferFromAsync({
 | 
				
			||||||
 | 
					                assetData: createAssetData({
 | 
				
			||||||
 | 
					                    bridgeData: createBridgeData({
 | 
				
			||||||
 | 
					                        returnData: hexRightPad('0x1'),
 | 
				
			||||||
 | 
					                    }),
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            return expect(tx).to.revertWith('BRIDGE_FAILED');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it('fails if bridge is an EOA', async () => {
 | 
				
			||||||
 | 
					            const tx = transferFromAsync({
 | 
				
			||||||
 | 
					                assetData: createAssetData({
 | 
				
			||||||
 | 
					                    bridgeAddress: randomAddress(),
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            // This will actually revert when the AP tries to decode the return
 | 
				
			||||||
 | 
					            // value.
 | 
				
			||||||
 | 
					            return expect(tx).to.be.rejected();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it('fails if bridge reverts', async () => {
 | 
				
			||||||
 | 
					            const revertError = 'FOOBAR';
 | 
				
			||||||
 | 
					            const tx = transferFromAsync({
 | 
				
			||||||
 | 
					                assetData: createAssetData({
 | 
				
			||||||
 | 
					                    bridgeData: createBridgeData({
 | 
				
			||||||
 | 
					                        revertError,
 | 
				
			||||||
 | 
					                    }),
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            // This will actually revert when the AP tries to decode the return
 | 
				
			||||||
 | 
					            // value.
 | 
				
			||||||
 | 
					            return expect(tx).to.revertWith(revertError);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it('fails if balance of `to` increases by `amount - 1`', async () => {
 | 
				
			||||||
 | 
					            const amount = getRandomInteger(1, 100e18);
 | 
				
			||||||
 | 
					            const tx = transferFromAsync({
 | 
				
			||||||
 | 
					                amount,
 | 
				
			||||||
 | 
					                assetData: createAssetData({
 | 
				
			||||||
 | 
					                    bridgeData: createBridgeData({
 | 
				
			||||||
 | 
					                        transferAmount: amount.minus(1),
 | 
				
			||||||
 | 
					                    }),
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            // This will actually revert when the AP tries to decode the return
 | 
				
			||||||
 | 
					            // value.
 | 
				
			||||||
 | 
					            return expect(tx).to.revertWith('BRIDGE_UNDERPAY');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it('fails if balance of `to` decreases', async () => {
 | 
				
			||||||
 | 
					            const toAddress = randomAddress();
 | 
				
			||||||
 | 
					            await setTestTokenBalanceAsync(toAddress, 1e18);
 | 
				
			||||||
 | 
					            const tx = transferFromAsync({
 | 
				
			||||||
 | 
					                to: toAddress,
 | 
				
			||||||
 | 
					                assetData: createAssetData({
 | 
				
			||||||
 | 
					                    bridgeData: createBridgeData({
 | 
				
			||||||
 | 
					                        transferAmount: -1,
 | 
				
			||||||
 | 
					                    }),
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            // This will actually revert when the AP tries to decode the return
 | 
				
			||||||
 | 
					            // value.
 | 
				
			||||||
 | 
					            return expect(tx).to.revertWith('BRIDGE_UNDERPAY');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    describe('balanceOf()', () => {
 | 
				
			||||||
 | 
					        it('retrieves the balance of the encoded token', async () => {
 | 
				
			||||||
 | 
					            const _owner = randomAddress();
 | 
				
			||||||
 | 
					            const balance = getRandomInteger(1, 100e18);
 | 
				
			||||||
 | 
					            await bridgeContract.setTestTokenBalance.awaitTransactionSuccessAsync(_owner, balance);
 | 
				
			||||||
 | 
					            const assetData = createAssetData({
 | 
				
			||||||
 | 
					                tokenAddress: testTokenAddress,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            const actualBalance = await assetProxy.balanceOf.callAsync(encodeAssetData(assetData), _owner);
 | 
				
			||||||
 | 
					            expect(actualBalance).to.bignumber.eq(balance);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@@ -4,17 +4,20 @@
 | 
				
			|||||||
    "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
 | 
					    "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
 | 
				
			||||||
    "files": [
 | 
					    "files": [
 | 
				
			||||||
        "generated-artifacts/ERC1155Proxy.json",
 | 
					        "generated-artifacts/ERC1155Proxy.json",
 | 
				
			||||||
 | 
					        "generated-artifacts/ERC20BridgeProxy.json",
 | 
				
			||||||
        "generated-artifacts/ERC20Proxy.json",
 | 
					        "generated-artifacts/ERC20Proxy.json",
 | 
				
			||||||
        "generated-artifacts/ERC721Proxy.json",
 | 
					        "generated-artifacts/ERC721Proxy.json",
 | 
				
			||||||
        "generated-artifacts/IAssetData.json",
 | 
					        "generated-artifacts/IAssetData.json",
 | 
				
			||||||
        "generated-artifacts/IAssetProxy.json",
 | 
					        "generated-artifacts/IAssetProxy.json",
 | 
				
			||||||
        "generated-artifacts/IAssetProxyDispatcher.json",
 | 
					        "generated-artifacts/IAssetProxyDispatcher.json",
 | 
				
			||||||
        "generated-artifacts/IAuthorizable.json",
 | 
					        "generated-artifacts/IAuthorizable.json",
 | 
				
			||||||
 | 
					        "generated-artifacts/IERC20Bridge.json",
 | 
				
			||||||
        "generated-artifacts/MixinAssetProxyDispatcher.json",
 | 
					        "generated-artifacts/MixinAssetProxyDispatcher.json",
 | 
				
			||||||
        "generated-artifacts/MixinAuthorizable.json",
 | 
					        "generated-artifacts/MixinAuthorizable.json",
 | 
				
			||||||
        "generated-artifacts/MultiAssetProxy.json",
 | 
					        "generated-artifacts/MultiAssetProxy.json",
 | 
				
			||||||
        "generated-artifacts/Ownable.json",
 | 
					        "generated-artifacts/Ownable.json",
 | 
				
			||||||
        "generated-artifacts/StaticCallProxy.json",
 | 
					        "generated-artifacts/StaticCallProxy.json",
 | 
				
			||||||
 | 
					        "generated-artifacts/TestERC20Bridge.json",
 | 
				
			||||||
        "generated-artifacts/TestStaticCallTarget.json"
 | 
					        "generated-artifacts/TestStaticCallTarget.json"
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "exclude": ["./deploy/solc/solc_bin"]
 | 
					    "exclude": ["./deploy/solc/solc_bin"]
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user