@0x:contracts-staking Added tests for protocol fees

This commit is contained in:
Alex Towle
2019-09-04 22:02:21 -07:00
parent 494dc475c1
commit 30fee43928
15 changed files with 585 additions and 40 deletions

View File

@@ -99,37 +99,42 @@ contract MixinExchangeFees is
payable
onlyExchange
{
// Get the pool id of the maker address, and use this pool id to get the amount
// of fees collected during this epoch.
bytes32 poolId = getStakingPoolIdOfMaker(makerAddress);
uint256 _feesCollectedThisEpoch = protocolFeesThisEpochByPool[poolId];
if (msg.value == 0) {
// Transfer the protocol fee to this address.
erc20Proxy.transferFrom(
wethAssetData,
payerAddress,
address(this),
protocolFeePaid
);
// Update the amount of protocol fees paid to this pool this epoch.
protocolFeesThisEpochByPool[poolId] = _feesCollectedThisEpoch.safeAdd(protocolFeePaid);
} else if (msg.value == protocolFeePaid) {
// Update the amount of protocol fees paid to this pool this epoch.
protocolFeesThisEpochByPool[poolId] = _feesCollectedThisEpoch.safeAdd(protocolFeePaid);
} else {
// If the wrong message value was sent, revert with a rich error.
// If the protocol fee payment is invalid, revert with a rich error.
if (
protocolFeePaid == 0 ||
(msg.value != protocolFeePaid && msg.value != 0)
) {
LibRichErrors.rrevert(LibStakingRichErrors.InvalidProtocolFeePaymentError(
protocolFeePaid,
msg.value
));
}
// If there were no fees collected prior to this payment, activate the pool that is being paid.
if (_feesCollectedThisEpoch == 0) {
activePoolsThisEpoch.push(poolId);
// Transfer the protocol fee to this address if it should be paid in WETH.
if (msg.value == 0) {
erc20Proxy.transferFrom(
WETH_ASSET_DATA,
payerAddress,
address(this),
protocolFeePaid
);
}
// Get the pool id of the maker address.
bytes32 poolId = getStakingPoolIdOfMaker(makerAddress);
// Only attribute the protocol fee payment to a pool if the maker is registered to a pool.
if (poolId != NIL_POOL_ID) {
// Use the maker pool id to get the amount of fees collected during this epoch in the pool.
uint256 _feesCollectedThisEpoch = protocolFeesThisEpochByPool[poolId];
// Update the amount of protocol fees paid to this pool this epoch.
protocolFeesThisEpochByPool[poolId] = _feesCollectedThisEpoch.safeAdd(protocolFeePaid);
// If there were no fees collected prior to this payment, activate the pool that is being paid.
if (_feesCollectedThisEpoch == 0) {
activePoolsThisEpoch.push(poolId);
}
}
}
@@ -179,6 +184,18 @@ contract MixinExchangeFees is
return protocolFeesThisEpochByPool[poolId];
}
/// @dev Withdraws the entire WETH balance of the contract.
function _unwrapWETH()
internal
{
uint256 wethBalance = IEtherToken(WETH_ADDRESS).balanceOf(address(this));
// Don't withdraw WETH if the WETH balance is zero as a gas optimization.
if (wethBalance != 0) {
IEtherToken(WETH_ADDRESS).withdraw(wethBalance);
}
}
/// @dev Pays rewards to market making pools that were active this epoch.
/// Each pool receives a portion of the fees generated this epoch (see _cobbDouglas) that is
/// proportional to (i) the fee volume attributed to their pool over the epoch, and
@@ -204,11 +221,12 @@ contract MixinExchangeFees is
uint256 finalContractBalance
)
{
// step 1/4 - withdraw the entire wrapper ether balance into this contract.
// WETH is unwrapped here to keep `payProtocolFee()` calls relatively cheap.
uint256 wethBalance = IEtherToken(WETH_ADDRESS).balanceOf(address(this));
IEtherToken(WETH_ADDRESS).withdraw(wethBalance);
// step 1/4 - withdraw the entire wrapped ether balance into this contract. WETH
// is unwrapped here to keep `payProtocolFee()` calls relatively cheap,
// and WETH is only withdrawn if this contract's WETH balance is nonzero.
_unwrapWETH();
// Initialize initial values
totalActivePools = activePoolsThisEpoch.length;
totalFeesCollected = 0;
totalWeightedStake = 0;

View File

@@ -34,6 +34,8 @@ contract MixinConstants is
bytes32 constant internal NIL_POOL_ID = 0x0000000000000000000000000000000000000000000000000000000000000000;
bytes32 constant internal NIL_POOL_ID = 0x0000000000000000000000000000000000000000000000000000000000000000;
address constant internal NIL_ADDRESS = 0x0000000000000000000000000000000000000000;
bytes32 constant internal UNKNOWN_STAKING_POOL_ID = 0x0000000000000000000000000000000000000000000000000000000000000000;
@@ -46,4 +48,7 @@ contract MixinConstants is
// The address of the canonical WETH contract -- this will be used as an alternative to ether for paying protocol fees.
address constant internal WETH_ADDRESS = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
// abi.encodeWithSelector(IAssetData(address(0)).ERC20Token.selector, WETH_ADDRESS)
bytes constant internal WETH_ASSET_DATA = hex"f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2";
}

