Remove redundant code, add signature checks to order validations, and create DevUtils contract
This commit is contained in:
27
contracts/extensions/contracts/src/DevUtils/DevUtils.sol
Normal file
27
contracts/extensions/contracts/src/DevUtils/DevUtils.sol
Normal 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
|
||||
{}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user