From 410a3fef18fa09b5ae4385c93e283bdcb5450dc7 Mon Sep 17 00:00:00 2001 From: Amir Date: Mon, 6 Jan 2020 21:37:31 -0800 Subject: [PATCH] Add ChaiBridge balance checks to DevUtils --- .../contracts/src/interfaces/IChai.sol | 7 +++ .../dev-utils/contracts/src/DevUtils.sol | 10 +++- .../dev-utils/contracts/src/LibAssetData.sol | 59 +++++++++++++++++-- .../contracts/src/OrderValidationUtils.sol | 11 +++- 4 files changed, 76 insertions(+), 11 deletions(-) diff --git a/contracts/asset-proxy/contracts/src/interfaces/IChai.sol b/contracts/asset-proxy/contracts/src/interfaces/IChai.sol index 2074eece31..828939f807 100644 --- a/contracts/asset-proxy/contracts/src/interfaces/IChai.sol +++ b/contracts/asset-proxy/contracts/src/interfaces/IChai.sol @@ -30,4 +30,11 @@ contract IChai { uint256 wad ) external; + + /// @dev Queries Dai balance of Chai holder. + /// @param usr Address of Chai holder. + /// @return Dai balance. + function dai(address usr) + external + returns (uint256); } diff --git a/contracts/dev-utils/contracts/src/DevUtils.sol b/contracts/dev-utils/contracts/src/DevUtils.sol index f806a9969d..3acbcda487 100644 --- a/contracts/dev-utils/contracts/src/DevUtils.sol +++ b/contracts/dev-utils/contracts/src/DevUtils.sol @@ -37,9 +37,15 @@ contract DevUtils is LibEIP712ExchangeDomain, EthBalanceChecker { - constructor (address _exchange) + constructor ( + address _exchange, + address _chaiBridge + ) public - OrderValidationUtils(_exchange) + OrderValidationUtils( + _exchange, + _chaiBridge + ) OrderTransferSimulationUtils(_exchange) LibEIP712ExchangeDomain(uint256(0), address(0)) // null args because because we only use constants {} diff --git a/contracts/dev-utils/contracts/src/LibAssetData.sol b/contracts/dev-utils/contracts/src/LibAssetData.sol index 6333ea3d6b..af34570e6d 100644 --- a/contracts/dev-utils/contracts/src/LibAssetData.sol +++ b/contracts/dev-utils/contracts/src/LibAssetData.sol @@ -26,9 +26,13 @@ import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetProxy.sol"; import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol"; import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol"; +import "@0x/contracts-asset-proxy/contracts/src/interfaces/IChai.sol"; +import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol"; -contract LibAssetData { +contract LibAssetData is + DeploymentConstants +{ // 2^256 - 1 uint256 constant internal _MAX_UINT256 = uint256(-1); @@ -41,9 +45,13 @@ contract LibAssetData { address internal _ERC721_PROXY_ADDRESS; address internal _ERC1155_PROXY_ADDRESS; address internal _STATIC_CALL_PROXY_ADDRESS; + address internal _CHAI_BRIDGE_ADDRESS; // solhint-enable var-name-mixedcase - constructor (address _exchange) + constructor ( + address _exchange, + address _chaiBridge + ) public { _EXCHANGE = IExchange(_exchange); @@ -51,6 +59,7 @@ contract LibAssetData { _ERC721_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC721Token.selector); _ERC1155_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC1155Assets.selector); _STATIC_CALL_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).StaticCall.selector); + _CHAI_BRIDGE_ADDRESS = _chaiBridge; } /// @dev Returns the owner's balance of the assets(s) specified in @@ -62,7 +71,6 @@ contract LibAssetData { /// @return Number of assets (or asset baskets) held by owner. function getBalance(address ownerAddress, bytes memory assetData) public - view returns (uint256 balance) { // Get id of AssetProxy contract @@ -140,6 +148,19 @@ contract LibAssetData { // Success means that the staticcall can be made an unlimited amount of times balance = success ? _MAX_UINT256 : 0; + } else if (assetProxyId == IAssetData(address(0)).ERC20Bridge.selector) { + // Get address of ERC20 token and bridge contract + (, address tokenAddress, address bridgeAddress,) = decodeERC20BridgeAssetData(assetData); + if (tokenAddress == _getDaiAddress() && bridgeAddress == _CHAI_BRIDGE_ADDRESS) { + bytes memory chaiDaiCalldata = abi.encodeWithSelector( + IChai(address(0)).dai.selector, + ownerAddress + ); + // We do not make a STATICCALL because this function can potentially alter state + (bool success, bytes memory returnData) = _getChaiAddress().call(chaiDaiCalldata); + uint256 chaiBalance = success && returnData.length == 32 ? returnData.readUint256(0) : 0; + } + // Balance will be 0 if bridge is not supported } else if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) { // Get array of values and array of assetDatas (, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData); @@ -176,7 +197,6 @@ contract LibAssetData { /// corresponding to the same-indexed element in the assetData input. function getBatchBalances(address ownerAddress, bytes[] memory assetData) public - view returns (uint256[] memory balances) { uint256 length = assetData.length; @@ -316,7 +336,6 @@ contract LibAssetData { /// of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend. function getBalanceAndAssetProxyAllowance(address ownerAddress, bytes memory assetData) public - view returns (uint256 balance, uint256 allowance) { balance = getBalance(ownerAddress, assetData); @@ -332,7 +351,6 @@ contract LibAssetData { /// corresponding to the same-indexed element in the assetData input. function getBatchBalancesAndAssetProxyAllowances(address ownerAddress, bytes[] memory assetData) public - view returns (uint256[] memory balances, uint256[] memory allowances) { balances = getBatchBalances(ownerAddress, assetData); @@ -613,6 +631,33 @@ contract LibAssetData { ); } + /// @dev Decode ERC20Bridge asset data from the format described in the AssetProxy contract specification. + /// @param assetData AssetProxy-compliant asset data describing an ERC20Bridge asset + /// @return The ERC20BridgeProxy identifier, the address of the ERC20 token to transfer, the address + /// of the bridge contract, and extra data to be passed to the bridge contract. + function decodeERC20BridgeAssetData(bytes memory assetData) + public + pure + returns ( + bytes4 assetProxyId, + address tokenAddress, + address bridgeAddress, + bytes memory bridgeData + ) + { + assetProxyId = assetData.readBytes4(0); + + require( + assetProxyId == IAssetData(address(0)).ERC20Bridge.selector, + "WRONG_PROXY_ID" + ); + + (tokenAddress, bridgeAddress, bridgeData) = abi.decode( + assetData.slice(4, assetData.length), + (address, address, bytes) + ); + } + function revertIfInvalidAssetData(bytes memory assetData) public pure @@ -629,6 +674,8 @@ contract LibAssetData { decodeMultiAssetData(assetData); } else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) { decodeStaticCallAssetData(assetData); + } else if (assetProxyId == IAssetData(address(0)).ERC20Bridge.selector) { + decodeERC20BridgeAssetData(assetData); } else { revert("WRONG_PROXY_ID"); } diff --git a/contracts/dev-utils/contracts/src/OrderValidationUtils.sol b/contracts/dev-utils/contracts/src/OrderValidationUtils.sol index 7c0f10ea08..53a722a430 100644 --- a/contracts/dev-utils/contracts/src/OrderValidationUtils.sol +++ b/contracts/dev-utils/contracts/src/OrderValidationUtils.sol @@ -35,9 +35,15 @@ contract OrderValidationUtils is using LibBytes for bytes; using LibSafeMath for uint256; - constructor (address _exchange) + constructor ( + address _exchange, + address _chaiBridge + ) public - LibAssetData(_exchange) + LibAssetData( + _exchange, + _chaiBridge + ) {} /// @dev Fetches all order-relevant information needed to validate if the supplied order is fillable. @@ -173,7 +179,6 @@ contract OrderValidationUtils is /// the individual asset amounts located within the `assetData`. function getTransferableAssetAmount(address ownerAddress, bytes memory assetData) public - view returns (uint256 transferableAssetAmount) { (uint256 balance, uint256 allowance) = getBalanceAndAssetProxyAllowance(ownerAddress, assetData);