Remove redundant code, add signature checks to order validations, and create DevUtils contract

This commit is contained in:
Amir Bandeali
2019-05-26 16:15:56 -07:00
parent 7f94ebe362
commit 4d9f2586d9
2 changed files with 58 additions and 110 deletions

View File

@@ -0,0 +1,27 @@
/*
Copyright 2018 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.5;
pragma experimental ABIEncoderV2;
import "./OrderValidationUtils.sol";
contract DevUtils is
OrderValidationUtils
{}

View File

@@ -16,23 +16,20 @@
*/
pragma solidity ^0.5.8;
pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-asset-proxy/contracts/src/libs/LibAssetData.sol";
contract OrderValidator {
contract OrderValidationUtils is
LibAssetData
{
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)"));
struct TraderInfo {
uint256 makerBalance; // Maker's balance of makerAsset
uint256 makerAllowance; // Maker's allowance to corresponding AssetProxy
@@ -47,6 +44,7 @@ contract OrderValidator {
// solhint-disable var-name-mixedcase
IExchange internal EXCHANGE;
bytes internal ZRX_ASSET_DATA;
address internal ERC20_PROXY_ADDRESS;
// solhint-enable var-name-mixedcase
constructor (address _exchange, bytes memory _zrxAssetData)
@@ -54,6 +52,7 @@ contract OrderValidator {
{
EXCHANGE = IExchange(_exchange);
ZRX_ASSET_DATA = _zrxAssetData;
ERC20_PROXY_ADDRESS = EXCHANGE.getAssetProxy(ERC20_PROXY_ID);
}
/// @dev Fetches information for order and maker/taker of order.
@@ -127,11 +126,31 @@ contract OrderValidator {
view
returns (TraderInfo memory traderInfo)
{
(traderInfo.makerBalance, traderInfo.makerAllowance) = getBalanceAndAllowance(order.makerAddress, order.makerAssetData);
(traderInfo.takerBalance, traderInfo.takerAllowance) = getBalanceAndAllowance(takerAddress, order.takerAssetData);
bytes4 makerAssetProxyId = order.makerAssetData.readBytes4(0);
bytes4 takerAssetProxyId = order.takerAssetData.readBytes4(0);
(traderInfo.makerBalance, traderInfo.makerAllowance) = getBalanceAndAllowance(
order.makerAddress,
EXCHANGE.getAssetProxy(makerAssetProxyId),
order.makerAssetData
);
(traderInfo.takerBalance, traderInfo.takerAllowance) = getBalanceAndAllowance(
takerAddress,
EXCHANGE.getAssetProxy(takerAssetProxyId),
order.takerAssetData
);
bytes memory zrxAssetData = ZRX_ASSET_DATA;
(traderInfo.makerZrxBalance, traderInfo.makerZrxAllowance) = getBalanceAndAllowance(order.makerAddress, zrxAssetData);
(traderInfo.takerZrxBalance, traderInfo.takerZrxAllowance) = getBalanceAndAllowance(takerAddress, zrxAssetData);
address erc20ProxyAddress = ERC20_PROXY_ADDRESS;
(traderInfo.makerZrxBalance, traderInfo.makerZrxAllowance) = getBalanceAndAllowance(
order.makerAddress,
erc20ProxyAddress,
zrxAssetData
);
(traderInfo.takerZrxBalance, traderInfo.takerZrxAllowance) = getBalanceAndAllowance(
takerAddress,
erc20ProxyAddress,
zrxAssetData
);
return traderInfo;
}
@@ -151,102 +170,4 @@ contract OrderValidator {
}
return tradersInfo;
}
/// @dev Fetches token balances and allowances of an address to given assetProxy. Supports ERC20 and ERC721.
/// @param target Address to fetch balances and allowances of.
/// @param assetData Encoded data that can be decoded by a specified proxy contract when transferring asset.
/// @return Balance of asset and allowance set to given proxy of asset.
/// For ERC721 tokens, these values will always be 1 or 0.
function getBalanceAndAllowance(address target, bytes memory assetData)
public
view
returns (uint256 balance, uint256 allowance)
{
bytes4 assetProxyId = assetData.readBytes4(0);
address token = assetData.readAddress(16);
address assetProxy = EXCHANGE.getAssetProxy(assetProxyId);
if (assetProxyId == ERC20_DATA_ID) {
// Query balance
balance = IERC20Token(token).balanceOf(target);
// Query allowance
allowance = IERC20Token(token).allowance(target, assetProxy);
} else if (assetProxyId == ERC721_DATA_ID) {
uint256 tokenId = assetData.readUint256(36);
// Query owner of tokenId
address owner = getERC721TokenOwner(token, tokenId);
// Set balance to 1 if tokenId is owned by target
balance = target == owner ? 1 : 0;
// Check if ERC721Proxy is approved to spend tokenId
bool isApproved = IERC721Token(token).isApprovedForAll(target, assetProxy);
// Set alowance to 1 if ERC721Proxy is approved to spend tokenId
allowance = isApproved ? 1 : 0;
} else {
revert("UNSUPPORTED_ASSET_PROXY");
}
return (balance, allowance);
}
/// @dev Fetches token balances and allowances of an address for each given assetProxy. Supports ERC20 and ERC721.
/// @param target Address to fetch balances and allowances of.
/// @param assetData Array of encoded byte arrays that can be decoded by a specified proxy contract when transferring asset.
/// @return Balances and allowances of assets.
/// For ERC721 tokens, these values will always be 1 or 0.
function getBalancesAndAllowances(address target, bytes[] memory assetData)
public
view
returns (uint256[] memory, uint256[] memory)
{
uint256 length = assetData.length;
uint256[] memory balances = new uint256[](length);
uint256[] memory allowances = new uint256[](length);
for (uint256 i = 0; i != length; i++) {
(balances[i], allowances[i]) = getBalanceAndAllowance(target, assetData[i]);
}
return (balances, allowances);
}
/// @dev Calls `token.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned token.
/// @param token Address of ERC721 token.
/// @param tokenId The identifier for the specific NFT.
/// @return Owner of tokenId or null address if unowned.
function getERC721TokenOwner(address token, uint256 tokenId)
public
view
returns (address owner)
{
assembly {
// load free memory pointer
let cdStart := mload(64)
// bytes4(keccak256(ownerOf(uint256))) = 0x6352211e
mstore(cdStart, 0x6352211e00000000000000000000000000000000000000000000000000000000)
mstore(add(cdStart, 4), tokenId)
// staticcall `ownerOf(tokenId)`
// `ownerOf` will revert if tokenId is not owned
let success := staticcall(
gas, // forward all gas
token, // call token contract
cdStart, // start of calldata
36, // length of input is 36 bytes
cdStart, // write output over input
32 // size of output is 32 bytes
)
// Success implies that tokenId is owned
// Copy owner from return data if successful
if success {
owner := mload(cdStart)
}
}
// Owner initialized to address(0), no need to modify if call is unsuccessful
return owner;
}
}