-
Notifications
You must be signed in to change notification settings - Fork 175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(emp): add trimExcess function to send excess tokens #1975
Conversation
…rmined address Signed-off-by: Matt Rice <matthewcrice32@gmail.com>
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.
Overall this is a great solution! much more generic than the re-direct functionality. bit of a pity that it adds an extra param to the contractor which will make the test churn quite high but that's unavoidable given the desired features of this change.
@@ -38,6 +38,7 @@ contract ExpiringMultiPartyCreator is ContractCreator, Testable, Lockable { | |||
FixedPoint.Unsigned minSponsorTokens; | |||
uint256 withdrawalLiveness; | |||
uint256 liquidationLiveness; | |||
address beneficiary; |
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.
nit: beneficiary as a variable name makes sense in the context of this PR but taken out of context it is a bit confusing. perhaps a different variable name for this?
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.
prehaps excessTokenBeneficiary
?
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.
SGTM!
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.
Done.
if (address(token) == address(collateralCurrency)) { | ||
// If it is the collateral currency, send only the amount that the contract is not tracking. | ||
// Note: this could be due to rounding error or balance-chainging tokens, like aTokens. | ||
token.safeTransfer(beneficiary, balance.sub(pfc()).rawValue); |
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 makes sense to me. PFC is simply the rawTotalPositionCollateral
adjusted for fees. This is updated every time someone deposits or withdraws from the contract. If the balance shifts through some other action (an a-token or someone depositing collateral directly to the emp) then the balance of call will be higher than the pfc
and this function will trip any of that excess balance off.
In theory, this could also let us recover funds that are incorrectly sent to the 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.
Here you do not perform any checks on who beneficiary
is assigned to. It'll default to the 0x000 address as a result. Because this function has no access control someone could grief the EMP if it has any excess collateral by sending it to the 0x000 address. I'd consider either adding a check that enforces that the beneficiary
is not the 0 address or perhaps setting it to the Store
?
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.
Good point. I think checking that it's not 0x0 in the constructor is probably the simplest solution.
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.
Done. I added it to the validation done in the creator so the check wouldn't affect the bytecode size.
if (address(token) == address(collateralCurrency)) { | ||
// If it is the collateral currency, send only the amount that the contract is not tracking. | ||
// Note: this could be due to rounding error or balance-chainging tokens, like aTokens. | ||
token.safeTransfer(excessTokenBeneficiary, balance.sub(pfc()).rawValue); |
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.
So how does the balance of the contract diverge from pfc()
?
- user accidentally sends collateral to the contract
- interest denominated in collateral accrues to contract
anything else?
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 think those are the only two cases. But obviously, the viability of this change depends on that assumption. I'm going to take another pass through the contract to ensure there are no places where the balance of the contract is expected to deviate from pfc.
@nicholaspai @chrismaree I've added tests. PTAL. |
@@ -2,6 +2,7 @@ | |||
const { PositionStatesEnum, didContractThrow } = require("@umaprotocol/common"); | |||
const truffleAssert = require("truffle-assertions"); | |||
const { interfaceName } = require("@umaprotocol/common"); | |||
const { assert } = require("chai"); |
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.
nit: do you need to this assert
import? I thought it came with Truffle for free
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, my editor just got overzealous :). Nice catch!
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 think this is simplest implementation for this, nice! And I think the test coverage overall is great. It'd be better to run this basically after every single test in a single afterEach()
hook, but absent that I think we should add the trimExcess test after:
- Precision Loss tests in Liquidatable and PPM
- Non-18 decimal collateral EMP's
I see what you're saying. Will add! |
@nicholaspai added an afterEach for both the PPM and Liquidator tests. PTAL |
@@ -99,7 +101,8 @@ const actualDeploy = async inputCsv => { | |||
disputeBondPct: percentToFixedPoint(params.disputeBond), | |||
sponsorDisputeRewardPct: percentToFixedPoint(params.sponsorDisputeReward), | |||
disputerDisputeRewardPct: percentToFixedPoint(params.disputeReward), | |||
minSponsorTokens: percentToFixedPoint(params.minSponsorTokens) | |||
minSponsorTokens: percentToFixedPoint(params.minSponsorTokens), | |||
excessTokenBeneficiary: store.address |
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. good default.
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.
looks great! effective and clean testing. overall a perfect implementation of this logic.
assert.notEqual(beneficiaryCollateralBalance.toString(), "0"); | ||
}; | ||
|
||
afterEach(async () => { |
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 is awesome, does this make the rest of the expectNoExcessCollateralToTrim()
calls redundant now?
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.
Good point. Most of them are probably still necessary because we want to check that there are no intermediate steps that leave untracked tokens sitting in the contract. For instance, I was a little worried that somewhere in the liquidation and dispute flow that a bond or final fee was in the contract but not added to pfc, which would allow someone to drain it using this method. I'll go back and remove any that occur as the last step in a test case, though.
@nicholaspai merged PTAL. |
Motivation
#1974.
Summary
Added a function to send excess collateral to a prespecified beneficiary address. This address is passed in by the deployer. The excess collateral is assumed to be any collateral not included in pfc. This is an EMP-specific assumption that I would appreciate feedback on (correctness and understandability). It may be clearer to make a distinct internal function called _trackedBalance() or similar that pfc calls.
Note: this still needs tests. I wanted to get feedback before putting time into tests.
Issue(s)
Fixes #1974.