Remove fee abstraction from Forwarder contracts (first pass)

This commit is contained in:
Michael Zhu
2019-07-10 16:15:30 -07:00
parent 34be9830af
commit 25087f3c92
6 changed files with 67 additions and 194 deletions

View File

@@ -31,14 +31,12 @@ contract Forwarder is
{
constructor (
address _exchange,
bytes memory _zrxAssetData,
bytes memory _wethAssetData
)
public
Ownable()
LibConstants(
_exchange,
_zrxAssetData,
_wethAssetData
)
MixinForwarderCore()

View File

@@ -91,20 +91,11 @@ contract MixinExchangeWrapper is
internal
returns (FillResults memory totalFillResults)
{
bytes memory makerAssetData = orders[0].makerAssetData;
bytes memory wethAssetData = WETH_ASSET_DATA;
uint256 ordersLength = orders.length;
// The remaining amount of WETH to sell
uint256 remainingTakerAssetFillAmount = wethSellAmount;
for (uint256 i = 0; i != ordersLength; i++) {
// We assume that asset being bought by taker is the same for each order.
// We assume that asset being sold by taker is WETH for each order.
orders[i].makerAssetData = makerAssetData;
orders[i].takerAssetData = wethAssetData;
// Calculate the remaining amount of WETH to sell
uint256 remainingTakerAssetFillAmount = _safeSub(wethSellAmount, totalFillResults.takerAssetFilledAmount);
// Attempt to sell the remaining amount of WETH
FillResults memory singleFillResults = _fillOrderNoThrow(
orders[i],
@@ -112,8 +103,20 @@ contract MixinExchangeWrapper is
signatures[i]
);
// Update amounts filled and fees paid by maker and taker
_addFillResults(totalFillResults, singleFillResults);
// Update amounts filled and the remaining amount of WETH to sell
if (orders[i].makerAssetData.equals(orders[i].takerFeeAssetData)) {
_addFillResultsDeductFees(totalFillResults, singleFillResults)
} else {
_addFillResults(totalFillResults, singleFillResults);
remainingTakerAssetFillAmount = _safeSub(
remainingTakerAssetFillAmount,
singleFillResults.takerFeePaid
)
}
remainingTakerAssetFillAmount = _safeSub(
remainingTakerAssetFillAmount,
singleFillResults.takerAssetFilledAmount
)
// Stop execution if the entire amount of takerAsset has been sold
if (totalFillResults.takerAssetFilledAmount >= wethSellAmount) {
@@ -138,18 +141,10 @@ contract MixinExchangeWrapper is
internal
returns (FillResults memory totalFillResults)
{
bytes memory makerAssetData = orders[0].makerAssetData;
bytes memory wethAssetData = WETH_ASSET_DATA;
uint256 ordersLength = orders.length;
uint256 makerAssetFilledAmount = 0;
for (uint256 i = 0; i != ordersLength; i++) {
// We assume that asset being bought by taker is the same for each order.
// We assume that asset being sold by taker is WETH for each order.
orders[i].makerAssetData = makerAssetData;
orders[i].takerAssetData = wethAssetData;
// Calculate the remaining amount of makerAsset to buy
uint256 remainingMakerAssetFillAmount = _safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);
@@ -171,7 +166,11 @@ contract MixinExchangeWrapper is
);
// Update amounts filled and fees paid by maker and taker
_addFillResults(totalFillResults, singleFillResults);
if (orders[i].makerAssetData.equals(orders[i].takerFeeAssetData)) {
_addFillResultsDeductFees(totalFillResults, singleFillResults)
} else {
_addFillResults(totalFillResults, singleFillResults);
}
// Stop execution if the entire amount of makerAsset has been bought
makerAssetFilledAmount = totalFillResults.makerAssetFilledAmount;
@@ -186,74 +185,4 @@ contract MixinExchangeWrapper is
);
return totalFillResults;
}
/// @dev Buys zrxBuyAmount of ZRX fee tokens, taking into account ZRX fees for each order. This will guarantee
/// that at least zrxBuyAmount of ZRX is purchased (sometimes slightly over due to rounding issues).
/// It is possible that a request to buy 200 ZRX will require purchasing 202 ZRX
/// as 2 ZRX is required to purchase the 200 ZRX fee tokens. This guarantees at least 200 ZRX for future purchases.
/// The asset being sold by taker must always be WETH.
/// @param orders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset.
/// @param zrxBuyAmount Desired amount of ZRX to buy.
/// @param signatures Proofs that orders have been created by makers.
/// @return totalFillResults Amounts filled and fees paid by maker and taker.
function _marketBuyExactZrxWithWeth(
LibOrder.Order[] memory orders,
uint256 zrxBuyAmount,
bytes[] memory signatures
)
internal
returns (FillResults memory totalFillResults)
{
// Do nothing if zrxBuyAmount == 0
if (zrxBuyAmount == 0) {
return totalFillResults;
}
bytes memory zrxAssetData = ZRX_ASSET_DATA;
bytes memory wethAssetData = WETH_ASSET_DATA;
uint256 zrxPurchased = 0;
uint256 ordersLength = orders.length;
for (uint256 i = 0; i != ordersLength; i++) {
// All of these are ZRX/WETH, so we can drop the respective assetData from calldata.
orders[i].makerAssetData = zrxAssetData;
orders[i].takerAssetData = wethAssetData;
// Calculate the remaining amount of ZRX to buy.
uint256 remainingZrxBuyAmount = _safeSub(zrxBuyAmount, zrxPurchased);
// Convert the remaining amount of ZRX to buy into remaining amount
// of WETH to sell, assuming entire amount can be sold in the current order.
// We round up because the exchange rate computed by fillOrder rounds in favor
// of the Maker. In this case we want to overestimate the amount of takerAsset.
uint256 remainingWethSellAmount = _getPartialAmountCeil(
orders[i].takerAssetAmount,
_safeSub(orders[i].makerAssetAmount, orders[i].takerFee), // our exchange rate after fees
remainingZrxBuyAmount
);
// Attempt to sell the remaining amount of WETH.
FillResults memory singleFillResult = _fillOrderNoThrow(
orders[i],
remainingWethSellAmount,
signatures[i]
);
// Update amounts filled and fees paid by maker and taker.
_addFillResults(totalFillResults, singleFillResult);
zrxPurchased = _safeSub(totalFillResults.makerAssetFilledAmount, totalFillResults.takerFeePaid);
// Stop execution if the entire amount of ZRX has been bought.
if (zrxPurchased >= zrxBuyAmount) {
break;
}
}
require(
zrxPurchased >= zrxBuyAmount,
"COMPLETE_FILL_FAILED"
);
return totalFillResults;
}
}

View File

@@ -53,7 +53,6 @@ contract MixinForwarderCore is
"UNREGISTERED_ASSET_PROXY"
);
ETHER_TOKEN.approve(proxyAddress, MAX_UINT);
ZRX_TOKEN.approve(proxyAddress, MAX_UINT);
}
/// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value.
@@ -70,75 +69,44 @@ contract MixinForwarderCore is
function marketSellOrdersWithEth(
LibOrder.Order[] memory orders,
bytes[] memory signatures,
LibOrder.Order[] memory feeOrders,
bytes[] memory feeSignatures,
uint256 feePercentage,
address payable feeRecipient
)
public
payable
returns (
FillResults memory orderFillResults,
FillResults memory feeOrderFillResults
)
returns (FillResults memory orderFillResults)
{
// Convert ETH to WETH.
_convertEthToWeth();
uint256 wethSellAmount;
uint256 zrxBuyAmount;
uint256 makerAssetAmountPurchased;
if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) {
// Calculate amount of WETH that won't be spent on ETH fees.
wethSellAmount = _getPartialAmountFloor(
PERCENTAGE_DENOMINATOR,
_safeAdd(PERCENTAGE_DENOMINATOR, feePercentage),
msg.value
);
// Market sell available WETH.
// ZRX fees are paid with this contract's balance.
orderFillResults = _marketSellWeth(
orders,
wethSellAmount,
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.
wethSellAmount = _getPartialAmountFloor(
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,
wethSellAmount,
signatures
);
// Buy back all ZRX spent on fees.
zrxBuyAmount = orderFillResults.takerFeePaid;
feeOrderFillResults = _marketBuyExactZrxWithWeth(
feeOrders,
zrxBuyAmount,
feeSignatures
);
makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount;
}
// Calculate amount of WETH that won't be spent on ETH fees.
uint256 wethSellAmount = _getPartialAmountFloor(
PERCENTAGE_DENOMINATOR,
_safeAdd(PERCENTAGE_DENOMINATOR, feePercentage),
msg.value
);
// Market sell 95% of WETH.
// ZRX fees are payed with this contract's balance.
orderFillResults = _marketSellWeth(
orders,
wethSellAmount,
signatures
);
// Transfer feePercentage of total ETH spent on primary orders to feeRecipient.
// Refund remaining ETH to msg.sender.
_transferEthFeeAndRefund(
orderFillResults.takerAssetFilledAmount,
feeOrderFillResults.takerAssetFilledAmount,
feePercentage,
feeRecipient
);
// Transfer purchased assets to msg.sender.
_transferAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased);
_transferAssetToSender(
orders[0].makerAssetData,
orderFillResults.makerAssetFilledAmount
);
}
/// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction.
@@ -156,61 +124,36 @@ contract MixinForwarderCore is
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
bytes[] memory signatures,
LibOrder.Order[] memory feeOrders,
bytes[] memory feeSignatures,
uint256 feePercentage,
address payable feeRecipient
)
public
payable
returns (
FillResults memory orderFillResults,
FillResults memory feeOrderFillResults
)
returns (FillResults memory orderFillResults)
{
// Convert ETH to WETH.
_convertEthToWeth();
uint256 zrxBuyAmount;
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 = _marketBuyExactZrxWithWeth(
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 = _marketBuyExactAmountWithWeth(
orders,
makerAssetFillAmount,
signatures
);
// Buy back all ZRX spent on fees.
zrxBuyAmount = orderFillResults.takerFeePaid;
feeOrderFillResults = _marketBuyExactZrxWithWeth(
feeOrders,
zrxBuyAmount,
feeSignatures
);
makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount;
}
// Attempt to purchase desired amount of makerAsset.
// ZRX fees are payed with this contract's balance.
orderFillResults = _marketBuyExactAmountWithWeth(
orders,
makerAssetFillAmount,
signatures
);
// Transfer feePercentage of total ETH spent on primary orders to feeRecipient.
// Refund remaining ETH to msg.sender.
_transferEthFeeAndRefund(
orderFillResults.takerAssetFilledAmount,
feeOrderFillResults.takerAssetFilledAmount,
feePercentage,
feeRecipient
);
// Transfer purchased assets to msg.sender.
_transferAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased);
_transferAssetToSender(
orders[0].makerAssetData,
orderFillResults.makerAssetFilledAmount
);
}
}

