Merge pull request #2440 from 0xProject/feat/dev-utils/chai-bridge-validation
Add ChaiBridge order balance/allowance checks to DevUtils
This commit is contained in:
commit
28d1f3eef0
@ -18,10 +18,22 @@
|
|||||||
|
|
||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract PotLike {
|
||||||
|
function chi() external returns (uint256);
|
||||||
|
function rho() external returns (uint256);
|
||||||
|
function drip() external returns (uint256);
|
||||||
|
function join(uint256) external;
|
||||||
|
function exit(uint256) external;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// The actual Chai contract can be found here: https://github.com/dapphub/chai
|
// The actual Chai contract can be found here: https://github.com/dapphub/chai
|
||||||
contract IChai {
|
contract IChai is
|
||||||
|
IERC20Token
|
||||||
|
{
|
||||||
/// @dev Withdraws Dai owned by `src`
|
/// @dev Withdraws Dai owned by `src`
|
||||||
/// @param src Address that owns Dai.
|
/// @param src Address that owns Dai.
|
||||||
/// @param wad Amount of Dai to withdraw.
|
/// @param wad Amount of Dai to withdraw.
|
||||||
@ -30,4 +42,25 @@ contract IChai {
|
|||||||
uint256 wad
|
uint256 wad
|
||||||
)
|
)
|
||||||
external;
|
external;
|
||||||
|
|
||||||
|
/// @dev Queries Dai balance of Chai holder.
|
||||||
|
/// @param usr Address of Chai holder.
|
||||||
|
/// @return Dai balance.
|
||||||
|
function dai(address usr)
|
||||||
|
external
|
||||||
|
returns (uint256);
|
||||||
|
|
||||||
|
/// @dev Queries the Pot contract used by the Chai contract.
|
||||||
|
function pot()
|
||||||
|
external
|
||||||
|
returns (PotLike);
|
||||||
|
|
||||||
|
/// @dev Deposits Dai in exchange for Chai
|
||||||
|
/// @param dst Address to receive Chai.
|
||||||
|
/// @param wad Amount of Dai to deposit.
|
||||||
|
function join(
|
||||||
|
address dst,
|
||||||
|
uint256 wad
|
||||||
|
)
|
||||||
|
external;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ export {
|
|||||||
UniswapBridgeContract,
|
UniswapBridgeContract,
|
||||||
KyberBridgeContract,
|
KyberBridgeContract,
|
||||||
ChaiBridgeContract,
|
ChaiBridgeContract,
|
||||||
|
IChaiContract,
|
||||||
} from './wrappers';
|
} from './wrappers';
|
||||||
|
|
||||||
export { ERC20Wrapper } from './erc20_wrapper';
|
export { ERC20Wrapper } from './erc20_wrapper';
|
||||||
|
@ -37,14 +37,24 @@ contract DevUtils is
|
|||||||
LibEIP712ExchangeDomain,
|
LibEIP712ExchangeDomain,
|
||||||
EthBalanceChecker
|
EthBalanceChecker
|
||||||
{
|
{
|
||||||
constructor (address _exchange)
|
constructor (
|
||||||
|
address _exchange,
|
||||||
|
address _chaiBridge
|
||||||
|
)
|
||||||
public
|
public
|
||||||
OrderValidationUtils(_exchange)
|
OrderValidationUtils(
|
||||||
|
_exchange,
|
||||||
|
_chaiBridge
|
||||||
|
)
|
||||||
OrderTransferSimulationUtils(_exchange)
|
OrderTransferSimulationUtils(_exchange)
|
||||||
LibEIP712ExchangeDomain(uint256(0), address(0)) // null args because because we only use constants
|
LibEIP712ExchangeDomain(uint256(0), address(0)) // null args because because we only use constants
|
||||||
{}
|
{}
|
||||||
|
|
||||||
function getOrderHash(LibOrder.Order memory order, uint256 chainId, address exchange)
|
function getOrderHash(
|
||||||
|
LibOrder.Order memory order,
|
||||||
|
uint256 chainId,
|
||||||
|
address exchange
|
||||||
|
)
|
||||||
public
|
public
|
||||||
pure
|
pure
|
||||||
returns (bytes32 orderHash)
|
returns (bytes32 orderHash)
|
||||||
|
@ -26,10 +26,14 @@ import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetProxy.sol";
|
|||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
|
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
|
||||||
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.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";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||||
|
|
||||||
|
|
||||||
contract LibAssetData {
|
contract LibAssetData is
|
||||||
|
DeploymentConstants
|
||||||
|
{
|
||||||
// 2^256 - 1
|
// 2^256 - 1
|
||||||
uint256 constant internal _MAX_UINT256 = uint256(-1);
|
uint256 constant internal _MAX_UINT256 = uint256(-1);
|
||||||
|
|
||||||
@ -41,9 +45,13 @@ contract LibAssetData {
|
|||||||
address internal _ERC721_PROXY_ADDRESS;
|
address internal _ERC721_PROXY_ADDRESS;
|
||||||
address internal _ERC1155_PROXY_ADDRESS;
|
address internal _ERC1155_PROXY_ADDRESS;
|
||||||
address internal _STATIC_CALL_PROXY_ADDRESS;
|
address internal _STATIC_CALL_PROXY_ADDRESS;
|
||||||
|
address internal _CHAI_BRIDGE_ADDRESS;
|
||||||
// solhint-enable var-name-mixedcase
|
// solhint-enable var-name-mixedcase
|
||||||
|
|
||||||
constructor (address _exchange)
|
constructor (
|
||||||
|
address _exchange,
|
||||||
|
address _chaiBridge
|
||||||
|
)
|
||||||
public
|
public
|
||||||
{
|
{
|
||||||
_EXCHANGE = IExchange(_exchange);
|
_EXCHANGE = IExchange(_exchange);
|
||||||
@ -51,6 +59,7 @@ contract LibAssetData {
|
|||||||
_ERC721_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC721Token.selector);
|
_ERC721_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC721Token.selector);
|
||||||
_ERC1155_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC1155Assets.selector);
|
_ERC1155_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC1155Assets.selector);
|
||||||
_STATIC_CALL_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).StaticCall.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
|
/// @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.
|
/// @return Number of assets (or asset baskets) held by owner.
|
||||||
function getBalance(address ownerAddress, bytes memory assetData)
|
function getBalance(address ownerAddress, bytes memory assetData)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (uint256 balance)
|
returns (uint256 balance)
|
||||||
{
|
{
|
||||||
// Get id of AssetProxy contract
|
// Get id of AssetProxy contract
|
||||||
@ -71,16 +79,8 @@ contract LibAssetData {
|
|||||||
if (assetProxyId == IAssetData(address(0)).ERC20Token.selector) {
|
if (assetProxyId == IAssetData(address(0)).ERC20Token.selector) {
|
||||||
// Get ERC20 token address
|
// Get ERC20 token address
|
||||||
address tokenAddress = assetData.readAddress(16);
|
address tokenAddress = assetData.readAddress(16);
|
||||||
|
balance = _erc20BalanceOf(tokenAddress, ownerAddress);
|
||||||
|
|
||||||
// Encode data for `balanceOf(ownerAddress)`
|
|
||||||
bytes memory balanceOfData = abi.encodeWithSelector(
|
|
||||||
IERC20Token(address(0)).balanceOf.selector,
|
|
||||||
ownerAddress
|
|
||||||
);
|
|
||||||
|
|
||||||
// Query balance
|
|
||||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData);
|
|
||||||
balance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
|
||||||
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
|
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
|
||||||
// Get ERC721 token address and id
|
// Get ERC721 token address and id
|
||||||
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
|
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
|
||||||
@ -94,6 +94,7 @@ contract LibAssetData {
|
|||||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(ownerOfCalldata);
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(ownerOfCalldata);
|
||||||
address currentOwnerAddress = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0);
|
address currentOwnerAddress = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0);
|
||||||
balance = currentOwnerAddress == ownerAddress ? 1 : 0;
|
balance = currentOwnerAddress == ownerAddress ? 1 : 0;
|
||||||
|
|
||||||
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
|
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
|
||||||
// Get ERC1155 token address, array of ids, and array of values
|
// Get ERC1155 token address, array of ids, and array of values
|
||||||
(, address tokenAddress, uint256[] memory tokenIds, uint256[] memory tokenValues,) = decodeERC1155AssetData(assetData);
|
(, address tokenAddress, uint256[] memory tokenIds, uint256[] memory tokenValues,) = decodeERC1155AssetData(assetData);
|
||||||
@ -125,6 +126,7 @@ contract LibAssetData {
|
|||||||
balance = scaledBalance;
|
balance = scaledBalance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
||||||
// Encode data for `staticCallProxy.transferFrom(assetData,...)`
|
// Encode data for `staticCallProxy.transferFrom(assetData,...)`
|
||||||
bytes memory transferFromData = abi.encodeWithSelector(
|
bytes memory transferFromData = abi.encodeWithSelector(
|
||||||
@ -140,6 +142,17 @@ contract LibAssetData {
|
|||||||
|
|
||||||
// Success means that the staticcall can be made an unlimited amount of times
|
// Success means that the staticcall can be made an unlimited amount of times
|
||||||
balance = success ? _MAX_UINT256 : 0;
|
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) {
|
||||||
|
uint256 chaiBalance = _erc20BalanceOf(_getChaiAddress(), ownerAddress);
|
||||||
|
// Calculate Dai balance
|
||||||
|
balance = _convertChaiToDaiAmount(chaiBalance);
|
||||||
|
}
|
||||||
|
// Balance will be 0 if bridge is not supported
|
||||||
|
|
||||||
} else if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) {
|
} else if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) {
|
||||||
// Get array of values and array of assetDatas
|
// Get array of values and array of assetDatas
|
||||||
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
|
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
|
||||||
@ -176,7 +189,6 @@ contract LibAssetData {
|
|||||||
/// corresponding to the same-indexed element in the assetData input.
|
/// corresponding to the same-indexed element in the assetData input.
|
||||||
function getBatchBalances(address ownerAddress, bytes[] memory assetData)
|
function getBatchBalances(address ownerAddress, bytes[] memory assetData)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (uint256[] memory balances)
|
returns (uint256[] memory balances)
|
||||||
{
|
{
|
||||||
uint256 length = assetData.length;
|
uint256 length = assetData.length;
|
||||||
@ -197,7 +209,6 @@ contract LibAssetData {
|
|||||||
/// @return Number of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
|
/// @return Number of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
|
||||||
function getAssetProxyAllowance(address ownerAddress, bytes memory assetData)
|
function getAssetProxyAllowance(address ownerAddress, bytes memory assetData)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (uint256 allowance)
|
returns (uint256 allowance)
|
||||||
{
|
{
|
||||||
// Get id of AssetProxy contract
|
// Get id of AssetProxy contract
|
||||||
@ -243,6 +254,7 @@ contract LibAssetData {
|
|||||||
// Query allowance
|
// Query allowance
|
||||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(allowanceData);
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(allowanceData);
|
||||||
allowance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
allowance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
||||||
|
|
||||||
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
|
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
|
||||||
// Get ERC721 token address and id
|
// Get ERC721 token address and id
|
||||||
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
|
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
|
||||||
@ -268,6 +280,7 @@ contract LibAssetData {
|
|||||||
// Allowance is 2^256 - 1 if `isApprovedForAll` returned true
|
// Allowance is 2^256 - 1 if `isApprovedForAll` returned true
|
||||||
allowance = _MAX_UINT256;
|
allowance = _MAX_UINT256;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
|
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
|
||||||
// Get ERC1155 token address
|
// Get ERC1155 token address
|
||||||
(, address tokenAddress, , , ) = decodeERC1155AssetData(assetData);
|
(, address tokenAddress, , , ) = decodeERC1155AssetData(assetData);
|
||||||
@ -282,9 +295,26 @@ contract LibAssetData {
|
|||||||
// Query allowance
|
// Query allowance
|
||||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
|
||||||
allowance = success && returnData.length == 32 && returnData.readUint256(0) == 1 ? _MAX_UINT256 : 0;
|
allowance = success && returnData.length == 32 && returnData.readUint256(0) == 1 ? _MAX_UINT256 : 0;
|
||||||
|
|
||||||
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
||||||
// The StaticCallProxy does not require any approvals
|
// The StaticCallProxy does not require any approvals
|
||||||
allowance = _MAX_UINT256;
|
allowance = _MAX_UINT256;
|
||||||
|
|
||||||
|
} 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 allowanceData = abi.encodeWithSelector(
|
||||||
|
IERC20Token(address(0)).allowance.selector,
|
||||||
|
ownerAddress,
|
||||||
|
_CHAI_BRIDGE_ADDRESS
|
||||||
|
);
|
||||||
|
(bool success, bytes memory returnData) = _getChaiAddress().staticcall(allowanceData);
|
||||||
|
uint256 chaiAllowance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
||||||
|
// Dai allowance is unlimited if Chai allowance is unlimited
|
||||||
|
allowance = chaiAllowance == _MAX_UINT256 ? _MAX_UINT256 : _convertChaiToDaiAmount(chaiAllowance);
|
||||||
|
}
|
||||||
|
// Allowance will be 0 if bridge is not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allowance will be 0 if the assetProxyId is unknown
|
// Allowance will be 0 if the assetProxyId is unknown
|
||||||
@ -298,7 +328,6 @@ contract LibAssetData {
|
|||||||
/// element corresponding to the same-indexed element in the assetData input.
|
/// element corresponding to the same-indexed element in the assetData input.
|
||||||
function getBatchAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
|
function getBatchAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (uint256[] memory allowances)
|
returns (uint256[] memory allowances)
|
||||||
{
|
{
|
||||||
uint256 length = assetData.length;
|
uint256 length = assetData.length;
|
||||||
@ -316,7 +345,6 @@ contract LibAssetData {
|
|||||||
/// of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
|
/// of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
|
||||||
function getBalanceAndAssetProxyAllowance(address ownerAddress, bytes memory assetData)
|
function getBalanceAndAssetProxyAllowance(address ownerAddress, bytes memory assetData)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (uint256 balance, uint256 allowance)
|
returns (uint256 balance, uint256 allowance)
|
||||||
{
|
{
|
||||||
balance = getBalance(ownerAddress, assetData);
|
balance = getBalance(ownerAddress, assetData);
|
||||||
@ -332,7 +360,6 @@ contract LibAssetData {
|
|||||||
/// corresponding to the same-indexed element in the assetData input.
|
/// corresponding to the same-indexed element in the assetData input.
|
||||||
function getBatchBalancesAndAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
|
function getBatchBalancesAndAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (uint256[] memory balances, uint256[] memory allowances)
|
returns (uint256[] memory balances, uint256[] memory allowances)
|
||||||
{
|
{
|
||||||
balances = getBatchBalances(ownerAddress, assetData);
|
balances = getBatchBalances(ownerAddress, assetData);
|
||||||
@ -613,6 +640,35 @@ 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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Reverts if assetData is not of a valid format for its given proxy id.
|
||||||
|
/// @param assetData AssetProxy compliant asset data.
|
||||||
function revertIfInvalidAssetData(bytes memory assetData)
|
function revertIfInvalidAssetData(bytes memory assetData)
|
||||||
public
|
public
|
||||||
pure
|
pure
|
||||||
@ -629,8 +685,50 @@ contract LibAssetData {
|
|||||||
decodeMultiAssetData(assetData);
|
decodeMultiAssetData(assetData);
|
||||||
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
||||||
decodeStaticCallAssetData(assetData);
|
decodeStaticCallAssetData(assetData);
|
||||||
|
} else if (assetProxyId == IAssetData(address(0)).ERC20Bridge.selector) {
|
||||||
|
decodeERC20BridgeAssetData(assetData);
|
||||||
} else {
|
} else {
|
||||||
revert("WRONG_PROXY_ID");
|
revert("WRONG_PROXY_ID");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Queries balance of an ERC20 token. Returns 0 if call was unsuccessful.
|
||||||
|
/// @param tokenAddress Address of ERC20 token.
|
||||||
|
/// @param ownerAddress Address of owner of ERC20 token.
|
||||||
|
/// @return balance ERC20 token balance of owner.
|
||||||
|
function _erc20BalanceOf(
|
||||||
|
address tokenAddress,
|
||||||
|
address ownerAddress
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (uint256 balance)
|
||||||
|
{
|
||||||
|
// Encode data for `balanceOf(ownerAddress)`
|
||||||
|
bytes memory balanceOfData = abi.encodeWithSelector(
|
||||||
|
IERC20Token(address(0)).balanceOf.selector,
|
||||||
|
ownerAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
// Query balance
|
||||||
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData);
|
||||||
|
balance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
||||||
|
return balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Converts an amount of Chai into its equivalent Dai amount.
|
||||||
|
/// Also accumulates Dai from DSR if called after the last time it was collected.
|
||||||
|
/// @param chaiAmount Amount of Chai to converts.
|
||||||
|
function _convertChaiToDaiAmount(uint256 chaiAmount)
|
||||||
|
internal
|
||||||
|
returns (uint256 daiAmount)
|
||||||
|
{
|
||||||
|
PotLike pot = IChai(_getChaiAddress()).pot();
|
||||||
|
// Accumulate savings if called after last time savings were collected
|
||||||
|
uint256 chiMultiplier = (now > pot.rho())
|
||||||
|
? pot.drip()
|
||||||
|
: pot.chi();
|
||||||
|
daiAmount = LibMath.getPartialAmountFloor(chiMultiplier, 10**27, chaiAmount);
|
||||||
|
return daiAmount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,15 @@ contract OrderValidationUtils is
|
|||||||
using LibBytes for bytes;
|
using LibBytes for bytes;
|
||||||
using LibSafeMath for uint256;
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
constructor (address _exchange)
|
constructor (
|
||||||
|
address _exchange,
|
||||||
|
address _chaiBridge
|
||||||
|
)
|
||||||
public
|
public
|
||||||
LibAssetData(_exchange)
|
LibAssetData(
|
||||||
|
_exchange,
|
||||||
|
_chaiBridge
|
||||||
|
)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/// @dev Fetches all order-relevant information needed to validate if the supplied order is fillable.
|
/// @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`.
|
/// the individual asset amounts located within the `assetData`.
|
||||||
function getTransferableAssetAmount(address ownerAddress, bytes memory assetData)
|
function getTransferableAssetAmount(address ownerAddress, bytes memory assetData)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (uint256 transferableAssetAmount)
|
returns (uint256 transferableAssetAmount)
|
||||||
{
|
{
|
||||||
(uint256 balance, uint256 allowance) = getBalanceAndAssetProxyAllowance(ownerAddress, assetData);
|
(uint256 balance, uint256 allowance) = getBalanceAndAssetProxyAllowance(ownerAddress, assetData);
|
||||||
|
@ -14,7 +14,7 @@ import { contractAddresses, dydxAccountOwner } from '../mainnet_fork_utils';
|
|||||||
|
|
||||||
import { dydxEvents } from './abi/dydxEvents';
|
import { dydxEvents } from './abi/dydxEvents';
|
||||||
|
|
||||||
blockchainTests.fork.resets('Mainnet dydx bridge tests', env => {
|
blockchainTests.fork.skip('Mainnet dydx bridge tests', env => {
|
||||||
let testContract: DydxBridgeContract;
|
let testContract: DydxBridgeContract;
|
||||||
// random account to receive tokens from dydx
|
// random account to receive tokens from dydx
|
||||||
const receiver = '0x986ccf5234d9cfbb25246f1a5bfa51f4ccfcb308';
|
const receiver = '0x986ccf5234d9cfbb25246f1a5bfa51f4ccfcb308';
|
||||||
|
@ -0,0 +1,76 @@
|
|||||||
|
import { IChaiContract } from '@0x/contracts-asset-proxy';
|
||||||
|
import { artifacts as devUtilsArtifacts, DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||||
|
import { ERC20TokenContract } from '@0x/contracts-erc20';
|
||||||
|
import { blockchainTests, constants, expect } from '@0x/contracts-test-utils';
|
||||||
|
import { assetDataUtils } from '@0x/order-utils';
|
||||||
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
|
|
||||||
|
import { contractAddresses } from '../mainnet_fork_utils';
|
||||||
|
|
||||||
|
blockchainTests.fork.resets('DevUtils mainnet tests', env => {
|
||||||
|
const daiAddress = '0x6b175474e89094c44da98b954eedeac495271d0f';
|
||||||
|
const chaiAddress = '0x06af07097c9eeb7fd685c692751d5c66db49c215';
|
||||||
|
const daiHolder = '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b';
|
||||||
|
let noDaiAddress: string;
|
||||||
|
|
||||||
|
const assetData = assetDataUtils.encodeERC20BridgeAssetData(
|
||||||
|
daiAddress,
|
||||||
|
contractAddresses.chaiBridge,
|
||||||
|
constants.NULL_BYTES,
|
||||||
|
);
|
||||||
|
|
||||||
|
const dai = new ERC20TokenContract(daiAddress, env.provider, env.txDefaults);
|
||||||
|
const chai = new IChaiContract(chaiAddress, env.provider, env.txDefaults);
|
||||||
|
let devUtils: DevUtilsContract;
|
||||||
|
const daiDepositAmount = Web3Wrapper.toBaseUnitAmount(1000, 18);
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
[noDaiAddress] = await env.getAccountAddressesAsync();
|
||||||
|
devUtils = await DevUtilsContract.deployFrom0xArtifactAsync(
|
||||||
|
devUtilsArtifacts.DevUtils,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
devUtilsArtifacts,
|
||||||
|
contractAddresses.exchange,
|
||||||
|
contractAddresses.chaiBridge,
|
||||||
|
);
|
||||||
|
await dai.approve(chai.address, constants.MAX_UINT256).awaitTransactionSuccessAsync({ from: daiHolder });
|
||||||
|
await chai.join(daiHolder, daiDepositAmount).awaitTransactionSuccessAsync({ from: daiHolder });
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('LibAssetData', () => {
|
||||||
|
describe('ChaiBridge getBalance', () => {
|
||||||
|
it('should return the correct non-zero Dai balance for a Chai holder', async () => {
|
||||||
|
const expectedDaiBalance = await chai.dai(daiHolder).callAsync();
|
||||||
|
const daiBalance = await devUtils.getBalance(daiHolder, assetData).callAsync();
|
||||||
|
expect(daiBalance).bignumber.eq(expectedDaiBalance);
|
||||||
|
});
|
||||||
|
it('should return a balance of 0 when Chai balance is 0', async () => {
|
||||||
|
const daiBalance = await devUtils.getBalance(noDaiAddress, assetData).callAsync();
|
||||||
|
expect(daiBalance).bignumber.eq(constants.ZERO_AMOUNT);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('ChaiBridge getProxyAllowance', () => {
|
||||||
|
it('should return the correct non-zero non-unlimited allowance', async () => {
|
||||||
|
const chaiBalance = await chai.balanceOf(daiHolder).callAsync();
|
||||||
|
await chai
|
||||||
|
.approve(contractAddresses.chaiBridge, chaiBalance)
|
||||||
|
.awaitTransactionSuccessAsync({ from: daiHolder });
|
||||||
|
const daiBalance = await chai.dai(daiHolder).callAsync();
|
||||||
|
const allowance = await devUtils.getAssetProxyAllowance(daiHolder, assetData).callAsync();
|
||||||
|
expect(allowance).to.bignumber.eq(daiBalance);
|
||||||
|
});
|
||||||
|
it('should return an unlimited allowance of Dai when Chai allowance is also unlimited', async () => {
|
||||||
|
await chai
|
||||||
|
.approve(contractAddresses.chaiBridge, constants.MAX_UINT256)
|
||||||
|
.awaitTransactionSuccessAsync({ from: daiHolder });
|
||||||
|
const allowance = await devUtils.getAssetProxyAllowance(daiHolder, assetData).callAsync();
|
||||||
|
expect(allowance).to.bignumber.eq(constants.MAX_UINT256);
|
||||||
|
});
|
||||||
|
it('should return an allowance of 0 when Chai allowance is 0', async () => {
|
||||||
|
const allowance = await devUtils.getAssetProxyAllowance(noDaiAddress, assetData).callAsync();
|
||||||
|
expect(allowance).to.bignumber.eq(constants.ZERO_AMOUNT);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -25,6 +25,7 @@ blockchainTests('DevUtils.getOrderHash', env => {
|
|||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
exchange.address,
|
exchange.address,
|
||||||
|
constants.NULL_ADDRESS,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -79,6 +79,7 @@ blockchainTests.resets('LibAssetData', env => {
|
|||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
deployment.exchange.address,
|
deployment.exchange.address,
|
||||||
|
constants.NULL_ADDRESS,
|
||||||
);
|
);
|
||||||
|
|
||||||
staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync(
|
staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync(
|
||||||
|
@ -203,6 +203,7 @@ export class DeploymentManager {
|
|||||||
environment.txDefaults,
|
environment.txDefaults,
|
||||||
devUtilsArtifacts,
|
devUtilsArtifacts,
|
||||||
exchange.address,
|
exchange.address,
|
||||||
|
constants.NULL_ADDRESS,
|
||||||
);
|
);
|
||||||
const assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, environment.provider);
|
const assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, environment.provider);
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import { BigNumber } from '@0x/utils';
|
|||||||
|
|
||||||
import { contractAddresses, getContractwrappers } from './mainnet_fork_utils';
|
import { contractAddresses, getContractwrappers } from './mainnet_fork_utils';
|
||||||
|
|
||||||
blockchainTests.fork.resets('Mainnet configs tests', env => {
|
blockchainTests.live('Mainnet configs tests', env => {
|
||||||
let contractWrappers: ContractWrappers;
|
let contractWrappers: ContractWrappers;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
|
@ -20,6 +20,7 @@ export let providerConfigs: Web3Config = {
|
|||||||
shouldUseInProcessGanache: true,
|
shouldUseInProcessGanache: true,
|
||||||
shouldAllowUnlimitedContractSize: true,
|
shouldAllowUnlimitedContractSize: true,
|
||||||
hardfork: 'istanbul',
|
hardfork: 'istanbul',
|
||||||
|
unlocked_accounts: ['0x6cc5f688a315f3dc28a7781717a9a798a59fda7b', '0x55dc8f21d20d4c6ed3c82916a438a413ca68e335'],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const provider: Web3ProviderEngine = web3Factory.getRpcProvider(providerConfigs);
|
export const provider: Web3ProviderEngine = web3Factory.getRpcProvider(providerConfigs);
|
||||||
|
60
packages/contract-artifacts/artifacts/DevUtils.json
generated
60
packages/contract-artifacts/artifacts/DevUtils.json
generated
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -175,6 +175,7 @@ export async function runMigrationsAsync(
|
|||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
exchange.address,
|
exchange.address,
|
||||||
|
constants.NULL_ADDRESS,
|
||||||
);
|
);
|
||||||
|
|
||||||
// tslint:disable-next-line:no-unused-variable
|
// tslint:disable-next-line:no-unused-variable
|
||||||
|
@ -113,7 +113,7 @@ export async function runMigrationsAsync(supportedProvider: SupportedProvider, t
|
|||||||
assetProxyArtifacts,
|
assetProxyArtifacts,
|
||||||
);
|
);
|
||||||
|
|
||||||
await ChaiBridgeContract.deployFrom0xArtifactAsync(
|
const chaiBridge = await ChaiBridgeContract.deployFrom0xArtifactAsync(
|
||||||
assetProxyArtifacts.ChaiBridge,
|
assetProxyArtifacts.ChaiBridge,
|
||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
@ -251,6 +251,7 @@ export async function runMigrationsAsync(supportedProvider: SupportedProvider, t
|
|||||||
txDefaults,
|
txDefaults,
|
||||||
devUtilsArtifacts,
|
devUtilsArtifacts,
|
||||||
exchange.address,
|
exchange.address,
|
||||||
|
chaiBridge.address,
|
||||||
);
|
);
|
||||||
|
|
||||||
await CoordinatorContract.deployFrom0xArtifactAsync(
|
await CoordinatorContract.deployFrom0xArtifactAsync(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user