View File

@@ -37,20 +37,11 @@ contract MixinStorage is
constructor()
public
Ownable()
{
// Set the erc20 asset proxy data.
wethAssetData = abi.encodeWithSelector(
IAssetData(address(0)).ERC20Token.selector,
WETH_ADDRESS
);
}
{} // solhint-disable-line no-empty-blocks
// 0x ERC20 Proxy
IAssetProxy internal erc20Proxy;
// The asset data that should be sent to transfer weth
bytes internal wethAssetData;
// address of staking contract
address internal stakingContract;

View File

@@ -0,0 +1,41 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "../src/Staking.sol";
contract TestProtocolFees is
Staking
{
function setPoolIdOfMaker(bytes32 poolId, address makerAddress)
external
{
poolIdByMakerAddress[makerAddress] = poolId;
}
function getActivePoolsByEpoch()
external
view
returns (bytes32[] memory)
{
return activePoolsThisEpoch;
}
}

View File

@@ -0,0 +1,44 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
import "@0x/contracts-asset-proxy/contracts/src/ERC20Proxy.sol";
contract TestProtocolFeesERC20Proxy is
ERC20Proxy
{
event TransferFromCalled(
bytes assetData,
address from,
address to,
uint256 amount
);
function transferFrom(
bytes calldata assetData,
address from,
address to,
uint256 amount
)
external
{
emit TransferFromCalled(assetData, from, to, amount);
}
}

View File

@@ -0,0 +1,53 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "../src/Staking.sol";
contract TestStaking is
Staking
{
// Stub out `payProtocolFee` to be the naive payProtocolFee function so that tests will
// not fail for WETH protocol fees.
function payProtocolFee(
address makerAddress,
address,
uint256
)
external
payable
onlyExchange
{
uint256 amount = msg.value;
bytes32 poolId = getStakingPoolIdOfMaker(makerAddress);
uint256 _feesCollectedThisEpoch = protocolFeesThisEpochByPool[poolId];
protocolFeesThisEpochByPool[poolId] = _feesCollectedThisEpoch.safeAdd(amount);
if (_feesCollectedThisEpoch == 0) {
activePoolsThisEpoch.push(poolId);
}
}
// Stub out `_unwrapWETH` to prevent the calls to `finalizeFees` from failing in tests
// that do not relate to protocol fee payments in WETH.
function _unwrapWETH()
internal
{} // solhint-disable-line no-empty-blocks
}