Refactor contracts to only use allowances for AssetProxies (spender is no longer an input)

This commit is contained in:
Amir Bandeali
2019-06-05 10:41:25 -07:00
parent 12afeb30ae
commit be6fce5a89
2 changed files with 217 additions and 281 deletions

View File

@@ -24,6 +24,7 @@ import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
import "@0x/contracts-asset-proxy/contracts/src/libs/LibAssetProxyIds.sol";
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
contract LibAssetData is
@@ -33,68 +34,74 @@ contract LibAssetData is
using LibBytes for bytes;
/// @dev Returns the owner's balance of the token(s) specified in
/// assetData. When the asset data contains multiple tokens (eg in
/// ERC1155 or Multi-Asset), the return value indicates how many
/// complete "baskets" of those tokens are owned by owner.
/// @param owner Owner of the tokens specified by assetData.
/// @param assetData Description of tokens, per the AssetProxy contract
/// specification.
/// @return Number of tokens (or token baskets) held by owner.
function getBalance(address owner, bytes memory assetData)
// solhint-disable var-name-mixedcase
IExchange internal _EXCHANGE;
address internal _ERC20_PROXY_ADDRESS;
address internal _ERC721_PROXY_ADDRESS;
address internal _ERC1155_PROXY_ADDRESS;
// solhint-enable var-name-mixedcase
constructor (address _exchange)
public
{
_EXCHANGE = IExchange(_exchange);
_ERC20_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC20_PROXY_ID);
_ERC721_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC721_PROXY_ID);
_ERC1155_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC1155_PROXY_ID);
}
/// @dev Returns the owner's balance of the assets(s) specified in
/// assetData. When the asset data contains multiple assets (eg in
/// ERC1155 or Multi-Asset), the return value indicates how many
/// complete "baskets" of those assets are owned by owner.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
/// @return Number of assets (or asset baskets) held by owner.
function getBalance(address ownerAddress, bytes memory assetData)
public
view
returns (uint256 balance)
{
bytes4 proxyId = assetData.readBytes4(0);
bytes4 assetProxyId = assetData.readBytes4(0);
if (proxyId == ERC20_PROXY_ID) {
if (assetProxyId == ERC20_PROXY_ID) {
address tokenAddress = assetData.readAddress(16);
balance = IERC20Token(tokenAddress).balanceOf(owner);
} else if (proxyId == ERC721_PROXY_ID) {
balance = IERC20Token(tokenAddress).balanceOf(ownerAddress);
} else if (assetProxyId == ERC721_PROXY_ID) {
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
balance = getERC721TokenOwner(tokenAddress, tokenId) == owner ? 1 : 0;
} else if (proxyId == ERC1155_PROXY_ID) {
(
,
address tokenAddress,
uint256[] memory tokenIds,
uint256[] memory tokenValues,
) = decodeERC1155AssetData(assetData);
for (uint256 i = 0; i < tokenIds.length; i++) {
uint256 totalBalance = IERC1155(tokenAddress).balanceOf(owner, tokenIds[i]);
balance = getERC721TokenOwner(tokenAddress, tokenId) == ownerAddress ? 1 : 0;
} else if (assetProxyId == ERC1155_PROXY_ID) {
(, address tokenAddress, uint256[] memory tokenIds, uint256[] memory tokenValues,) = decodeERC1155AssetData(assetData);
uint256 length = tokenIds.length;
for (uint256 i = 0; i != length; i++) {
uint256 totalBalance = IERC1155(tokenAddress).balanceOf(ownerAddress, tokenIds[i]);
uint256 scaledBalance = totalBalance / tokenValues[i];
if (scaledBalance < balance || balance == 0) {
balance = scaledBalance;
}
}
} else if (proxyId == MULTI_ASSET_PROXY_ID) {
(
,
uint256[] memory assetAmounts,
bytes[] memory nestedAssetData
) = decodeMultiAssetData(assetData);
for (uint256 i = 0; i < nestedAssetData.length; i++) {
uint256 totalBalance = getBalance(owner, nestedAssetData[i]);
} else if (assetProxyId == MULTI_ASSET_PROXY_ID) {
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
uint256 length = nestedAssetData.length;
for (uint256 i = 0; i != length; i++) {
uint256 totalBalance = getBalance(ownerAddress, nestedAssetData[i]);
uint256 scaledBalance = totalBalance / assetAmounts[i];
if (scaledBalance < balance || balance == 0) {
balance = scaledBalance;
}
}
} else {
revert("UNSUPPORTED_PROXY_ID");
}
}
// Balance will be 0 if assetProxyId is unknown
return balance;
}
/// @dev Calls getBalance() for each element of assetData.
/// @param owner Owner of the tokens specified by assetData.
/// @param assetData Array of token descriptors, each encoded per the
/// AssetProxy contract specification.
/// @return Array of token balances from getBalance(), with each element
/// corresponding to the same-indexed element in the assetData input.
function getBatchBalances(address owner, bytes[] memory assetData)
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
/// @return Array of asset balances from getBalance(), with each element
/// corresponding to the same-indexed element in the assetData input.
function getBatchBalances(address ownerAddress, bytes[] memory assetData)
public
view
returns (uint256[] memory balances)
@@ -102,83 +109,65 @@ contract LibAssetData is
uint256 length = assetData.length;
balances = new uint256[](length);
for (uint256 i = 0; i != length; i++) {
balances[i] = getBalance(owner, assetData[i]);
balances[i] = getBalance(ownerAddress, assetData[i]);
}
return balances;
}
/// @dev Returns the number of token(s) (described by assetData) that
/// spender is authorized to spend. When the asset data contains
/// multiple tokens (eg for Multi-Asset), the return value indicates
/// how many complete "baskets" of those tokens may be spent by spender.
/// @param owner Owner of the tokens specified by assetData.
/// @param spender Address whose authority to spend is in question.
/// @param assetData Description of tokens, per the AssetProxy contract
/// specification.
/// @return Number of tokens (or token baskets) that the spender is
/// authorized to spend.
function getAllowance(
address owner,
address spender,
bytes memory assetData
)
/// @dev Returns the number of asset(s) (described by assetData) that
/// the corresponding AssetProxy contract is authorized to spend. When the asset data contains
/// multiple assets (eg for Multi-Asset), the return value indicates
/// how many complete "baskets" of those assets may be spent by all of the corresponding
/// AssetProxy contracts.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
/// @return Number of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend.
function getAssetProxyAllowance(address ownerAddress, bytes memory assetData)
public
view
returns (uint256 allowance)
{
bytes4 proxyId = assetData.readBytes4(0);
bytes4 assetProxyId = assetData.readBytes4(0);
if (proxyId == ERC20_PROXY_ID) {
address tokenAddress = assetData.readAddress(16);
allowance = IERC20Token(tokenAddress).allowance(owner, spender);
} else if (proxyId == ERC721_PROXY_ID) {
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
IERC721Token token = IERC721Token(tokenAddress);
if (token.isApprovedForAll(owner, spender)) {
allowance = _MAX_UINT256;
} else if (token.getApproved(tokenId) == spender) {
allowance = 1;
}
} else if (proxyId == ERC1155_PROXY_ID) {
(, address tokenAddress, , , ) = decodeERC1155AssetData(assetData);
allowance = IERC1155(tokenAddress).isApprovedForAll(owner, spender) ? _MAX_UINT256 : 0;
} else if (proxyId == MULTI_ASSET_PROXY_ID) {
(
,
uint256[] memory amounts,
bytes[] memory nestedAssetData
) = decodeMultiAssetData(assetData);
for (uint256 i = 0; i < nestedAssetData.length; i++) {
uint256 totalAllowance = getAllowance(
owner,
spender,
nestedAssetData[i]
);
if (assetProxyId == MULTI_ASSET_PROXY_ID) {
(, uint256[] memory amounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
uint256 length = nestedAssetData.length;
for (uint256 i = 0; i != length; i++) {
uint256 totalAllowance = getAssetProxyAllowance(ownerAddress, nestedAssetData[i]);
uint256 scaledAllowance = totalAllowance / amounts[i];
if (scaledAllowance < allowance || allowance == 0) {
allowance = scaledAllowance;
}
}
} else {
revert("UNSUPPORTED_PROXY_ID");
}
if (assetProxyId == ERC20_PROXY_ID) {
address tokenAddress = assetData.readAddress(16);
allowance = IERC20Token(tokenAddress).allowance(ownerAddress, _ERC20_PROXY_ADDRESS);
} else if (assetProxyId == ERC721_PROXY_ID) {
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
IERC721Token token = IERC721Token(tokenAddress);
address assetProxyAddress = _ERC721_PROXY_ADDRESS;
if (token.isApprovedForAll(ownerAddress, assetProxyAddress)) {
allowance = _MAX_UINT256;
} else if (token.getApproved(tokenId) == assetProxyAddress) {
allowance = 1;
}
} else if (assetProxyId == ERC1155_PROXY_ID) {
(, address tokenAddress, , , ) = decodeERC1155AssetData(assetData);
allowance = IERC1155(tokenAddress).isApprovedForAll(ownerAddress, _ERC1155_PROXY_ADDRESS) ? _MAX_UINT256 : 0;
}
// Allowance will be 0 if the assetProxyId is unknown
return allowance;
}
/// @dev Calls getAllowance() for each element of assetData.
/// @param owner Owner of the tokens specified by assetData.
/// @param spenders Array of addresses whose authority to spend is in question for each corresponding assetData.
/// @param assetData Description of tokens, per the AssetProxy contract
/// specification.
/// @return An array of token allowances from getAllowance(), with each
/// element corresponding to the same-indexed element in the assetData
/// input.
function getBatchAllowances(
address owner,
address[] memory spenders,
bytes[] memory assetData
)
/// @dev Calls getAssetProxyAllowance() for each element of assetData.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
/// @return An array of asset allowances from getAllowance(), with each
/// element corresponding to the same-indexed element in the assetData input.
function getBatchAssetProxyAllowances(address ownerAddress, bytes[] memory assetData)
public
view
returns (uint256[] memory allowances)
@@ -186,63 +175,44 @@ contract LibAssetData is
uint256 length = assetData.length;
allowances = new uint256[](length);
for (uint256 i = 0; i != length; i++) {
allowances[i] = getAllowance(owner, spenders[i], assetData[i]);
allowances[i] = getAssetProxyAllowance(ownerAddress, assetData[i]);
}
return allowances;
}
/// @dev Calls getBalance() and getAllowance() for assetData.
/// @param owner Owner of the tokens specified by assetData.
/// @param spender Address whose authority to spend is in question.
/// @param assetData Description of tokens, per the AssetProxy contract
/// specification.
/// @return Number of tokens (or token baskets) held by owner, and number
/// of tokens (or token baskets) that the spender is authorized to
/// spend.
function getBalanceAndAllowance(
address owner,
address spender,
bytes memory assetData
)
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Details of asset, encoded per the AssetProxy contract specification.
/// @return Number of assets (or asset baskets) held by owner, and number
/// 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(owner, assetData);
allowance = getAllowance(owner, spender, assetData);
balance = getBalance(ownerAddress, assetData);
allowance = getAssetProxyAllowance(ownerAddress, assetData);
return (balance, allowance);
}
/// @dev Calls getBatchBalances() and getBatchAllowances() for each element
/// of assetData.
/// @param owner Owner of the tokens specified by assetData.
/// @param spenders Array of addresses whose authority to spend is in question for each corresponding assetData.
/// @param assetData Description of tokens, per the AssetProxy contract
/// specification.
/// @return An array of token balances from getBalance(), and an array of
/// token allowances from getAllowance(), with each element
/// corresponding to the same-indexed element in the assetData input.
function getBatchBalancesAndAllowances(
address owner,
address[] memory spenders,
bytes[] memory assetData
)
/// @dev Calls getBatchBalances() and getBatchAllowances() for each element of assetData.
/// @param ownerAddress Owner of the assets specified by assetData.
/// @param assetData Array of asset details, each encoded per the AssetProxy contract specification.
/// @return An array of asset balances from getBalance(), and an array of
/// asset allowances from getAllowance(), with each element
/// 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
)
returns (uint256[] memory balances, uint256[] memory allowances)
{
balances = getBatchBalances(owner, assetData);
allowances = getBatchAllowances(owner, spenders, assetData);
balances = getBatchBalances(ownerAddress, assetData);
allowances = getBatchAssetProxyAllowances(ownerAddress, assetData);
return (balances, allowances);
}
/// @dev Encode ERC-20 asset data into the format described in the
/// AssetProxy contract specification.
/// @param tokenAddress The address of the ERC-20 contract hosting the
/// token to be traded.
/// @dev Encode ERC-20 asset data into the format described in the AssetProxy contract specification.
/// @param tokenAddress The address of the ERC-20 contract hosting the asset to be traded.
/// @return AssetProxy-compliant data describing the asset.
function encodeERC20AssetData(address tokenAddress)
public
@@ -253,41 +223,34 @@ contract LibAssetData is
return assetData;
}
/// @dev Decode ERC-20 asset data from the format described in the
/// AssetProxy contract specification.
/// @param assetData AssetProxy-compliant asset data describing an ERC-20
/// asset.
/// @return The ERC-20 AssetProxy identifier, and the address of the ERC-20
/// contract hosting this asset.
/// @dev Decode ERC-20 asset data from the format described in the AssetProxy contract specification.
/// @param assetData AssetProxy-compliant asset data describing an ERC-20 asset.
/// @return The ERC-20 AssetProxy identifier, and the address of the ERC-20
/// contract hosting this asset.
function decodeERC20AssetData(bytes memory assetData)
public
pure
returns (
bytes4 proxyId,
bytes4 assetProxyId,
address tokenAddress
)
{
proxyId = assetData.readBytes4(0);
assetProxyId = assetData.readBytes4(0);
require(
proxyId == ERC20_PROXY_ID,
assetProxyId == ERC20_PROXY_ID,
"WRONG_PROXY_ID"
);
tokenAddress = assetData.readAddress(16);
return (proxyId, tokenAddress);
return (assetProxyId, tokenAddress);
}
/// @dev Encode ERC-721 asset data into the format described in the
/// AssetProxy specification.
/// @param tokenAddress The address of the ERC-721 contract hosting the
/// token to be traded.
/// @param tokenId The identifier of the specific token to be traded.
/// @dev Encode ERC-721 asset data into the format described in the AssetProxy specification.
/// @param tokenAddress The address of the ERC-721 contract hosting the asset to be traded.
/// @param tokenId The identifier of the specific asset to be traded.
/// @return AssetProxy-compliant asset data describing the asset.
function encodeERC721AssetData(
address tokenAddress,
uint256 tokenId
)
function encodeERC721AssetData(address tokenAddress, uint256 tokenId)
public
pure
returns (bytes memory assetData)
@@ -300,41 +263,37 @@ contract LibAssetData is
return assetData;
}
/// @dev Decode ERC-721 asset data from the format described in the
/// AssetProxy contract specification.
/// @param assetData AssetProxy-compliant asset data describing an ERC-721
/// asset.
/// @dev Decode ERC-721 asset data from the format described in the AssetProxy contract specification.
/// @param assetData AssetProxy-compliant asset data describing an ERC-721 asset.
/// @return The ERC-721 AssetProxy identifier, the address of the ERC-721
/// contract hosting this asset, and the identifier of the specific
/// token to be traded.
/// contract hosting this asset, and the identifier of the specific
/// asset to be traded.
function decodeERC721AssetData(bytes memory assetData)
public
pure
returns (
bytes4 proxyId,
bytes4 assetProxyId,
address tokenAddress,
uint256 tokenId
)
{
proxyId = assetData.readBytes4(0);
assetProxyId = assetData.readBytes4(0);
require(
proxyId == ERC721_PROXY_ID,
assetProxyId == ERC721_PROXY_ID,
"WRONG_PROXY_ID"
);
tokenAddress = assetData.readAddress(16);
tokenId = assetData.readUint256(36);
return (proxyId, tokenAddress, tokenId);
return (assetProxyId, tokenAddress, tokenId);
}
/// @dev Encode ERC-1155 asset data into the format described in the
/// AssetProxy contract specification.
/// @param tokenAddress The address of the ERC-1155 contract hosting the
/// token(s) to be traded.
/// @param tokenIds The identifiers of the specific tokens to be traded.
/// @param tokenValues The amounts of each token to be traded.
/// @param callbackData ...
/// @dev Encode ERC-1155 asset data into the format described in the AssetProxy contract specification.
/// @param tokenAddress The address of the ERC-1155 contract hosting the asset(s) to be traded.
/// @param tokenIds The identifiers of the specific assets to be traded.
/// @param tokenValues The amounts of each asset to be traded.
/// @param callbackData Data to be passed to receiving contracts when a transfer is performed.
/// @return AssetProxy-compliant asset data describing the set of assets.
function encodeERC1155AssetData(
address tokenAddress,
@@ -356,32 +315,30 @@ contract LibAssetData is
return assetData;
}
/// @dev Decode ERC-1155 asset data from the format described in the
/// AssetProxy contract specification.
/// @param assetData AssetProxy-compliant asset data describing an ERC-1155
/// set of assets.
/// @dev Decode ERC-1155 asset data from the format described in the AssetProxy contract specification.
/// @param assetData AssetProxy-compliant asset data describing an ERC-1155 set of assets.
/// @return The ERC-1155 AssetProxy identifier, the address of the ERC-1155
/// contract hosting the assets, an array of the identifiers of the
/// tokens to be traded, an array of token amounts to be traded, and
/// callback data. Each element of the arrays corresponds to the
/// same-indexed element of the other array. Return values specified as
/// `memory` are returned as pointers to locations within the memory of
/// the input parameter `assetData`.
/// contract hosting the assets, an array of the identifiers of the
/// assets to be traded, an array of asset amounts to be traded, and
/// callback data. Each element of the arrays corresponds to the
/// same-indexed element of the other array. Return values specified as
/// `memory` are returned as pointers to locations within the memory of
/// the input parameter `assetData`.
function decodeERC1155AssetData(bytes memory assetData)
public
pure
returns (
bytes4 proxyId,
bytes4 assetProxyId,
address tokenAddress,
uint256[] memory tokenIds,
uint256[] memory tokenValues,
bytes memory callbackData
)
{
proxyId = assetData.readBytes4(0);
assetProxyId = assetData.readBytes4(0);
require(
proxyId == ERC1155_PROXY_ID,
assetProxyId == ERC1155_PROXY_ID,
"WRONG_PROXY_ID"
);
@@ -399,7 +356,7 @@ contract LibAssetData is
}
return (
proxyId,
assetProxyId,
tokenAddress,
tokenIds,
tokenValues,
@@ -407,16 +364,11 @@ contract LibAssetData is
);
}
/// @dev Encode data for multiple assets, per the AssetProxy contract
/// specification.
/// @dev Encode data for multiple assets, per the AssetProxy contract specification.
/// @param amounts The amounts of each asset to be traded.
/// @param nestedAssetData AssetProxy-compliant data describing each asset
/// to be traded.
/// @param nestedAssetData AssetProxy-compliant data describing each asset to be traded.
/// @return AssetProxy-compliant data describing the set of assets.
function encodeMultiAssetData(
uint256[] memory amounts,
bytes[] memory nestedAssetData
)
function encodeMultiAssetData(uint256[] memory amounts, bytes[] memory nestedAssetData)
public
pure
returns (bytes memory assetData)
@@ -429,28 +381,25 @@ contract LibAssetData is
return assetData;
}
/// @dev Decode multi-asset data from the format described in the
/// AssetProxy contract specification.
/// @param assetData AssetProxy-compliant data describing a multi-asset
/// basket.
/// @dev Decode multi-asset data from the format described in the AssetProxy contract specification.
/// @param assetData AssetProxy-compliant data describing a multi-asset basket.
/// @return The Multi-Asset AssetProxy identifier, an array of the amounts
/// of the assets to be traded, and an array of the
/// AssetProxy-compliant data describing each asset to be traded. Each
/// element of the arrays corresponds to the same-indexed element of
/// the other array.
/// of the assets to be traded, and an array of the
/// AssetProxy-compliant data describing each asset to be traded. Each
/// element of the arrays corresponds to the same-indexed element of the other array.
function decodeMultiAssetData(bytes memory assetData)
public
pure
returns (
bytes4 proxyId,
bytes4 assetProxyId,
uint256[] memory amounts,
bytes[] memory nestedAssetData
)
{
proxyId = assetData.readBytes4(0);
assetProxyId = assetData.readBytes4(0);
require(
proxyId == MULTI_ASSET_PROXY_ID,
assetProxyId == MULTI_ASSET_PROXY_ID,
"WRONG_PROXY_ID"
);
@@ -462,14 +411,14 @@ contract LibAssetData is
// solhint-enable indent
}
/// @dev Calls `token.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned token.
/// @param tokenAddress Address of ERC721 token.
/// @dev Calls `asset.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned asset.
/// @param tokenAddress Address of ERC721 asset.
/// @param tokenId The identifier for the specific NFT.
/// @return Owner of tokenId or null address if unowned.
function getERC721TokenOwner(address tokenAddress, uint256 tokenId)
public
view
returns (address owner)
returns (address ownerAddress)
{
bytes memory ownerOfCalldata = abi.encodeWithSelector(
0x6352211e,
@@ -478,7 +427,7 @@ contract LibAssetData is
(bool success, bytes memory returnData) = tokenAddress.staticcall(ownerOfCalldata);
owner = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0);
return owner;
ownerAddress = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0);
return ownerAddress;
}
}

