Forwarder StaticCall and MultiAsset buy support

This commit is contained in:
Michael Zhu 2019-12-16 11:23:48 -08:00 committed by Amir
parent 8b27380feb
commit 2b8c6dc8f9
2 changed files with 52 additions and 7 deletions

View File

@ -20,6 +20,7 @@ pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol"; import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "@0x/contracts-utils/contracts/src/Ownable.sol"; import "@0x/contracts-utils/contracts/src/Ownable.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol"; import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
@ -35,6 +36,7 @@ contract MixinAssets is
IAssets IAssets
{ {
using LibBytes for bytes; using LibBytes for bytes;
using LibSafeMath for uint256;
/// @dev Withdraws assets from this contract. It may be used by the owner to withdraw assets /// @dev Withdraws assets from this contract. It may be used by the owner to withdraw assets
/// that were accidentally sent to this contract. /// that were accidentally sent to this contract.
@ -95,7 +97,9 @@ contract MixinAssets is
_transferERC20Token(assetData, amount); _transferERC20Token(assetData, amount);
} else if (proxyId == IAssetData(address(0)).ERC721Token.selector) { } else if (proxyId == IAssetData(address(0)).ERC721Token.selector) {
_transferERC721Token(assetData, amount); _transferERC721Token(assetData, amount);
} else { } else if (proxyId == IAssetData(address(0)).MultiAsset.selector) {
_transferMultiAsset(assetData, amount);
} else if (proxyId != IAssetData(address(0)).StaticCall.selector) {
LibRichErrors.rrevert(LibForwarderRichErrors.UnsupportedAssetProxyError( LibRichErrors.rrevert(LibForwarderRichErrors.UnsupportedAssetProxyError(
proxyId proxyId
)); ));
@ -141,4 +145,23 @@ contract MixinAssets is
tokenId tokenId
); );
} }
function _transferMultiAsset(
bytes memory assetData,
uint256 amount
)
internal
{
// solhint-disable indent
(uint256[] memory nestedAmounts, bytes[] memory nestedAssetData) = abi.decode(
assetData.slice(4, assetData.length),
(uint256[], bytes[])
);
// solhint-enable indent
uint256 numNestedAssets = nestedAssetData.length;
for (uint256 i = 0; i != numNestedAssets; i++) {
_transferAssetToSender(nestedAssetData[i], amount.safeMul(nestedAmounts[i]));
}
}
} }

View File

@ -88,9 +88,10 @@ contract MixinExchangeWrapper is
uint256 makerAssetAcquiredAmount uint256 makerAssetAcquiredAmount
) )
{ {
bool noTakerFee = _noTakerFee(order.takerFee, order.takerFeeAssetData);
// No taker fee or percentage fee // No taker fee or percentage fee
if ( if (
order.takerFee == 0 || noTakerFee ||
_areUnderlyingAssetsEqual(order.takerFeeAssetData, order.makerAssetData) _areUnderlyingAssetsEqual(order.takerFeeAssetData, order.makerAssetData)
) { ) {
// Attempt to sell the remaining amount of WETH // Attempt to sell the remaining amount of WETH
@ -105,7 +106,7 @@ contract MixinExchangeWrapper is
// Subtract fee from makerAssetFilledAmount for the net amount acquired. // Subtract fee from makerAssetFilledAmount for the net amount acquired.
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount
.safeSub(singleFillResults.takerFeePaid); .safeSub(noTakerFee ? 0 : singleFillResults.takerFeePaid);
// WETH fee // WETH fee
} else if (_areUnderlyingAssetsEqual(order.takerFeeAssetData, order.takerAssetData)) { } else if (_areUnderlyingAssetsEqual(order.takerFeeAssetData, order.takerAssetData)) {
@ -230,9 +231,10 @@ contract MixinExchangeWrapper is
uint256 makerAssetAcquiredAmount uint256 makerAssetAcquiredAmount
) )
{ {
bool noTakerFee = _noTakerFee(order.takerFee, order.takerFeeAssetData);
// No taker fee or WETH fee // No taker fee or WETH fee
if ( if (
order.takerFee == 0 || noTakerFee ||
_areUnderlyingAssetsEqual(order.takerFeeAssetData, order.takerAssetData) _areUnderlyingAssetsEqual(order.takerFeeAssetData, order.takerAssetData)
) { ) {
// Calculate the remaining amount of takerAsset to sell // Calculate the remaining amount of takerAsset to sell
@ -251,7 +253,7 @@ contract MixinExchangeWrapper is
// WETH is also spent on the protocol and taker fees, so we add it here. // WETH is also spent on the protocol and taker fees, so we add it here.
wethSpentAmount = singleFillResults.takerAssetFilledAmount wethSpentAmount = singleFillResults.takerAssetFilledAmount
.safeAdd(singleFillResults.takerFeePaid) .safeAdd(noTakerFee ? 0 : singleFillResults.takerFeePaid)
.safeAdd(singleFillResults.protocolFeePaid); .safeAdd(singleFillResults.protocolFeePaid);
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount; makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount;
@ -419,7 +421,7 @@ contract MixinExchangeWrapper is
return fillResults; return fillResults;
} }
/// @dev Fills the input ExchangeV3 order. /// @dev Fills the input ExchangeV3 order.
/// Returns false if the transaction would otherwise revert. /// Returns false if the transaction would otherwise revert.
/// @param order Order struct containing order specifications. /// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell. /// @param takerAssetFillAmount Desired amount of takerAsset to sell.
@ -480,7 +482,7 @@ contract MixinExchangeWrapper is
address token2 = assetData2.readAddress(16); address token2 = assetData2.readAddress(16);
return (token1 == token2); return (token1 == token2);
} else { } else {
return false; return assetData1.equals(assetData2);
} }
} }
@ -494,4 +496,24 @@ contract MixinExchangeWrapper is
{ {
return order.makerFeeAssetData.length > 3 && order.makerFeeAssetData.readBytes4(0) == EXCHANGE_V2_ORDER_ID; return order.makerFeeAssetData.length > 3 && order.makerFeeAssetData.readBytes4(0) == EXCHANGE_V2_ORDER_ID;
} }
/// @dev Checks whether one asset is effectively equal to another asset.
/// This is the case if they have the same ERC20Proxy/ERC20BridgeProxy asset data, or if
/// one is the ERC20Bridge equivalent of the other.
/// @param takerFee Byte array encoded for the takerFee asset proxy.
/// @param takerFeeAssetData Byte array encoded for the maker asset proxy.
/// @return Whether or not the underlying assets are equal.
function _noTakerFee(
uint256 takerFee,
bytes memory takerFeeAssetData
)
internal
pure
returns (bool)
{
return (
takerFee == 0 ||
takerFeeAssetData.readBytes4(0) == IAssetData(address(0)).StaticCall.selector
);
}
} }