add ERC20Bridge buy support

This commit is contained in:
Michael Zhu
2019-11-15 15:20:42 -08:00
parent 42c4fe5705
commit 49e898b189
6 changed files with 72 additions and 28 deletions

View File

@@ -32,13 +32,13 @@ contract Forwarder is
{
constructor (
address _exchange,
bytes memory _wethAssetData
address _weth
)
public
Ownable()
LibConstants(
_exchange,
_wethAssetData
_weth
)
MixinForwarderCore()
{}

View File

@@ -21,7 +21,6 @@ pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/Ownable.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
import "./libs/LibConstants.sol";
@@ -36,13 +35,10 @@ contract MixinAssets is
{
using LibBytes for bytes;
bytes4 constant internal ERC20_TRANSFER_SELECTOR = bytes4(keccak256("transfer(address,uint256)"));
/// @dev Withdraws assets from this contract. The contract formerly required a ZRX balance in order
/// to function optimally, and this function allows the ZRX to be withdrawn by owner.
/// It may also be used to withdraw assets that were accidentally sent to this contract.
/// @dev Withdraws assets from this contract. It may be used by the owner to withdraw assets
/// that were accidentally sent to this contract.
/// @param assetData Byte array encoded for the respective asset proxy.
/// @param amount Amount of ERC20 token to withdraw.
/// @param amount Amount of the asset to withdraw.
function withdrawAsset(
bytes calldata assetData,
uint256 amount
@@ -64,13 +60,13 @@ contract MixinAssets is
{
bytes4 proxyId = assetData.readBytes4(0);
// For now we only care about ERC20, since percentage fees on ERC721 tokens are invalid.
if (proxyId == ERC20_DATA_ID) {
address proxyAddress = EXCHANGE.getAssetProxy(ERC20_DATA_ID);
if (proxyId == ERC20_PROXY_ID) {
address proxyAddress = EXCHANGE.getAssetProxy(ERC20_PROXY_ID);
if (proxyAddress == address(0)) {
LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError());
}
IERC20Token assetToken = IERC20Token(assetData.readAddress(16));
assetToken.approve(proxyAddress, MAX_UINT);
address token = assetData.readAddress(16);
LibERC20Token.approve(token, proxyAddress, MAX_UINT);
}
}
@@ -85,10 +81,12 @@ contract MixinAssets is
{
bytes4 proxyId = assetData.readBytes4(0);
if (proxyId == ERC20_DATA_ID) {
if (proxyId == ERC20_PROXY_ID) {
_transferERC20Token(assetData, amount);
} else if (proxyId == ERC721_DATA_ID) {
} else if (proxyId == ERC721_PROXY_ID) {
_transferERC721Token(assetData, amount);
} else if (proxyId == ERC20_BRIDGE_PROXY_ID) {
_transferERC20BridgeAsset(assetData, amount);
} else {
LibRichErrors.rrevert(LibForwarderRichErrors.UnsupportedAssetProxyError(
proxyId
@@ -135,4 +133,18 @@ contract MixinAssets is
tokenId
);
}
/// @dev Decodes ERC20Bridge assetData and transfers given amount to sender.
/// @param assetData Byte array encoded for the respective asset proxy.
/// @param amount Amount of asset to transfer to sender.
function _transferERC20BridgeAsset(
bytes memory assetData,
uint256 amount
)
internal
{
address token = assetData.readAddress(16);
// Transfer tokens.
LibERC20Token.transfer(token, msg.sender, amount);
}
}

View File

@@ -88,7 +88,7 @@ contract MixinExchangeWrapper is
)
{
// No taker fee or percentage fee
if (order.takerFee == 0 || order.takerFeeAssetData.equals(order.makerAssetData)) {
if (order.takerFee == 0 || _isPercentageFee(order.takerFeeAssetData, order.makerAssetData)) {
// Attempt to sell the remaining amount of WETH
LibFillResults.FillResults memory singleFillResults = _fillOrderNoThrow(
order,
@@ -228,7 +228,7 @@ contract MixinExchangeWrapper is
makerAssetAcquiredAmount = singleFillResults.makerAssetFilledAmount;
// Percentage fee
} else if (order.takerFeeAssetData.equals(order.makerAssetData)) {
} else if (_isPercentageFee(order.takerFeeAssetData, order.makerAssetData)) {
// Calculate the remaining amount of takerAsset to sell
uint256 remainingTakerAssetFillAmount = LibMath.getPartialAmountCeil(
order.takerAssetAmount,
@@ -316,4 +316,40 @@ contract MixinExchangeWrapper is
));
}
}
/// @dev Checks whether an order's taker fee is effectively denominated in the maker asset.
/// This is the case if they have the same ERC20Proxy asset data, or if the makerAssetData
/// is the ERC20Bridge equivalent of the takerFeeAssetData.
/// @param takerFeeAssetData Byte array encoded for the takerFee asset proxy.
/// @param makerAssetData Byte array encoded for the maker asset proxy.
/// @return isPercentageFee Whether or not the taker fee asset matches the maker asset.
function _isPercentageFee(
bytes memory takerFeeAssetData,
bytes memory makerAssetData
)
internal
pure
returns (bool)
{
bytes4 takerFeeAssetProxyId = takerFeeAssetData.readBytes4(0);
// If the takerFee asset is not ERC20, it cannot be a percentage fee (and will revert with
// an UnsupportedFeeError in the calling function).
if (takerFeeAssetProxyId != ERC20_PROXY_ID) {
return false;
}
bytes4 makerAssetProxyId = makerAssetData.readBytes4(0);
if (makerAssetProxyId == ERC20_PROXY_ID) {
// If the maker asset is ERC20, we can directly compare the asset data.
return takerFeeAssetData.equals(makerAssetData);
} else if (makerAssetProxyId == ERC20_BRIDGE_PROXY_ID) {
// If the maker asset is from an ERC20Bridge, compare the underlying token addresses.
address takerFeeToken = takerFeeAssetData.readAddress(16);
address makerToken = makerAssetData.readAddress(16);
return (takerFeeToken == makerToken);
} else {
// If the maker asset is of any other type, the taker fee cannot be a percentage fee.
return false;
}
}
}

View File

@@ -46,7 +46,7 @@ contract MixinForwarderCore is
constructor ()
public
{
address proxyAddress = EXCHANGE.getAssetProxy(ERC20_DATA_ID);
address proxyAddress = EXCHANGE.getAssetProxy(ERC20_PROXY_ID);
if (proxyAddress == address(0)) {
LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError());
}

View File

@@ -27,8 +27,10 @@ contract LibConstants {
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)"));
bytes4 constant internal ERC20_PROXY_ID = bytes4(keccak256("ERC20Token(address)"));
bytes4 constant internal ERC721_PROXY_ID = bytes4(keccak256("ERC721Token(address,uint256)"));
bytes4 constant internal ERC20_BRIDGE_PROXY_ID = bytes4(keccak256("ERC20Bridge(address,address,bytes)"));
uint256 constant internal MAX_UINT = 2**256 - 1;
uint256 constant internal PERCENTAGE_DENOMINATOR = 10**18;
uint256 constant internal MAX_FEE_PERCENTAGE = 5 * PERCENTAGE_DENOMINATOR / 100; // 5%
@@ -36,19 +38,15 @@ contract LibConstants {
// solhint-disable var-name-mixedcase
IExchange internal EXCHANGE;
IEtherToken internal ETHER_TOKEN;
bytes internal WETH_ASSET_DATA;
// solhint-enable var-name-mixedcase
constructor (
address _exchange,
bytes memory _wethAssetData
address _weth
)
public
{
EXCHANGE = IExchange(_exchange);
WETH_ASSET_DATA = _wethAssetData;
address etherToken = _wethAssetData.readAddress(16);
ETHER_TOKEN = IEtherToken(etherToken);
ETHER_TOKEN = IEtherToken(_weth);
}
}

View File

@@ -18,8 +18,6 @@
pragma solidity ^0.5.9;
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
library LibForwarderRichErrors {