View File

@@ -23,7 +23,7 @@ import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-asset-proxy/contracts/src/libs/LibAssetData.sol";
import "./LibAssetData.sol";
contract OrderValidationUtils is
@@ -33,23 +33,26 @@ contract OrderValidationUtils is
using LibBytes for bytes;
// solhint-disable var-name-mixedcase
IExchange internal EXCHANGE;
bytes internal ZRX_ASSET_DATA;
address internal ERC20_PROXY_ADDRESS;
bytes internal _ZRX_ASSET_DATA;
// solhint-enable var-name-mixedcase
constructor (address _exchange, bytes memory _zrxAssetData)
public
LibAssetData(_exchange)
{
EXCHANGE = IExchange(_exchange);
ZRX_ASSET_DATA = _zrxAssetData;
ERC20_PROXY_ADDRESS = EXCHANGE.getAssetProxy(ERC20_PROXY_ID);
_ZRX_ASSET_DATA = _zrxAssetData;
}
/// @dev Fetches information for order and maker/taker of order.
/// @dev Fetches all order-relevant information needed to validate if the supplied order is fillable.
/// @param order The order structure.
/// @param signature Proof that order has been created by maker.
/// @return OrderInfo, the remaining amount fillable by the taker, and validity of signature for given order.
/// @param signature Signature provided by maker that proves the order's authenticity.
/// `0x01` can always be provided if the signature does not need to be validated.
/// @return orderInfo The hash, status, and `takerAssetAmount` already filled for the given order.
/// @return fillableTakerAssetAmount The amount of the order's `takerAssetAmount` that is fillable given all on-chain state.
/// If the `takerAssetData` encodes data for multiple assets, `fillableTakerAssetAmount` will represent a "scaled"
/// amount, meaning it must be multiplied by all the individual asset amounts within the `takerAssetData` to get the final
/// amount of each asset that can be filled.
/// @return isValidSignature The validity of the provided signature.
function getOrderRelevantState(LibOrder.Order memory order, bytes memory signature)
public
view
@@ -60,74 +63,80 @@ contract OrderValidationUtils is
)
{
// Get info specific to order
orderInfo = EXCHANGE.getOrderInfo(order);
orderInfo = _EXCHANGE.getOrderInfo(order);
// Validate the maker's signature
// If the signature does not need to be validated, `0x01` can be supplied for the signature to always return `false`.
address makerAddress = order.makerAddress;
isValidSignature = EXCHANGE.isValidSignature(
isValidSignature = _EXCHANGE.isValidSignature(
orderInfo.orderHash,
makerAddress,
signature
);
// Get the transferable amount of the `makerAsset`
uint256 transferableMakerAssetAmount = getTransferableAssetAmount(order.makerAssetData, makerAddress);
uint256 transferableMakerAssetAmount = getTransferableAssetAmount(makerAddress, order.makerAssetData);
// Assign to stack variables to reduce redundant mloads
// Assign to stack variables to reduce redundant mloads/sloads
uint256 takerAssetAmount = order.takerAssetAmount;
uint256 makerFee = order.makerFee;
bytes memory zrxAssetData = _ZRX_ASSET_DATA;
// Get the amount of `takerAsset` that is purchasable given the transferability of `makerAsset` and `makerFeeAsset`
uint256 purchasableTakerAssetAmount;
if (order.makerAssetData.equals(ZRX_ASSET_DATA)) {
// Get the amount of `takerAsset` that is transferable to maker given the transferability of `makerAsset` and `makerFeeAsset`
// and the total amounts specified in the order
uint256 transferableTakerAssetAmount;
if (order.makerAssetData.equals(zrxAssetData)) {
// If `makerAsset` equals `makerFeeAsset`, the % that can be filled is
// transferableMakerAssetAmount / (makerAssetAmount + makerFee)
purchasableTakerAssetAmount = getPartialAmountFloor(
transferableTakerAssetAmount = getPartialAmountFloor(
transferableMakerAssetAmount,
safeAdd(order.makerAssetAmount, makerFee),
takerAssetAmount
);
} else {
// Get the transferable amount of the `makerFeeAsset`
uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount(ZRX_ASSET_DATA, makerAddress);
uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount(makerAddress, zrxAssetData);
// If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of
// (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee)
// If `makerFee` is 0, we default to using `transferableMakerAssetAmount`
purchasableTakerAssetAmount = makerFee == 0
? getPartialAmountFloor(
// If `makerFee` is 0, the % that can be filled is (transferableMakerAssetAmount / makerAssetAmount)
if (makerFee == 0) {
transferableTakerAssetAmount = getPartialAmountFloor(
transferableMakerAssetAmount,
order.makerAssetAmount,
takerAssetAmount
)
: min256(
getPartialAmountFloor(
transferableMakerAssetAmount,
order.makerAssetAmount,
takerAssetAmount
),
getPartialAmountFloor(
transferableMakerFeeAssetAmount,
makerFee,
takerAssetAmount
)
);
// If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of
// (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee)
} else {
uint256 transferableMakerToTakerAmount = getPartialAmountFloor(
transferableMakerAssetAmount,
order.makerAssetAmount,
takerAssetAmount
);
uint256 transferableMakerFeeToTakerAmount = getPartialAmountFloor(
transferableMakerFeeAssetAmount,
makerFee,
takerAssetAmount
);
transferableTakerAssetAmount = min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount);
}
}
// `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `purchasableTakerAssetAmount`
// `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `transferableTakerAssetAmount`
fillableTakerAssetAmount = min256(
safeSub(takerAssetAmount, orderInfo.orderTakerAssetFilledAmount),
purchasableTakerAssetAmount
transferableTakerAssetAmount
);
return (orderInfo, fillableTakerAssetAmount, isValidSignature);
}
/// @dev Fetches information for all passed in orders and the makers/takers of each order.
/// @param orders Array of order specifications.
/// @param signatures Proofs that orders have been created by makers.
/// @return Arrays of OrderInfo, fillable takerAssetAmounts, and validity of signatures that correspond to each order.
/// @dev Fetches all order-relevant information needed to validate if the supplied orders are fillable.
/// @param orders Array of order structures.
/// @param signatures Array of signatures provided by makers that prove the authenticity of the orders.
/// `0x01` can always be provided if a signature does not need to be validated.
/// @return ordersInfo Array of the hash, status, and `takerAssetAmount` already filled for each order.
/// @return fillableTakerAssetAmounts Array of amounts for each order's `takerAssetAmount` that is fillable given all on-chain state.
/// @return isValidSignature Array containing the validity of each provided signature.
function getOrderRelevantStates(LibOrder.Order[] memory orders, bytes[] memory signatures)
public
view
@@ -152,39 +161,17 @@ contract OrderValidationUtils is
return (ordersInfo, fillableTakerAssetAmounts, isValidSignature);
}
/// @dev Gets the address of the AssetProxy that corresponds to the given assetData.
/// @param assetData Description of tokens, per the AssetProxy contract specification.
/// @return Address of the AssetProxy contract.
function getAssetProxyAddress(bytes memory assetData)
public
view
returns (address assetProxyAddress)
{
if (assetData.equals(ZRX_ASSET_DATA)) {
return ERC20_PROXY_ADDRESS;
}
bytes4 assetProxyId = assetData.readBytes4(0);
assetProxyAddress = EXCHANGE.getAssetProxy(assetProxyId);
return assetProxyAddress;
}
/// @dev Gets the amount of an asset transferable by the owner.
/// @param assetData Description of tokens, per the AssetProxy contract specification.
/// @param ownerAddress Address of the owner of the asset.
/// @param assetData Description of tokens, per the AssetProxy contract specification.
/// @return The amount of the asset tranferable by the owner.
function getTransferableAssetAmount(bytes memory assetData, address ownerAddress)
function getTransferableAssetAmount(address ownerAddress, bytes memory assetData)
public
view
returns (uint256 transferableAssetAmount)
{
uint256 assetBalance = getBalance(ownerAddress, assetData);
address assetProxyAddress = getAssetProxyAddress(assetData);
uint256 assetAllowance = getAllowance(
ownerAddress,
assetProxyAddress,
assetData
);
transferableAssetAmount = min256(assetBalance, assetAllowance);
(uint256 balance, uint256 allowance) = getBalanceAndAssetProxyAllowance(ownerAddress, assetData);
transferableAssetAmount = min256(balance, allowance);
return transferableAssetAmount;
}
}