Fix rounding error issues, use different logic when makerAsset is ZRX
This commit is contained in:
@@ -31,4 +31,5 @@ contract LibForwarderErrors {
|
||||
string constant DEFAULT_FUNCTION_WETH_CONTRACT_ONLY = "DEFAULT_FUNCTION_WETH_CONTRACT_ONLY"; // Fallback function may only be used for WETH withdrawals.
|
||||
string constant INVALID_MSG_VALUE = "INVALID_MSG_VALUE"; // msg.value must be greater than 0.
|
||||
string constant INVALID_AMOUNT = "INVALID_AMOUNT"; // Amount must equal 1.
|
||||
string constant INVALID_ORDERS_LENGTH = "INVALID_ORDERS_LENGTH"; // Length of orders must be greater than 1.
|
||||
}
|
||||
|
@@ -28,7 +28,10 @@ contract MixinConstants is
|
||||
bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256("ERC20Token(address)"));
|
||||
bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256("ERC721Token(address,uint256,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%
|
||||
uint256 constant internal MAX_WETH_FILL_PERCENTAGE = 95 * PERCENTAGE_DENOMINATOR / 100; // 95%
|
||||
|
||||
constructor (
|
||||
address _exchange,
|
||||
address _etherToken,
|
||||
|
@@ -23,6 +23,7 @@ import "./mixins/MWeth.sol";
|
||||
import "./mixins/MAssets.sol";
|
||||
import "./mixins/MConstants.sol";
|
||||
import "./mixins/MForwarderCore.sol";
|
||||
import "../utils/LibBytes/LibBytes.sol";
|
||||
import "../protocol/Exchange/libs/LibOrder.sol";
|
||||
import "../protocol/Exchange/libs/LibFillResults.sol";
|
||||
import "../protocol/Exchange/libs/LibMath.sol";
|
||||
@@ -37,6 +38,8 @@ contract MixinForwarderCore is
|
||||
MForwarderCore
|
||||
{
|
||||
|
||||
using LibBytes for bytes;
|
||||
|
||||
/// @dev Constructor approves ERC20 proxy to transfer ZRX and WETH on this contract's behalf.
|
||||
constructor ()
|
||||
public
|
||||
@@ -74,24 +77,54 @@ contract MixinForwarderCore is
|
||||
FillResults memory feeOrderFillResults
|
||||
)
|
||||
{
|
||||
require(
|
||||
orders.length > 0,
|
||||
"INVALID_ORDERS_LENGTH"
|
||||
);
|
||||
|
||||
// Convert ETH to WETH.
|
||||
// 5% of WETH is reserved for filling feeOrders and paying feeRecipient.
|
||||
uint256 wethAvailable = convertEthToWeth();
|
||||
convertEthToWeth();
|
||||
|
||||
// Attempt to sell 95% of WETH.
|
||||
// ZRX fees are payed with this contract's balance.
|
||||
orderFillResults = marketSellWeth(
|
||||
orders,
|
||||
wethAvailable,
|
||||
signatures
|
||||
);
|
||||
|
||||
// Buy back all ZRX spent on fees.
|
||||
feeOrderFillResults = marketBuyZrx(
|
||||
feeOrders,
|
||||
orderFillResults.takerFeePaid,
|
||||
feeSignatures
|
||||
);
|
||||
uint256 makerAssetAmountPurchased;
|
||||
uint256 wethAvailable;
|
||||
if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) {
|
||||
// Calculate amount of WETH that won't be spent on ETH fees.
|
||||
wethAvailable = getPartialAmount(
|
||||
PERCENTAGE_DENOMINATOR,
|
||||
safeAdd(PERCENTAGE_DENOMINATOR, feePercentage),
|
||||
msg.value
|
||||
);
|
||||
// Market sell available WETH.
|
||||
// ZRX fees are paid with this contract's balance.
|
||||
orderFillResults = marketSellWeth(
|
||||
orders,
|
||||
wethAvailable,
|
||||
signatures
|
||||
);
|
||||
// The fee amount must be deducted from the amount transfered back to sender.
|
||||
makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid);
|
||||
} else {
|
||||
// 5% of WETH is reserved for filling feeOrders and paying feeRecipient.
|
||||
wethAvailable = getPartialAmount(
|
||||
MAX_WETH_FILL_PERCENTAGE,
|
||||
PERCENTAGE_DENOMINATOR,
|
||||
msg.value
|
||||
);
|
||||
// Market sell 95% of WETH.
|
||||
// ZRX fees are payed with this contract's balance.
|
||||
orderFillResults = marketSellWeth(
|
||||
orders,
|
||||
wethAvailable,
|
||||
signatures
|
||||
);
|
||||
// Buy back all ZRX spent on fees.
|
||||
feeOrderFillResults = marketBuyZrx(
|
||||
feeOrders,
|
||||
orderFillResults.takerFeePaid,
|
||||
feeSignatures
|
||||
);
|
||||
makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount;
|
||||
}
|
||||
|
||||
// Ensure that no extra WETH owned by this contract has been sold.
|
||||
uint256 totalWethSold = safeAdd(orderFillResults.takerAssetFilledAmount, feeOrderFillResults.takerAssetFilledAmount);
|
||||
@@ -110,7 +143,7 @@ contract MixinForwarderCore is
|
||||
);
|
||||
|
||||
// Transfer purchased assets to msg.sender.
|
||||
transferPurchasedAssetToSender(orders[0].makerAssetData, orderFillResults.makerAssetFilledAmount);
|
||||
transferPurchasedAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased);
|
||||
}
|
||||
|
||||
/// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction.
|
||||
@@ -140,23 +173,41 @@ contract MixinForwarderCore is
|
||||
FillResults memory feeOrderFillResults
|
||||
)
|
||||
{
|
||||
require(
|
||||
orders.length > 0,
|
||||
"INVALID_ORDERS_LENGTH"
|
||||
);
|
||||
|
||||
// Convert ETH to WETH.
|
||||
convertEthToWeth();
|
||||
|
||||
// Attemp to purchase desired amount of makerAsset.
|
||||
// ZRX fees are payed with this contract's balance.
|
||||
orderFillResults = marketBuyAsset(
|
||||
orders,
|
||||
makerAssetFillAmount,
|
||||
signatures
|
||||
);
|
||||
|
||||
// Buy back all ZRX spent on fees.
|
||||
feeOrderFillResults = marketBuyZrx(
|
||||
feeOrders,
|
||||
orderFillResults.takerFeePaid,
|
||||
feeSignatures
|
||||
);
|
||||
uint256 makerAssetAmountPurchased;
|
||||
if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) {
|
||||
// If the makerAsset is ZRX, it is not necessary to pay fees out of this
|
||||
// contracts's ZRX balance because fees are factored into the price of the order.
|
||||
orderFillResults = marketBuyZrx(
|
||||
orders,
|
||||
makerAssetFillAmount,
|
||||
signatures
|
||||
);
|
||||
// The fee amount must be deducted from the amount transfered back to sender.
|
||||
makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid);
|
||||
} else {
|
||||
// Attemp to purchase desired amount of makerAsset.
|
||||
// ZRX fees are payed with this contract's balance.
|
||||
orderFillResults = marketBuyAsset(
|
||||
orders,
|
||||
makerAssetFillAmount,
|
||||
signatures
|
||||
);
|
||||
// Buy back all ZRX spent on fees.
|
||||
feeOrderFillResults = marketBuyZrx(
|
||||
feeOrders,
|
||||
orderFillResults.takerFeePaid,
|
||||
feeSignatures
|
||||
);
|
||||
makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount;
|
||||
}
|
||||
|
||||
// Ensure that no extra WETH owned by this contract has been sold.
|
||||
uint256 totalWethSold = safeAdd(orderFillResults.takerAssetFilledAmount, feeOrderFillResults.takerAssetFilledAmount);
|
||||
@@ -175,7 +226,7 @@ contract MixinForwarderCore is
|
||||
);
|
||||
|
||||
// Transfer purchased assets to msg.sender.
|
||||
transferPurchasedAssetToSender(orders[0].makerAssetData, orderFillResults.makerAssetFilledAmount);
|
||||
transferPurchasedAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased);
|
||||
}
|
||||
|
||||
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
|
||||
@@ -280,7 +331,7 @@ contract MixinForwarderCore is
|
||||
// Attempt to sell the remaining amount of WETH.
|
||||
FillResults memory singleFillResult = EXCHANGE.fillOrderNoThrow(
|
||||
orders[i],
|
||||
remainingWethSellAmount,
|
||||
safeAdd(remainingWethSellAmount, 1),
|
||||
signatures[i]
|
||||
);
|
||||
|
||||
@@ -289,14 +340,14 @@ contract MixinForwarderCore is
|
||||
zrxPurchased = safeSub(totalFillResults.makerAssetFilledAmount, totalFillResults.takerFeePaid);
|
||||
|
||||
// Stop execution if the entire amount of ZRX has been bought.
|
||||
if (zrxPurchased == zrxBuyAmount) {
|
||||
if (zrxPurchased >= zrxBuyAmount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that all ZRX spent while filling primary orders has been repurchased.
|
||||
require(
|
||||
zrxPurchased == zrxBuyAmount,
|
||||
zrxPurchased >= zrxBuyAmount,
|
||||
"COMPLETE_FILL_FAILED"
|
||||
);
|
||||
}
|
||||
|
@@ -29,10 +29,6 @@ contract MixinWeth is
|
||||
MWeth
|
||||
{
|
||||
|
||||
uint256 constant internal PERCENTAGE_DENOMINATOR = 10**18;
|
||||
uint256 constant internal MAX_FEE_PERCENTAGE = 5 * PERCENTAGE_DENOMINATOR / 100; // 5%
|
||||
uint256 constant internal MAX_WETH_FILL_PERCENTAGE = 95 * PERCENTAGE_DENOMINATOR / 100; // 95%
|
||||
|
||||
/// @dev Default payabale function, this allows us to withdraw WETH
|
||||
function ()
|
||||
public
|
||||
@@ -45,23 +41,14 @@ contract MixinWeth is
|
||||
}
|
||||
|
||||
/// @dev Converts message call's ETH value into WETH.
|
||||
/// @return 95% of ETH converted to WETH.
|
||||
function convertEthToWeth()
|
||||
internal
|
||||
returns (uint256 wethAvailable)
|
||||
{
|
||||
require(
|
||||
msg.value > 0,
|
||||
"INVALID_MSG_VALUE"
|
||||
);
|
||||
|
||||
ETHER_TOKEN.deposit.value(msg.value)();
|
||||
wethAvailable = getPartialAmount(
|
||||
MAX_WETH_FILL_PERCENTAGE,
|
||||
PERCENTAGE_DENOMINATOR,
|
||||
msg.value
|
||||
);
|
||||
return wethAvailable;
|
||||
}
|
||||
|
||||
/// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient.
|
||||
|
@@ -28,6 +28,9 @@ contract MConstants {
|
||||
bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256("ERC20Token(address)"));
|
||||
bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256("ERC721Token(address,uint256,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%
|
||||
uint256 constant internal MAX_WETH_FILL_PERCENTAGE = 95 * PERCENTAGE_DENOMINATOR / 100; // 95%
|
||||
|
||||
// solhint-disable var-name-mixedcase
|
||||
IExchange internal EXCHANGE;
|
||||
|
@@ -22,10 +22,8 @@ pragma solidity 0.4.24;
|
||||
contract MWeth {
|
||||
|
||||
/// @dev Converts message call's ETH value into WETH.
|
||||
/// @return 95% of ETH converted to WETH.
|
||||
function convertEthToWeth()
|
||||
internal
|
||||
returns (uint256 wethAvailable);
|
||||
internal;
|
||||
|
||||
/// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient.
|
||||
/// Refunds any excess ETH to msg.sender.
|
||||
|
Reference in New Issue
Block a user