-
Notifications
You must be signed in to change notification settings - Fork 465
Conversation
b521a4d
to
73dfb7d
Compare
73dfb7d
to
724f9ab
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work bud! Very concise implementation and test coverage lvl 💯
|
||
// Transfer the protocol fee to this address if it should be paid in WETH. | ||
if (msg.value == 0) { | ||
erc20Proxy.transferFrom( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is more directed to architecture opposed to the logic, but I'm wondering if we should deploy a separate feeProxy
.
(i)
When users set their allowance on the ERC20 proxy they permit our exchange contract to transfer tokens on their behalf. The user then explicitly consents to the transfer of token X in amount Y when they sign an order.
Here we're overloading the ERC20 proxy to collect a hidden or implicit fee. We can broadcast to our community that there now exists a fee, but it's still a passive agreement that the average user may not understand.
(ii)
The ERC20 proxy is super OP. It can transfer any ERC20 that the user has set allowance for. In this code, we really only need access to WETH.
(iii)
This also raises a question around the proxy used to stake ZRX. On one hand, this doesn't break (i) because the user still explicitly agrees to the transfer by calling stake()
. However (ii) does apply because in that scenario we really only need access to ZRX.
I propose we have either (i) a wethFeeProxy
and a zrxStakeProxy
, or (ii) a combined stakingProxy
that users can set allowances for WETH and ZRX on. Note that this can just be the generic ERC20 Proxy, but redeployed.
@abandeali1 curious to get your thoughts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have mixed feelings on it. I think I'm leaning towards having a separate wethAssetProxy
but continuing to use the current ERC20Proxy
for ZRX.
The good news is that we don't need to decide now since this can literally be a fork of the current ERC20Proxy
. Let's just rename this variable wethAssetProxy
(might as well rename ZrxVault.erc20Proxy
to zrxAssetProxy
while we're at it).
// Ensure that the correct number of logs were recorded. | ||
expect(receipt.logs.length).to.be.eq(1); | ||
|
||
// Ensure that the correct log was recorded. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering if we could move this into an actor or assertion wrapper, to follow similar patterns used elsewhere in our tests. Would also reduce a lot of the code that's repeated in these tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I can work on that. It should be a very simple actor for now.
contract TestStaking is | ||
Staking | ||
{ | ||
// Stub out `payProtocolFee` to be the naive payProtocolFee function so that tests will |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we say a bit more here? I'm still not sure why tests would fail against the actual implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up just removing it since they do work with the current impl
import "../src/Staking.sol"; | ||
|
||
|
||
contract TestProtocolFees is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, I really like this pattern 💯
} from '../src'; | ||
|
||
// tslint:disable:no-unnecessary-type-assertion | ||
blockchainTests('Protocol Fee Unit Tests', env => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Solid coverage!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super clean overall, just had a few nits!
address constant internal WETH_ADDRESS = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); | ||
|
||
// abi.encodeWithSelector(IAssetData(address(0)).ERC20Token.selector, WETH_ADDRESS) | ||
bytes constant internal WETH_ASSET_DATA = hex"f47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since these values are hard coded, it could be good to assert that this actually equals abi.encodeWithSelector(IAssetData(address(0)).ERC20Token.selector, WETH_ADDRESS)
in the constructor of this contract.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't a merge blocker, but it's probably a good idea to add comments here with all of the testnet WETH addresses/assetDatas. We should also add WETH to the contract-addresses
package now that it is used within the protocol.
@@ -18,6 +18,8 @@ | |||
|
|||
pragma solidity ^0.5.9; | |||
|
|||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This import isn't being used at the moment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will actually be used now, since the check is in the constructor.
poolIdByMakerAddress[makerAddress] = poolId; | ||
} | ||
|
||
function getActivePoolsByEpoch() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably add a getter for this in the staking contract if it doesn't already exist. Alternatively, should everything just be made public? If we go that route we can probably do it in another PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed offline, a task has been made to implement this change in a separate PR, where naming conventions will also be updated.
contract TestStaking is | ||
Staking | ||
{ | ||
// Stub out `payProtocolFee` to be the naive payProtocolFee function so that tests will |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are your thoughts on creating some internal getter function for WETH_ASSET_DATA
(getWethAssetData
), using that in the actual payProtocolFee
, and then stubbing it out in this contract? That way we can test with actual WETH transfers. It's probably a couple extra gas but the tradeoff feels worth it to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could actually be a cool pattern to use for all deployment constants. Not sure if we have any similar situations with any of the other constants at the moment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed offline, I'm a big fan and I think that we should do it on an as-needed basis.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the integration tests aren't being implemented in this PR, I'm going to hold off on this until it's needed.
73d598b
to
f52f929
Compare
85d99fd
to
d270581
Compare
d270581
to
2fdd4e9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like how you employed the actor pattern. Nice work! (~‾▿‾)~
@@ -217,8 +263,9 @@ contract MixinExchangeFees is | |||
); | |||
|
|||
// store pool stats | |||
uint256 protocolFeeBalance = protocolFeesThisEpochByPool[poolId]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this an optimization?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like it actually makes it less efficient. I think that it's a holdover from how it would have worked before WETH protocol fees.
// Stub out `payProtocolFee` to be the naive payProtocolFee function so that tests will | ||
// not fail for WETH protocol fees. These tests will fail otherwise because many of them | ||
// will transfer | ||
function payProtocolFee( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we still need this, since it's commented out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, I'll remove.
Description
This pull request introduces WETH support to the staking contract, implementing part of ZEIP-42. Despite the fact that supporting a new asset typically increases the complexity of a system, in this case, only the
payProtocolFee
andfinalizeFees
functions were made more complex.payProtocolFee
The logic is now entirely compatible with ETH or WETH, and this works whether the maker is registered in a staking pool or not. These changes are also unit tested in the file
test/protocol_fees.ts
.finalizeFees
Only a small change needed to be made to this function. Now this function will unwrap WETH and withdraw all of the unwrapped ETH to the staking contract. After this, finalization can carry on normally, so this is not a very big change.
Testing instructions
yall
Types of changes
Checklist:
[WIP]
if necessary.