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

Improvement/aura fed to base fed #9

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

08xmt
Copy link
Collaborator

@08xmt 08xmt commented Jun 8, 2023

Reimplements the AuraFed as the LossyFed abstract class, adding new functionality like:

  • Governance emergency withdraws
  • Migration logic that makes it possible to migrate to other fed contracts
  • Token sweeping
    And generally better assurances of accounting and access control logic being correctly implemented.

function _addLiquidity(uint dolaAmount, uint maxSlippage) internal returns(uint){
uint initialBal = BPT.balanceOf(address(this));
uint BPTWanted = bptNeededForDola(dolaAmount);
uint minBptOut = BPTWanted - BPTWanted * maxSlippage / BPS;
Copy link

@wavey0x wavey0x Jun 19, 2023

Choose a reason for hiding this comment

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

This is sandwichable because calculations can be affected by a front-runner. Be sure to use private mempool or add in a function parameter which allows the fed chair to pass in an off-chain computed value.

Edit: Actually, i think it is not sandwichable since it is a stable pool and the function considers the assets pegged, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It is sandwhichable up to the maxSlippage amount. We generally try to execute with flashbots rpc, but I can definitely see the idea in adding a "minBPT" or similar.

function _addLiquidity(uint dolaAmount, uint maxSlippage) internal returns(uint){
uint initialBal = BPT.balanceOf(address(this));
uint BPTWanted = bptNeededForDola(dolaAmount);
uint minBptOut = BPTWanted - BPTWanted * maxSlippage / BPS;
Copy link

Choose a reason for hiding this comment

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

Might consider adding parentheses for order of operations clarity.

_;
}

function setMaxLossExpansionBps(uint newMaxLossExpansionBps) onlyGov external {
Copy link

Choose a reason for hiding this comment

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

Updating max loss on Expansions and TakeProfit are gov only, but Contractions can be guardian as well. Is the difference due to one being more sensitive than the other?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, idea is that an emergency contraction may need to happen in short order, due to a hack of an underlying protocol. We saw that play out during the Euler hack, where fast contractions were instrumental in avoiding as much bad debt as possible.


/**
* @notice Migrates both claims and debt from another fed to this one
* @dev Must call the migraTo function of the target fed
Copy link

Choose a reason for hiding this comment

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

typo

@@ -128,14 +143,36 @@ abstract contract BaseFed {
function takeProfit(uint flag) external virtual;
Copy link

Choose a reason for hiding this comment

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

Small suggestion. Can try a pattern like this which enforces events are always implemented without relying on developer to remember.

function takeProfit(uint flag) external {
    (address token, uint amount) = _takeProfit(flag);
    Profit(token, amount);
}

function _takeProfit() external virtual returns(address token, uint amount);

uint bptNeeded = bptNeededForDola(dolaAmount);
function _removeLiquidity(uint dolaAmount, uint maxSlippage) internal returns(uint){
uint initialBal = _DOLA.balanceOf(address(this));
uint BPTNeeded = bptNeededForDola(dolaAmount);
uint minDolaOut = dolaAmount - dolaAmount * maxSlippage / BPS;
Copy link

Choose a reason for hiding this comment

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

Consider parentheses for order of operations clarity

uint bptNeeded = bptNeededForDola(amountDola);
require(bptNeeded <= bptSupply(), "Not enough BPT tokens");
require(bptNeeded <= claimsSupply(), "Not enough BPT tokens");
Copy link

Choose a reason for hiding this comment

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

could save some gas by using claimsBefore value here instead of recalculating claimsSupply().

* @param amountDola The amount of dola tokens to withdraw. Note that more tokens may
* be withdrawn than requested, as price is calculated by debts to strategies, but strategies
* may have outperformed price of dola token.
* @return claimsUsed The amoutn of balancer pools tokens spent on withdrawing.
Copy link

Choose a reason for hiding this comment

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

typo

require(totalClaims >= claimsToMigrate, "NOT ENOUGH CLAIMS");
uint claimsToUnstake = claimsToMigrate - BPT.balanceOf(address(this));
require(dolaBptRewardPool.withdrawAndUnwrap(claimsToUnstake, false), "AURA WITHDRAW FAILED");
uint debtToMigrate = debt * claimsToMigrate / totalClaims;
Copy link

Choose a reason for hiding this comment

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

is it possible that there is ever debt > 0, and also totalClaims == 0?

If so this would revert when dividing by zero and wouldn't allow us to move that debt from one Fed to another.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's possible but at that point there would be no point in migrating, as the debt is purely bad debt and it has no claims to back it up. Becomes the job of the treasury to pay off the bad debt accumulated by the Fed.

swapExactIn(address(bpt), address(dola), bptBal, minDolaOut);
return dola.balanceOf(address(this));
swapExactIn(address(BPT), address(_DOLA), BPTBal, minDolaOut);
return _DOLA.balanceOf(address(this));
Copy link

Choose a reason for hiding this comment

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

info:
Inconsistent return value behavior between _removeLiquidity() and _removeAllLiquidity().
The latter does not account for any pre-exiting DOLA in the contract.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's working as intended. When calling the _removeLiquidity function, the intention for the Fed should be to remove a certain amount of DOLA from the active market, where _removeAllLiquidity is meant to wind down the Fed operation.
I suppose we could return the additional DOLA, but that's ultimately immaterial, as aslong the DOLA is outside of the market, then it's not influencing prices, and we can either take it as profit or burn it when winding down the Fed. In the general, DOLA existing within the mainnet AuraFed would be an anomaly.

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.

2 participants