Skip to content
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

Merged
merged 16 commits into from
Sep 16, 2020

Conversation

mrice32
Copy link
Member

@mrice32 mrice32 commented Sep 9, 2020

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.

…rmined address

Signed-off-by: Matt Rice <matthewcrice32@gmail.com>
Copy link
Member

@chrismaree chrismaree left a 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;
Copy link
Member

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?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prehaps excessTokenBeneficiary?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM!

Copy link
Member Author

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);
Copy link
Member

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.

Copy link
Member

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?

Copy link
Member Author

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.

Copy link
Member Author

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.

Signed-off-by: Matt Rice <matthewcrice32@gmail.com>
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);
Copy link
Member

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?

Copy link
Member Author

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.

Signed-off-by: Matt Rice <matthewcrice32@gmail.com>
@mrice32 mrice32 marked this pull request as ready for review September 10, 2020 20:29
@mrice32
Copy link
Member Author

mrice32 commented Sep 10, 2020

@nicholaspai @chrismaree I've added tests. PTAL.

@coveralls
Copy link

coveralls commented Sep 10, 2020

Coverage Status

Coverage increased (+0.06%) to 92.754% when pulling b1a47ae on mrice32:trim_excess into 5643675 on UMAprotocol:master.

@@ -2,6 +2,7 @@
const { PositionStatesEnum, didContractThrow } = require("@umaprotocol/common");
const truffleAssert = require("truffle-assertions");
const { interfaceName } = require("@umaprotocol/common");
const { assert } = require("chai");
Copy link
Member

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

Copy link
Member Author

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!

Copy link
Member

@nicholaspai nicholaspai left a 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

@mrice32
Copy link
Member Author

mrice32 commented Sep 10, 2020

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!

Signed-off-by: Matt Rice <matthewcrice32@gmail.com>
@mrice32
Copy link
Member Author

mrice32 commented Sep 10, 2020

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

Signed-off-by: Matt Rice <matthewcrice32@gmail.com>
Signed-off-by: Matt Rice <matthewcrice32@gmail.com>
Signed-off-by: Matt Rice <matthewcrice32@gmail.com>
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice. good default.

Copy link
Member

@chrismaree chrismaree left a 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 () => {
Copy link
Member

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?

Copy link
Member Author

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.

Signed-off-by: Matt Rice <matthewcrice32@gmail.com>
Signed-off-by: Matt Rice <matthewcrice32@gmail.com>
Signed-off-by: Matt Rice <matthewcrice32@gmail.com>
Signed-off-by: Matt Rice <matthewcrice32@gmail.com>
@mrice32
Copy link
Member Author

mrice32 commented Sep 16, 2020

@nicholaspai merged PTAL.

@mrice32 mrice32 merged commit 658f4d9 into UMAprotocol:master Sep 16, 2020
@mrice32 mrice32 deleted the trim_excess branch September 16, 2020 14:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add a function to send excess collateral to a prespecified address
4 participants