View File

@@ -55,8 +55,7 @@ contract MixinWeth is
/// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
/// @param feeRecipient Address that will receive ETH when orders are filled.
function _transferEthFeeAndRefund(
uint256 wethSoldExcludingFeeOrders,
uint256 wethSoldForZrx,
uint256 wethSold,
uint256 feePercentage,
address payable feeRecipient
)
@@ -69,7 +68,6 @@ contract MixinWeth is
);
// Ensure that no extra WETH owned by this contract has been sold.
uint256 wethSold = _safeAdd(wethSoldExcludingFeeOrders, wethSoldForZrx);
require(
wethSold <= msg.value,
"OVERSOLD_WETH"
@@ -82,7 +80,7 @@ contract MixinWeth is
uint256 ethFee = _getPartialAmountFloor(
feePercentage,
PERCENTAGE_DENOMINATOR,
wethSoldExcludingFeeOrders
wethSold
);
// Ensure fee is less than amount of WETH remaining.

View File

@@ -38,25 +38,19 @@ contract LibConstants {
// solhint-disable var-name-mixedcase
IExchange internal EXCHANGE;
IEtherToken internal ETHER_TOKEN;
IERC20Token internal ZRX_TOKEN;
bytes internal ZRX_ASSET_DATA;
bytes internal WETH_ASSET_DATA;
// solhint-enable var-name-mixedcase
constructor (
address _exchange,
bytes memory _zrxAssetData,
bytes memory _wethAssetData
)
public
{
EXCHANGE = IExchange(_exchange);
ZRX_ASSET_DATA = _zrxAssetData;
WETH_ASSET_DATA = _wethAssetData;
address etherToken = _wethAssetData.readAddress(16);
address zrxToken = _zrxAssetData.readAddress(16);
ETHER_TOKEN = IEtherToken(etherToken);
ZRX_TOKEN = IERC20Token(zrxToken);
}
}

View File

@@ -402,4 +402,15 @@ library LibFillResults {
return matchedFillResults;
}
function _addFillResultsDeductFees(FillResults memory totalFillResults, FillResults memory singleFillResults)
internal
pure
{
totalFillResults.makerAssetFilledAmount = _safeAdd(totalFillResults.makerAssetFilledAmount, singleFillResults.makerAssetFilledAmount);
totalFillResults.takerAssetFilledAmount = _safeAdd(totalFillResults.takerAssetFilledAmount, singleFillResults.takerAssetFilledAmount);
// The fee amount must be deducted from the amount transfered back to sender.
totalFillResults.makerAssetFilledAmount = _safeSub(totalFillResults.makerAssetFilledAmount, singleFillResults.takerFeePaid);
}
}