From a7ff7d5cfb28a436bf11ea64371534ff3a421942 Mon Sep 17 00:00:00 2001 From: 08xmt Date: Mon, 5 Jun 2023 12:00:47 +0200 Subject: [PATCH 1/6] Add lossy Fed abstract contract --- src/LossyFed.sol | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/LossyFed.sol diff --git a/src/LossyFed.sol b/src/LossyFed.sol new file mode 100644 index 0000000..6636416 --- /dev/null +++ b/src/LossyFed.sol @@ -0,0 +1,64 @@ +pragma solidity ^0.8.13; + +import "src/MintingFed.sol"; + +abstract contract LossyFed is MintingFed { + uint public maxLossExpansionBps; + uint public maxLossContractionBps; + uint public maxLossTakeProfitBps; + uint public maxLossSetableByGuardian = 500; + address public guardian; + constructor(address _DOLA, + address _gov, + address _chair, + address _guardian, + uint _maxLossExpansionBps, + uint _maxLossContractionBps, + uint _maxLossTakeProfitBps + ) MintingFed(_DOLA, _gov, _chair) + { + require(_maxLossExpansionBps < 10000, "Expansion max loss too high"); + require(_maxLossContractionBps < 10000, "Contraction max loss too high"); + require(_maxLossTakeProfitBps < 10000, "TakeProfit max loss too high"); + guardian = _guardian; + maxLossExpansionBps = _maxLossExpansionBps; + maxLossContractionBps = _maxLossContractionBps; + maxLossTakeProfitBps = _maxLossTakeProfitBps; + } + + modifier onlyGuardian { + require(msg.sender == guardian || msg.sender == gov, "ONLY GOV OR GUARDIAN"); + _; + } + + function setMaxLossExpansionBps(uint newMaxLossExpansionBps) onlyGov external { + require(newMaxLossExpansionBps <= 10000, "Max loss above 100%"); + maxLossExpansionBps = newMaxLossExpansionBps; + } + + function setMaxLossContractionBps(uint newMaxLossContractionBps) onlyGuardian external{ + if(msg.sender == guardian){ + //We limit the max loss a guardian can set, as we only want governance to be able to set a very high maxloss + require(newMaxLossContractionBps <= maxLossSetableByGuardian, "Above allowed maxloss for chair"); + } + require(newMaxLossContractionBps <= 10000, "Max loss above 100%"); + maxLossContractionBps = newMaxLossContractionBps; + } + + function setMaxLossTakeProfitBps(uint newMaxLossTakeProfitBps) onlyGov external { + require(newMaxLossTakeProfitBps <= 10000, "Max loss above 100%"); + maxLossTakeProfitBps = newMaxLossTakeProfitBps; + } + + function setMaxLossSetableByGuardian(uint newMaxLossSetableByGuardian) onlyGov external { + require(newMaxLossSetableByGuardian < 10000, "Max loss above 100%"); + maxLossSetableByGuardian = newMaxLossSetableByGuardian; + } + + function setGuardian(address newGuardian) onlyGov external { + guardian = newGuardian; + emit NewGuardian(newGuardian); + } + + event NewGuardian(address); +} From 9016b9718f119ea12b257055deb35e903b47f3ad Mon Sep 17 00:00:00 2001 From: 08xmt Date: Mon, 5 Jun 2023 12:01:35 +0200 Subject: [PATCH 2/6] Update aurafed to conform to LossyFed --- src/BaseFed.sol | 43 +++++- src/MintingFed.sol | 19 ++- src/aura-fed/AuraFed.sol | 248 +++++++++++-------------------- src/aura-fed/BalancerAdapter.sol | 74 ++++----- test/AuraFed.t.sol | 110 +++++++------- test/BalancerAdapter.t.sol | 4 +- 6 files changed, 239 insertions(+), 259 deletions(-) diff --git a/src/BaseFed.sol b/src/BaseFed.sol index de41b3b..2847613 100644 --- a/src/BaseFed.sol +++ b/src/BaseFed.sol @@ -14,11 +14,12 @@ abstract contract BaseFed { address public pendingGov; //Chair address allowed to perform expansions and contractions address public chair; + //Address of contract that is allowed to call the migrate functions + address public migrator; constructor(address _DOLA, address _gov, address _chair){ - require(gov != address(0), "Gov set to 0"); + require(_gov != address(0), "Gov set to 0"); require(_DOLA != address(0), "Must be correct DOLA address"); - require(block.chainid == 1, "Must mint DOLA on Mainnet"); gov = _gov; chair = _chair; DOLA = IERC20(_DOLA); @@ -38,6 +39,11 @@ abstract contract BaseFed { _; } + modifier onlyMigrator(){ + require(msg.sender == migrator, "ONLY MIGRATOR"); + _; + } + /** * @notice Sets the pendingGov, which can claim gov role. * @dev Only callable by gov @@ -67,6 +73,15 @@ abstract contract BaseFed { emit NewChair(newChair); } + /** + * @notice Sets the migrator of the Fed + * @dev Should only be set if it's desired to migrate claims and debts to another fed + * @param newMigrator Address of the contract to migrate to + */ + function setMigrator(address newMigrator) onlyGov external { + migrator = newMigrator; + } + /** * @notice Resigns from the Fed role * @dev Useful in case of key compromise or multi-sig compromise @@ -128,7 +143,7 @@ abstract contract BaseFed { function takeProfit(uint flag) external virtual; /** - * @notice Function for withdrawing underlying of Fed in emergency. + * @notice Withdraws claims from Fed in case of an emergency. Can be useful in case of Fed accounting errors, hacks of underlying market or accidents. * @dev Will likely destroy all contract accounting. Use carefully. Should send withdrawn tokens to gov. */ @@ -136,6 +151,28 @@ abstract contract BaseFed { revert("NOT IMPLEMENTED"); } + /** + * @notice Migrates both claims and debt to another fed from this one + * @dev Must send claims tokens to the new fed. This can be LP tokens, cTokens etc. + * @dev Must make sure accounting is correct, with the total number of claims and debt between feds staying the same after migration + * @param claimsToMigrate The number of claims to migrate to the other fed + * @return debtToMigrate The debt associated with the transfered claims + */ + function migrateTo(uint claimsToMigrate) onlyMigrator external virtual returns(uint debtToMigrate){ + revert("NOT IMPLEMENTED"); + } + + /** + * @notice Migrates both claims and debt from another fed to this one + * @dev Must call the migraTo function of the target fed + * @dev Must increment both claims and debt + * @param fed The address of the fed to migrate from + * @param claimsToMigrate The amount of claims to migrate from the target fed + */ + function migrateFrom(address fed, uint claimsToMigrate) onlyChair external virtual { + revert("NOT IMPLEMENTED"); + } + // ********************** // * Standard Functions * // ********************** diff --git a/src/MintingFed.sol b/src/MintingFed.sol index 3239b03..cdd7cf5 100644 --- a/src/MintingFed.sol +++ b/src/MintingFed.sol @@ -48,6 +48,13 @@ abstract contract MintingFed is BaseFed{ */ function takeProfit(uint flag) override external virtual; + /** + * @notice Function for calculating actual supply of claims, which may differ from the claims variable for a variety of reasons. + * @return claims The number of claims the address owns. + */ + function claimsSupply() public view virtual returns(uint claims); + + /** * @notice Function for withdrawing underlying of Fed in emergency. Can be useful in case of Fed accounting errors, hacks of underlying market or accidents. @@ -103,7 +110,11 @@ abstract contract MintingFed is BaseFed{ */ function contraction(uint amount) onlyChair override external { (uint claimsUsed, uint dolaReceived) = _withdraw(amount); - claims -= claimsUsed; + if(claimsUsed > claims){ + claims = 0; + } else { + claims -= claimsUsed; + } _repayDebt(dolaReceived); } @@ -113,7 +124,11 @@ abstract contract MintingFed is BaseFed{ */ function contractAll() onlyChair override external { (uint claimsUsed, uint dolaReceived) = _withdrawAll(); - claims -= claimsUsed; + if(claimsUsed > claims){ + claims = 0; + } else { + claims -= claimsUsed; + } _repayDebt(dolaReceived); } } diff --git a/src/aura-fed/AuraFed.sol b/src/aura-fed/AuraFed.sol index 5ffb037..f00672d 100644 --- a/src/aura-fed/AuraFed.sol +++ b/src/aura-fed/AuraFed.sol @@ -5,225 +5,155 @@ import "src/interfaces/balancer/IVault.sol"; import "src/interfaces/aura/IAuraLocker.sol"; import "src/interfaces/aura/IAuraBalRewardPool.sol"; import "src/aura-fed/BalancerAdapter.sol"; +import "src/MintingFed.sol"; +import "src/LossyFed.sol"; interface IAuraBooster { - function depositAll(uint _pid, bool _stake) external; - function withdraw(uint _pid, uint _amount) external; + function depositAll(uint _guagePid, bool _stake) external; + function withdraw(uint _guagePid, uint _amount) external; } -contract AuraFed is BalancerComposableStablepoolAdapter{ +contract AuraFed is BalancerComposableStablepoolAdapter, LossyFed { IAuraBalRewardPool public dolaBptRewardPool; IAuraBooster public booster; - IERC20 public bal; - IERC20 public aura; - address public chair; // Fed Chair - address public guardian; - address public gov; - uint public dolaSupply; - uint public constant pid = 8; //Gauge pid, should never change - uint public maxLossExpansionBps; - uint public maxLossWithdrawBps; - uint public maxLossTakeProfitBps; - uint public maxLossSetableByGuardian = 500; - - event Expansion(uint amount); - event Contraction(uint amount); + IERC20 public constant BAL = IERC20(0xba100000625a3754423978a60c9317c58a424e3D); + IERC20 public constant AURA = IERC20(0xC0c293ce456fF0ED870ADd98a0828Dd4d2903DBF); + uint public constant guagePid = 8; constructor( address dola_, - address aura_, - address vault_, address dolaBptRewardPool_, + address bpt_, address booster_, address chair_, address guardian_, address gov_, uint maxLossExpansionBps_, - uint maxLossWithdrawBps_, - uint maxLossTakeProfitBps_, - bytes32 poolId_) - BalancerComposableStablepoolAdapter(poolId_, dola_, vault_) + uint maxLossContractionBps_, + uint maxLossTakeProfitBps_) + LossyFed(dola_, gov_, chair_, guardian_, maxLossExpansionBps_, maxLossContractionBps_, maxLossTakeProfitBps_) { - require(maxLossExpansionBps_ < 10000, "Expansion max loss too high"); - require(maxLossWithdrawBps_ < 10000, "Withdraw max loss too high"); - require(maxLossTakeProfitBps_ < 10000, "TakeProfit max loss too high"); + init(bpt_); dolaBptRewardPool = IAuraBalRewardPool(dolaBptRewardPool_); booster = IAuraBooster(booster_); - aura = IERC20(aura_); - bal = IERC20(dolaBptRewardPool.rewardToken()); - (address bpt,) = IVault(vault_).getPool(poolId_); - IERC20(bpt).approve(booster_, type(uint256).max); - maxLossExpansionBps = maxLossExpansionBps_; - maxLossWithdrawBps = maxLossWithdrawBps_; - maxLossTakeProfitBps = maxLossTakeProfitBps_; - chair = chair_; - gov = gov_; - guardian = guardian_; - } - - /** - @notice Method for gov to change gov address - */ - function changeGov(address newGov_) public { - require(msg.sender == gov, "ONLY GOV"); - - gov = newGov_; + BPT.approve(booster_, type(uint256).max); } /** - @notice Method for gov to change the chair - */ - function changeChair(address newChair_) public { - require(msg.sender == gov, "ONLY GOV"); - chair = newChair_; - } - - /** - @notice Method for current chair of the Aura FED to resign - */ - function resign() public { + * @notice Deposits amount of dola tokens into balancer, before locking with aura + * @param dolaAmount Amount of dola token to deposit + * @return claimsReceived Returns the amount of balancer pool tokens received + */ + function _deposit(uint dolaAmount) internal override returns(uint claimsReceived) { require(msg.sender == chair, "ONLY CHAIR"); - chair = address(0); - } - - function setMaxLossExpansionBps(uint newMaxLossExpansionBps) public { - require(msg.sender == gov, "ONLY GOV"); - require(newMaxLossExpansionBps <= 10000, "Can't have max loss above 100%"); - maxLossExpansionBps = newMaxLossExpansionBps; - } - - function setMaxLossWithdrawBps(uint newMaxLossWithdrawBps) public { - require(msg.sender == gov || msg.sender == guardian, "ONLY GOV OR CHAIR"); - if(msg.sender == guardian){ - //We limit the max loss a guardian, as we only want governance to be able to set a very high maxloss - require(newMaxLossWithdrawBps <= maxLossSetableByGuardian, "Above allowed maxloss for chair"); - } - require(newMaxLossWithdrawBps <= 10000, "Can't have max loss above 100%"); - maxLossWithdrawBps = newMaxLossWithdrawBps; - } - - function setMaxLossTakeProfitBps(uint newMaxLossTakeProfitBps) public { - require(msg.sender == gov, "ONLY GOV"); - require(newMaxLossTakeProfitBps <= 10000, "Can't have max loss above 100%"); - maxLossTakeProfitBps = newMaxLossTakeProfitBps; - } - - function setMaxLossSetableByGuardian(uint newMaxLossSetableByGuardian) public { - require(msg.sender == gov, "ONLY GOV"); - require(newMaxLossSetableByGuardian < 10000); - maxLossSetableByGuardian = newMaxLossSetableByGuardian; + DOLA.mint(address(this), dolaAmount); + claimsReceived = _addLiquidity(dolaAmount, maxLossExpansionBps); + booster.depositAll(guagePid, true); } /** - @notice Deposits amount of dola tokens into balancer, before locking with aura - @param amount Amount of dola token to deposit - */ - function expansion(uint amount) public { - require(msg.sender == chair, "ONLY CHAIR"); - dolaSupply += amount; - IERC20(dola).mint(address(this), amount); - _deposit(amount, maxLossExpansionBps); - booster.depositAll(pid, true); - emit Expansion(amount); - } - /** - @notice Withdraws an amount of dola token to be burnt, contracting DOLA dolaSupply - @dev Be careful when setting maxLoss parameter. There will almost always be some loss from - slippage + trading fees that may be incurred when withdrawing from a Balancer pool. - On the other hand, setting the maxLoss too high, may cause you to be front run by MEV - sandwhich bots, making sure your entire maxLoss is incurred. - Recommended to always broadcast withdrawl transactions(contraction & takeProfits) - through a frontrun protected RPC like Flashbots RPC. - @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. - */ - function contraction(uint amountDola) public { + * @notice Withdraws an amount of dola token to be burnt, contracting DOLA debt + * @dev Be careful when setting maxLoss parameter. There will almost always be some loss from + * slippage + trading fees that may be incurred when withdrawing from a Balancer pool. + * On the other hand, setting the maxLoss too high, may cause you to be front run by MEV + * sandwhich bots, making sure your entire maxLoss is incurred. + * Recommended to always broadcast withdrawl transactions(contraction & takeProfits) + * through a frontrun protected RPC like Flashbots RPC. + * @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. + * @return dolaReceived The DOLA received from the withdrawal + */ + function _withdraw(uint amountDola) internal override returns(uint claimsUsed, uint dolaReceived){ require(msg.sender == chair, "ONLY CHAIR"); //Calculate how many lp tokens are needed to withdraw the dola + uint claimsBefore = claimsSupply(); uint bptNeeded = bptNeededForDola(amountDola); - require(bptNeeded <= bptSupply(), "Not enough BPT tokens"); + require(bptNeeded <= claimsSupply(), "Not enough BPT tokens"); //Withdraw BPT tokens from aura, but don't claim rewards require(dolaBptRewardPool.withdrawAndUnwrap(bptNeeded, false), "AURA WITHDRAW FAILED"); - //Withdraw DOLA from balancer pool - uint dolaWithdrawn = _withdraw(amountDola, maxLossWithdrawBps); - require(dolaWithdrawn > 0, "Must contract"); - _burnAndPay(); - emit Contraction(dolaWithdrawn); - } - - /** - @notice Withdraws every remaining balLP token. Can take up to maxLossWithdrawBps in loss, compared to dolaSupply. - It will still be necessary to call takeProfit to withdraw any potential rewards. - */ - function contractAll() public { - require(msg.sender == chair, "ONLY CHAIR"); - //dolaBptRewardPool.withdrawAllAndUnwrap(false); - require(dolaBptRewardPool.withdrawAndUnwrap(dolaBptRewardPool.balanceOf(address(this)), false), "AURA WITHDRAW FAILED"); - uint dolaWithdrawn = _withdrawAll(maxLossWithdrawBps); + uint dolaWithdrawn = _removeLiquidity(amountDola, maxLossContractionBps); require(dolaWithdrawn > 0, "Must contract"); - _burnAndPay(); - emit Contraction(dolaWithdrawn); + return(claimsBefore - claimsSupply(), dolaWithdrawn); } /** - @notice Burns all dola tokens held by the fed up to the dolaSupply, taking any surplus as profit. - */ - function _burnAndPay() internal { - uint dolaBal = dola.balanceOf(address(this)); - if(dolaBal > dolaSupply){ - IERC20(dola).transfer(gov, dolaBal - dolaSupply); - IERC20(dola).burn(dolaSupply); - dolaSupply = 0; - } else { - IERC20(dola).burn(dolaBal); - dolaSupply -= dolaBal; - } + * @notice Withdraws every remaining balLP token. Can take up to maxLossContractionBps in loss, compared to debt. + * @dev It will be necessary to call takeProfit to withdraw any rewards. + * @return claimsUsed The amoutn of balancer pools tokens spent on withdrawing. + * @return dolaReceived The DOLA received from the withdrawal + */ + function _withdrawAll() internal override returns(uint claimsUsed, uint dolaReceived){ + uint totalClaims = claimsSupply(); + uint claimsToUnstake = dolaBptRewardPool.balanceOf(address(this)); + require(dolaBptRewardPool.withdrawAndUnwrap(claimsToUnstake, false), "AURA WITHDRAW FAILED"); + uint dolaWithdrawn = _removeAllLiquidity(maxLossContractionBps); + require(dolaWithdrawn > 0, "MUST CONTRACT"); + return(totalClaims - claimsSupply(), dolaWithdrawn); } /** - @notice Withdraws the profit generated by aura staking - @dev See dev note on Contraction method - */ - function takeProfit(bool harvestLP) public { + * @notice Withdraws the profit generated by aura staking + * @param flag Flags special behaviour that may only be allowed by special roles. + * Flag = 1: Takes profit on BPTs + */ + function takeProfit(uint flag) override external { //This takes balLP at face value, but doesn't take into account slippage or fees //Worth considering that the additional transaction fees incurred by withdrawing the small amount of profit generated by tx fees, //may not eclipse additional transaction costs. Set harvestLP = false to only withdraw bal and aura rewards. - uint bptValue = bptSupply() * bpt.getRate() / 10**18; - if(harvestLP && bptValue > dolaSupply) { + uint bptValue = claimsSupply() * BPT.getRate() / 10**18; + if(flag == 1 && bptValue > debt) { require(msg.sender == chair, "ONLY CHAIR CAN TAKE BPT PROFIT"); - uint dolaSurplus = bptValue - dolaSupply; + uint dolaSurplus = bptValue - debt; uint bptToWithdraw = bptNeededForDola(dolaSurplus); if(bptToWithdraw > dolaBptRewardPool.balanceOf(address(this))){ bptToWithdraw = dolaBptRewardPool.balanceOf(address(this)); } require(dolaBptRewardPool.withdrawAndUnwrap(bptToWithdraw, false), "AURA WITHDRAW FAILED"); - uint dolaProfit = _withdraw(dolaSurplus, maxLossTakeProfitBps); + uint dolaProfit = _removeLiquidity(dolaSurplus, maxLossTakeProfitBps); require(dolaProfit > 0, "NO PROFIT"); - dola.transfer(gov, dolaProfit); + DOLA.transfer(gov, dolaProfit); } require(dolaBptRewardPool.getReward(address(this), true), "Getting reward failed"); - bal.transfer(gov, bal.balanceOf(address(this))); - aura.transfer(gov, aura.balanceOf(address(this))); + uint balBalance = BAL.balanceOf(address(this)); + uint auraBalance = AURA.balanceOf(address(this)); + if(balBalance > 0) BAL.transfer(gov, balBalance); + if(auraBalance > 0) AURA.transfer(gov, auraBalance); } /** - @notice Burns the remaining dola supply in case the FED has been completely contracted, and still has a negative dola balance. - */ - function burnRemainingDolaSupply() public { - dola.transferFrom(msg.sender, address(this), dolaSupply); - dola.burn(dolaSupply); - dolaSupply = 0; + * @notice Withdraws balancer pool tokens to governance in an emergency. + * @dev Will ruin accounting of the contract + */ + function emergencyWithdraw() onlyGov override external { + require(dolaBptRewardPool.withdrawAndUnwrap(dolaBptRewardPool.balanceOf(address(this)), false), "AURA WITHDRAW FAILED"); + BPT.transfer(gov, BPT.balanceOf(address(this))); + } + + /** + * @notice Migrates claims tokens and debt to the Migrator fed contract + * @dev The calling migrator must increment its debt by amount returned by this call + * @param claimsToMigrate Amount of claims tokens to migrate + */ + function migrateTo(uint claimsToMigrate) onlyMigrator override external returns(uint){ + uint totalClaims = claimsSupply(); + 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; + BPT.transfer(migrator, claimsToMigrate); + return debtToMigrate; } /** @notice View function for getting bpt tokens in the contract + aura dolaBptRewardPool */ - function bptSupply() public view returns(uint){ - return IERC20(bpt).balanceOf(address(this)) + dolaBptRewardPool.balanceOf(address(this)); + function claimsSupply() public view override returns(uint){ + return BPT.balanceOf(address(this)) + dolaBptRewardPool.balanceOf(address(this)); } } diff --git a/src/aura-fed/BalancerAdapter.sol b/src/aura-fed/BalancerAdapter.sol index 0871d1d..f5c552d 100644 --- a/src/aura-fed/BalancerAdapter.sol +++ b/src/aura-fed/BalancerAdapter.sol @@ -11,18 +11,18 @@ interface IBPT is IERC20{ contract BalancerComposableStablepoolAdapter { uint constant BPS = 10_000; - bytes32 immutable poolId; - IERC20 immutable dola; - IBPT immutable bpt = IBPT(0x5b3240B6BE3E7487d61cd1AFdFC7Fe4Fa1D81e64); - IVault immutable vault; + bytes32 public poolId; + IERC20 constant _DOLA = IERC20(0x865377367054516e17014CcdED1e7d814EDC9ce4); + IBPT public BPT; + IVault constant VAULT = IVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); IVault.FundManagement fundMan; - constructor(bytes32 poolId_, address dola_, address vault_){ - poolId = poolId_; - dola = IERC20(dola_); - vault = IVault(vault_); - dola.approve(vault_, type(uint).max); - bpt.approve(vault_, type(uint).max); + //Use internal init instead of constructor due to stack too deep + function init(address _bpt) internal{ + BPT = IBPT(_bpt); + poolId = BPT.getPoolId(); + _DOLA.approve(address(VAULT), type(uint).max); + BPT.approve(address(VAULT), type(uint).max); fundMan.sender = address(this); fundMan.fromInternalBalance = false; fundMan.recipient = payable(address(this)); @@ -31,7 +31,7 @@ contract BalancerComposableStablepoolAdapter { /** @notice Swaps exact amount of assetIn for asseetOut through a balancer pool. Output must be higher than minOut - @dev Due to the unique design of Balancer ComposableStablePools, where BPT are part of the swappable balance, we can just swap DOLA directly for BPT + @dev Due to the unique design of Balancer ComposableStablePools, where BPT are part of the swappable balance, we can just swap _DOLA directly for BPT @param assetIn Address of the asset to trade an exact amount in @param assetOut Address of the asset to trade for @param amount Amount of assetIn to trade @@ -48,58 +48,58 @@ contract BalancerComposableStablepoolAdapter { swapStruct.amount = amount; //swapStruct.userData: User data can be left empty - vault.swap(swapStruct, fundMan, minOut, block.timestamp+1); + VAULT.swap(swapStruct, fundMan, minOut, block.timestamp+1); } /** - @notice Deposit an amount of dola into balancer, getting balancer pool tokens in return - @param dolaAmount Amount of dola to buy BPTs for - @param maxSlippage Maximum amount of value that can be lost in basis points, assuming DOLA = 1$ + @notice Deposit an amount of _DOLA into balancer, getting balancer pool tokens in return + @param dolaAmount Amount of _DOLA to buy BPTs for + @param maxSlippage Maximum amount of value that can be lost in basis points, assuming _DOLA = 1$ */ - function _deposit(uint dolaAmount, uint maxSlippage) internal returns(uint){ - uint init = bpt.balanceOf(address(this)); - uint bptWanted = bptNeededForDola(dolaAmount); - uint minBptOut = bptWanted - bptWanted * maxSlippage / BPS; - swapExactIn(address(dola), address(bpt), dolaAmount, minBptOut); - uint bptOut = bpt.balanceOf(address(this)) - init; - return bptOut; + 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; + swapExactIn(address(_DOLA), address(BPT), dolaAmount, minBptOut); + uint BPTOut = BPT.balanceOf(address(this)) - initialBal; + return BPTOut; } /** @notice Withdraws an amount of value close to dolaAmount @dev Will rarely withdraw an amount equal to dolaAmount, due to slippage. - @param dolaAmount Amount of dola the withdrawer wants to withdraw - @param maxSlippage Maximum amount of value that can be lost in basis points, assuming DOLA = 1$ + @param dolaAmount Amount of _DOLA the withdrawer wants to withdraw + @param maxSlippage Maximum amount of value that can be lost in basis points, assuming _DOLA = 1$ */ - function _withdraw(uint dolaAmount, uint maxSlippage) internal returns(uint){ - uint init = dola.balanceOf(address(this)); - 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; - swapExactIn(address(bpt), address(dola), bptNeeded, minDolaOut); - uint dolaOut = dola.balanceOf(address(this)) - init; + swapExactIn(address(BPT), address(_DOLA), BPTNeeded, minDolaOut); + uint dolaOut = _DOLA.balanceOf(address(this)) - initialBal; return dolaOut; } /** @notice Withdraws all BPT in the contract @dev Will rarely withdraw an amount equal to dolaAmount, due to slippage. - @param maxSlippage Maximum amount of value that can be lost in basis points, assuming DOLA = 1$ + @param maxSlippage Maximum amount of value that can be lost in basis points, assuming _DOLA = 1$ */ - function _withdrawAll(uint maxSlippage) internal returns(uint){ - uint bptBal = bpt.balanceOf(address(this)); - uint expectedDolaOut = bptBal * bpt.getRate() / 10**18; + function _removeAllLiquidity(uint maxSlippage) internal returns(uint){ + uint BPTBal = BPT.balanceOf(address(this)); + uint expectedDolaOut = BPTBal * BPT.getRate() / 10**18; uint minDolaOut = expectedDolaOut - expectedDolaOut * maxSlippage / BPS; - swapExactIn(address(bpt), address(dola), bptBal, minDolaOut); - return dola.balanceOf(address(this)); + swapExactIn(address(BPT), address(_DOLA), BPTBal, minDolaOut); + return _DOLA.balanceOf(address(this)); } /** @notice Get amount of BPT equal to the value of dolaAmount, assuming Dola = 1$ @dev Uses the getRate() function of the balancer pool to calculate the value of the dolaAmount - @param dolaAmount Amount of DOLA to get the equal value in BPT. + @param dolaAmount Amount of _DOLA to get the equal value in BPT. @return Uint representing the amount of BPT the dolaAmount should be worth. */ function bptNeededForDola(uint dolaAmount) public view returns(uint) { - return dolaAmount * 10**18 / bpt.getRate(); + return dolaAmount * 10**18 / BPT.getRate(); } } diff --git a/test/AuraFed.t.sol b/test/AuraFed.t.sol index a968e9e..18d8e9e 100644 --- a/test/AuraFed.t.sol +++ b/test/AuraFed.t.sol @@ -12,7 +12,7 @@ interface IMintable is IERC20 { } contract Swapper is BalancerComposableStablepoolAdapter { - constructor(bytes32 poolId_, address dola_, address vault_) BalancerComposableStablepoolAdapter(poolId_, dola_, vault_){} + constructor(address bpt_) {init(bpt_);} function swapExact(address assetIn, address assetOut, uint amount) public{ swapExactIn(assetIn, assetOut, amount, 1); @@ -33,7 +33,7 @@ contract AuraFedTest is DSTest{ address minter = address(0xB); address gov = address(0x926dF14a23BE491164dCF93f4c468A50ef659D5B); uint maxLossExpansion = 20; - uint maxLossWithdraw = 20; + uint maxLossContraction = 20; uint maxLossTakeProfit = 20; bytes32 poolId = bytes32(0x5b3240b6be3e7487d61cd1afdfc7fe4fa1d81e6400000000000000000000037b); address holder = 0x4D2F01D281Dd0b98e75Ca3E1FdD36823B16a7dbf; @@ -43,19 +43,17 @@ contract AuraFedTest is DSTest{ function setUp() public { fed = new AuraFed( address(dola), - address(aura), - vault, address(baseRewardPool), + address(bpt), booster, chair, guardian, gov, maxLossExpansion, - maxLossWithdraw, - maxLossTakeProfit, - poolId + maxLossContraction, + maxLossTakeProfit ); - swapper = new Swapper(poolId, address(dola), vault); + swapper = new Swapper(address(bpt)); vm.startPrank(gov); dola.addMinter(address(fed)); dola.addMinter(minter); @@ -64,17 +62,17 @@ contract AuraFedTest is DSTest{ function testExpansion_succeed_whenExpandedWithinAcceptableSlippage() public { uint amount = 1 ether; - uint initialDolaSupply = fed.dolaSupply(); - uint initialbptSupply = fed.bptSupply(); + uint initialDolaSupply = fed.debt(); + uint initialbptSupply = fed.claims(); uint initialDolaTotalSupply = dola.totalSupply(); vm.prank(chair); fed.expansion(amount); assertEq(initialDolaTotalSupply + amount, dola.totalSupply()); - assertEq(initialDolaSupply + amount, fed.dolaSupply()); + assertEq(initialDolaSupply + amount, fed.debt()); //TODO: Should have greater precision about the amount of balLP acquired - assertGt(fed.bptSupply(), initialbptSupply); + assertGt(fed.claims(), initialbptSupply); } function testFailExpansion_fail_whenExpandedOutsideAcceptableSlippage() public { @@ -88,22 +86,22 @@ contract AuraFedTest is DSTest{ uint amount = 1 ether; vm.prank(chair); fed.expansion(amount*2); - uint initialDolaSupply = fed.dolaSupply(); + uint initialDolaSupply = fed.debt(); uint initialDolaTotalSupply = dola.totalSupply(); - uint initialBalLpSupply = fed.bptSupply(); + uint initialBalLpSupply = fed.claims(); vm.prank(chair); fed.contraction(amount); //Make sure basic accounting of contraction is correct: - assertGt(initialBalLpSupply, fed.bptSupply()); - assertGt(initialDolaSupply, fed.dolaSupply()); + assertGt(initialBalLpSupply, fed.claims()); + assertGt(initialDolaSupply, fed.debt()); assertGt(initialDolaTotalSupply, dola.totalSupply()); - assertEq(initialDolaTotalSupply - dola.totalSupply(), initialDolaSupply - fed.dolaSupply()); + assertEq(initialDolaTotalSupply - dola.totalSupply(), initialDolaSupply - fed.debt()); //Make sure maxLoss wasn't exceeded - assertLe(initialDolaSupply-fed.dolaSupply(), amount*10_000/(10_000-maxLossWithdraw), "Amount withdrawn exceeds maxloss"); - assertLe(initialDolaTotalSupply-dola.totalSupply(), amount*10_000/(10_000-maxLossWithdraw), "Amount withdrawn exceeds maxloss"); + assertLe(initialDolaSupply-fed.debt(), amount*10_000/(10_000-maxLossContraction), "Amount withdrawn exceeds maxloss"); + assertLe(initialDolaTotalSupply-dola.totalSupply(), amount*10_000/(10_000-maxLossContraction), "Amount withdrawn exceeds maxloss"); } function testContraction_succeed_whenContractedWithProfit() public { @@ -111,17 +109,17 @@ contract AuraFedTest is DSTest{ vm.prank(chair); fed.expansion(amount); washTrade(100, 1000_000 ether); - uint initialDolaSupply = fed.dolaSupply(); + uint initialDolaSupply = fed.debt(); uint initialDolaTotalSupply = dola.totalSupply(); - uint initialBalLpSupply = fed.bptSupply(); + uint initialBalLpSupply = fed.claims(); uint initialGovDola = dola.balanceOf(gov); vm.prank(chair); fed.contraction(amount); //Make sure basic accounting of contraction is correct: - assertGt(initialBalLpSupply, fed.bptSupply(), "BPT Supply didn't drop"); - assertEq(initialDolaSupply-amount, fed.dolaSupply(), "Internal Dola Supply didn't drop by test amount"); + assertGt(initialBalLpSupply, fed.claims(), "BPT Supply didn't drop"); + assertEq(initialDolaSupply-amount, fed.debt(), "Internal Dola Supply didn't drop by test amount"); assertEq(initialDolaTotalSupply, dola.totalSupply()+amount, "Total Dola Supply didn't drop by test amount"); assertGt(dola.balanceOf(gov), initialGovDola, "Gov dola balance isn't higher"); } @@ -129,9 +127,9 @@ contract AuraFedTest is DSTest{ function testContractAll_succeed_whenContractedWithinAcceptableSlippage() public { vm.prank(chair); fed.expansion(1000 ether); - uint initialDolaSupply = fed.dolaSupply(); + uint initialDolaSupply = fed.debt(); uint initialDolaTotalSupply = dola.totalSupply(); - uint initialBalLpSupply = fed.bptSupply(); + uint initialBalLpSupply = fed.claims(); vm.prank(chair); fed.contractAll(); @@ -140,30 +138,30 @@ contract AuraFedTest is DSTest{ assertLe(initialDolaTotalSupply-initialDolaSupply, dola.totalSupply()); //Make sure maxLoss wasn't exceeded - assertLe(initialDolaSupply-fed.dolaSupply(), initialDolaSupply*10_000/(10_000-maxLossWithdraw), "Amount withdrawn exceeds maxloss"); - assertLe(initialDolaTotalSupply-dola.totalSupply(), initialDolaSupply*10_000/(10_000-maxLossWithdraw), "Amount withdrawn exceeds maxloss"); + assertLe(initialDolaSupply-fed.debt(), initialDolaSupply*10_000/(10_000-maxLossContraction), "Amount withdrawn exceeds maxloss"); + assertLe(initialDolaTotalSupply-dola.totalSupply(), initialDolaSupply*10_000/(10_000-maxLossContraction), "Amount withdrawn exceeds maxloss"); uint percentageToWithdraw = 10**18; - uint percentageActuallyWithdrawnBal = initialBalLpSupply * 10**18 / (initialBalLpSupply - fed.bptSupply()); - assertLe(percentageActuallyWithdrawnBal * (10_000 - maxLossWithdraw) / 10_000, percentageToWithdraw, "Too much bpt spent"); + uint percentageActuallyWithdrawnBal = initialBalLpSupply * 10**18 / (initialBalLpSupply - fed.claims()); + assertLe(percentageActuallyWithdrawnBal * (10_000 - maxLossContraction) / 10_000, percentageToWithdraw, "Too much bpt spent"); } function testContractAll_succeed_whenContractedWithProfit() public { vm.prank(chair); fed.expansion(1000 ether); washTrade(100, 100_000 ether); - uint initialDolaSupply = fed.dolaSupply(); + uint initialDolaSupply = fed.debt(); uint initialDolaTotalSupply = dola.totalSupply(); uint initialGovDola = dola.balanceOf(gov); - uint initialBalLpSupply = fed.bptSupply(); + uint initialBalLpSupply = fed.claims(); vm.prank(chair); fed.contractAll(); //Make sure basic accounting of contraction is correct: assertEq(initialDolaTotalSupply-initialDolaSupply, dola.totalSupply(), "Dola supply was not decreased by initialDolaSupply"); - assertEq(fed.dolaSupply(), 0); - assertEq(fed.bptSupply(), 0); - assertGt(initialBalLpSupply, fed.bptSupply()); + assertEq(fed.debt(), 0); + assertEq(fed.claims(), 0); + assertGt(initialBalLpSupply, fed.claims()); assertGt(dola.balanceOf(gov), initialGovDola); } @@ -173,14 +171,14 @@ contract AuraFedTest is DSTest{ fed.expansion(1000 ether); uint initialAura = aura.balanceOf(gov); uint initialAuraBal = bal.balanceOf(gov); - uint initialBalLpSupply = fed.bptSupply(); + uint initialBalLpSupply = fed.claims(); uint initialGovDola = dola.balanceOf(gov); - fed.takeProfit(true); + fed.takeProfit(1); vm.stopPrank(); assertEq(aura.balanceOf(gov), initialAura, "treasury aura balance didn't increase"); assertEq(bal.balanceOf(gov), initialAuraBal, "treasury bal balance din't increase"); - assertEq(initialBalLpSupply, fed.bptSupply()); + assertEq(initialBalLpSupply, fed.claims()); assertEq(dola.balanceOf(gov), initialGovDola); } @@ -189,18 +187,18 @@ contract AuraFedTest is DSTest{ fed.expansion(1000 ether); uint initialAura = aura.balanceOf(gov); uint initialAuraBal = bal.balanceOf(gov); - uint initialBalLpSupply = fed.bptSupply(); + uint initialBalLpSupply = fed.claims(); uint initialGovDola = dola.balanceOf(gov); //Pass time washTrade(100, 10_000 ether); vm.warp(baseRewardPool.periodFinish() + 1); vm.startPrank(chair); - fed.takeProfit(false); + fed.takeProfit(0); vm.stopPrank(); assertGt(aura.balanceOf(gov), initialAura, "treasury aura balance didn't increase"); assertGt(bal.balanceOf(gov), initialAuraBal, "treasury bal balance din't increase"); - assertEq(initialBalLpSupply, fed.bptSupply(), "bpt supply changed"); + assertEq(initialBalLpSupply, fed.claims(), "bpt supply changed"); assertEq(dola.balanceOf(gov), initialGovDola, "Gov DOLA supply changed"); } @@ -214,20 +212,20 @@ contract AuraFedTest is DSTest{ vm.startPrank(chair); uint initialAura = aura.balanceOf(gov); uint initialAuraBal = bal.balanceOf(gov); - uint initialBalLpSupply = fed.bptSupply(); + uint initialBalLpSupply = fed.claims(); uint initialGovDola = dola.balanceOf(gov); fed.contraction(200 ether); - assertEq(fed.dolaSupply(), 0); + assertEq(fed.debt(), 0); //Pass time washTrade(100, 10_000 ether); vm.warp(baseRewardPool.periodFinish() + 1); vm.startPrank(chair); - fed.takeProfit(true); + fed.takeProfit(1); vm.stopPrank(); assertGt(aura.balanceOf(gov), initialAura, "treasury aura balance didn't increase"); assertGt(bal.balanceOf(gov), initialAuraBal, "treasury bal balance din't increase"); - assertGt(initialBalLpSupply, fed.bptSupply(), "bpt Supply wasn't reduced"); + assertGt(initialBalLpSupply, fed.claims(), "bpt Supply wasn't reduced"); assertGt(dola.balanceOf(gov), initialGovDola, "Gov DOLA balance didn't increase"); } @@ -239,8 +237,8 @@ contract AuraFedTest is DSTest{ dola.mint(address(minter), 1000 ether); dola.approve(address(fed), 1000 ether); - fed.burnRemainingDolaSupply(); - assertEq(fed.dolaSupply(), 0); + fed.repayDebt(fed.debt()); + assertEq(fed.debt(), 0); } function testContraction_FailWithOnlyChair_whenCalledByOtherAddress() public { @@ -259,14 +257,14 @@ contract AuraFedTest is DSTest{ assertTrue(initial != fed.maxLossExpansionBps()); } - function testSetMaxLossWithdrawBps_succeed_whenCalledByGov() public { - uint initial = fed.maxLossWithdrawBps(); + function testSetMaxLossContractionBps_succeed_whenCalledByGov() public { + uint initial = fed.maxLossContractionBps(); vm.prank(gov); - fed.setMaxLossWithdrawBps(1); + fed.setMaxLossContractionBps(1); - assertEq(fed.maxLossWithdrawBps(), 1); - assertTrue(initial != fed.maxLossWithdrawBps()); + assertEq(fed.maxLossContractionBps(), 1); + assertTrue(initial != fed.maxLossContractionBps()); } function testSetMaxLossTakeProfitBps_succeed_whenCalledByGov() public { @@ -288,16 +286,16 @@ contract AuraFedTest is DSTest{ assertEq(fed.maxLossExpansionBps(), initial); } - function testSetMaxLossWithdrawBps_fail_whenCalledByGov() public { - uint initial = fed.maxLossWithdrawBps(); + function testSetMaxLossContractionBps_fail_whenNotCalledByGov() public { + uint initial = fed.maxLossContractionBps(); vm.expectRevert("ONLY GOV"); - fed.setMaxLossWithdrawBps(1); + fed.setMaxLossContractionBps(1); - assertEq(fed.maxLossWithdrawBps(), initial); + assertEq(fed.maxLossContractionBps(), initial); } - function testSetMaxLossTakeProfitBps_fail_whenCalledByGov() public { + function testSetMaxLossTakeProfitBps_fail_whenNotCalledByGov() public { uint initial = fed.maxLossTakeProfitBps(); vm.expectRevert("ONLY GOV"); diff --git a/test/BalancerAdapter.t.sol b/test/BalancerAdapter.t.sol index 13ced02..77ac98f 100644 --- a/test/BalancerAdapter.t.sol +++ b/test/BalancerAdapter.t.sol @@ -10,7 +10,7 @@ interface IMintable is IERC20 { } contract Swapper is BalancerComposableStablepoolAdapter { - constructor(bytes32 poolId_, address dola_, address vault_) BalancerComposableStablepoolAdapter(poolId_, dola_, vault_){} + constructor(address bpt_) {init(bpt_);} function swapExact(address assetIn, address assetOut, uint amount) public{ swapExactIn(assetIn, assetOut, amount, 1); @@ -30,7 +30,7 @@ contract BalancerTest is DSTest{ Swapper swapper; function setUp() public { - swapper = new Swapper(poolId, address(dola), vault); + swapper = new Swapper(address(bpt)); vm.prank(gov); dola.addMinter(minter); } From 6c3e158734b3f713415b7bbe4a7caec283b220bf Mon Sep 17 00:00:00 2001 From: 08xmt Date: Tue, 6 Jun 2023 16:28:33 +0200 Subject: [PATCH 3/6] Test baseFed functionality --- test/BaseFed.t.sol | 162 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 test/BaseFed.t.sol diff --git a/test/BaseFed.t.sol b/test/BaseFed.t.sol new file mode 100644 index 0000000..61bd830 --- /dev/null +++ b/test/BaseFed.t.sol @@ -0,0 +1,162 @@ +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "src/BaseFed.sol"; + +contract MockToken { + mapping(address => uint) public balanceOf; + + function transfer(address to, uint amount) external returns(bool){ + balanceOf[msg.sender] -= amount; + balanceOf[to] += amount; + return true; + } + + function transferFrom(address from, address to, uint amount) external returns(bool){ + balanceOf[from] -= amount; + balanceOf[to] += amount; + return true; + } + + function mint(address to, uint amount) external { + balanceOf[to] += amount; + } + + +} + +contract BaseFedImplementation is BaseFed{ + constructor(address dola, address gov, address chair) BaseFed(dola, gov, chair){} + + function _repayDebt(uint amount) internal override{ + debt -= amount; + } + + function takeProfit(uint flag) external override{} + + function increaseDebt(uint amount) external{ + debt += amount; + } +} + +contract BaseFedTest is Test{ + + address user = address(0xA); + address dola = 0x8F97cCA30Dbe80e7a8B462F1dD1a51C32accDfC8; + address gov = 0x8F97cCA30Dbe80e7a8B462F1dD1a51C32accDfC8; + address chair = 0x8F97cCA30Dbe80e7a8B462F1dD1a51C32accDfC8; + BaseFedImplementation fed; + + function setUp() public { + dola = address(new MockToken()); + fed = new BaseFedImplementation(dola, gov, chair); + } + + function testSetPendingGov_success_WhenCalledByGov() external{ + vm.prank(gov); + fed.setPendingGov(user); + assertEq(fed.pendingGov(), user, "Pending gov not eq user"); + } + + function testSetPendingGov_fails_WhenCalledByNonGov(address caller) external{ + vm.assume(caller != fed.gov()); + vm.prank(caller); + vm.expectRevert("NOT GOV"); + fed.setPendingGov(user); + } + + function testClaimPendingGov_success_WhenClaimedByPendingGov() external{ + vm.prank(gov); + fed.setPendingGov(user); + assertEq(fed.pendingGov(), user, "Pending not user"); + vm.prank(user); + fed.claimPendingGov(); + assertEq(fed.gov(), user, "Gov not user"); + } + + function testClaimPendingGov_fails_WhenClaimedByNonPendingGov(address caller) external{ + vm.assume(user != caller); + vm.prank(gov); + fed.setPendingGov(user); + vm.prank(caller); + vm.expectRevert("NOT PENDING GOV"); + fed.claimPendingGov(); + } + + function testSetChair_success_WhenCalledByGov() external { + vm.prank(gov); + fed.setChair(user); + assertEq(fed.chair(), user, "User not chair"); + } + + function testSetChair_fails_WhenCalledByNonGov(address caller) external { + vm.assume(caller != fed.gov()); + vm.prank(caller); + vm.expectRevert("NOT GOV"); + fed.setChair(user); + } + + function testSetMigrator_success_WhenCalledByGov() external { + vm.prank(gov); + fed.setMigrator(user); + assertEq(fed.migrator(), user, "User not migrator"); + } + + function testSetMigrator_fails_WhenCalledByNonGov(address caller) external { + vm.assume(caller != fed.gov()); + vm.prank(caller); + vm.expectRevert("NOT GOV"); + fed.setMigrator(user); + } + + function testResign_SetsChairToZero_WhenCalledByChair() external { + vm.prank(chair); + fed.resign(); + assertEq(fed.chair(), address(0), "Fed chair not zero address"); + } + + function testResign_Fails_WhenCalledByNonChair(address caller) external { + vm.assume(caller != fed.chair() && caller != fed.gov()); + vm.prank(caller); + vm.expectRevert("NOT PERMISSIONED"); + fed.resign(); + } + + function testSweep_TransfersAllToGov_WhenCalledByGov() external { + MockToken mockToken = new MockToken(); + mockToken.mint(address(fed), 1 ether); + assertEq(mockToken.balanceOf(gov), 0); + vm.prank(gov); + fed.sweep(address(mockToken)); + assertEq(mockToken.balanceOf(gov), 1 ether, "Gov did not receive swept tokens"); + } + + function testSweep_Fails_WhenCalledByNonGov(address caller) external { + vm.assume(caller != gov); + MockToken mockToken = new MockToken(); + mockToken.mint(address(fed), 1 ether); + assertEq(mockToken.balanceOf(gov), 0); + vm.prank(caller); + vm.expectRevert("NOT GOV"); + fed.sweep(address(mockToken)); + assertEq(mockToken.balanceOf(gov), 0, "Gov received ether"); + } + + function testRepayDebt_TransfersDolaToContract_WhenCalledByUserWithDolaTokens() external { + MockToken(dola).mint(user, 1 ether); + fed.increaseDebt(1 ether); + vm.prank(user); + fed.repayDebt(1 ether); + assertEq(fed.debt(), 0); + assertEq(MockToken(dola).balanceOf(address(fed)), 1 ether); + } + + function testRepayDebt_FailsWhenRepayingMoreThanDebt_WhenCalledByUserWithDolaTokens() external { + MockToken(dola).mint(user, 2 ether); + fed.increaseDebt(1 ether); + vm.prank(user); + vm.expectRevert("BURN HIGHER THAN DEBT"); + fed.repayDebt(2 ether); + } + +} From 4023eff1f785b10dc83a9fc94900c71162ac6597 Mon Sep 17 00:00:00 2001 From: 08xmt Date: Thu, 8 Jun 2023 13:38:01 +0200 Subject: [PATCH 4/6] Update AuraFed tests --- src/aura-fed/AuraFed.sol | 10 +- src/aura-fed/BalancerStablepoolAdapter.sol | 124 +++++++++++++++++ test/AuraFed.t.sol | 149 ++++++++++++++------- test/MinterFed.t.sol | 135 +++++++++++++++++++ 4 files changed, 363 insertions(+), 55 deletions(-) create mode 100644 src/aura-fed/BalancerStablepoolAdapter.sol create mode 100644 test/MinterFed.t.sol diff --git a/src/aura-fed/AuraFed.sol b/src/aura-fed/AuraFed.sol index f00672d..f2b23b2 100644 --- a/src/aura-fed/AuraFed.sol +++ b/src/aura-fed/AuraFed.sol @@ -4,7 +4,7 @@ import "src/interfaces/IERC20.sol"; import "src/interfaces/balancer/IVault.sol"; import "src/interfaces/aura/IAuraLocker.sol"; import "src/interfaces/aura/IAuraBalRewardPool.sol"; -import "src/aura-fed/BalancerAdapter.sol"; +import "src/aura-fed/BalancerStablepoolAdapter.sol"; import "src/MintingFed.sol"; import "src/LossyFed.sol"; @@ -13,13 +13,13 @@ interface IAuraBooster { function withdraw(uint _guagePid, uint _amount) external; } -contract AuraFed is BalancerComposableStablepoolAdapter, LossyFed { +contract AuraFed is BalancerStablepoolAdapter, LossyFed { IAuraBalRewardPool public dolaBptRewardPool; IAuraBooster public booster; IERC20 public constant BAL = IERC20(0xba100000625a3754423978a60c9317c58a424e3D); IERC20 public constant AURA = IERC20(0xC0c293ce456fF0ED870ADd98a0828Dd4d2903DBF); - uint public constant guagePid = 8; + uint public constant guagePid = 45; constructor( address dola_, @@ -47,7 +47,6 @@ contract AuraFed is BalancerComposableStablepoolAdapter, LossyFed { */ function _deposit(uint dolaAmount) internal override returns(uint claimsReceived) { require(msg.sender == chair, "ONLY CHAIR"); - DOLA.mint(address(this), dolaAmount); claimsReceived = _addLiquidity(dolaAmount, maxLossExpansionBps); booster.depositAll(guagePid, true); } @@ -139,6 +138,7 @@ contract AuraFed is BalancerComposableStablepoolAdapter, LossyFed { * @notice Migrates claims tokens and debt to the Migrator fed contract * @dev The calling migrator must increment its debt by amount returned by this call * @param claimsToMigrate Amount of claims tokens to migrate + * @return Amount of debt to migrate */ function migrateTo(uint claimsToMigrate) onlyMigrator override external returns(uint){ uint totalClaims = claimsSupply(); @@ -146,6 +146,8 @@ contract AuraFed is BalancerComposableStablepoolAdapter, LossyFed { uint claimsToUnstake = claimsToMigrate - BPT.balanceOf(address(this)); require(dolaBptRewardPool.withdrawAndUnwrap(claimsToUnstake, false), "AURA WITHDRAW FAILED"); uint debtToMigrate = debt * claimsToMigrate / totalClaims; + debt -= debtToMigrate; + claims -= claimsToMigrate; BPT.transfer(migrator, claimsToMigrate); return debtToMigrate; } diff --git a/src/aura-fed/BalancerStablepoolAdapter.sol b/src/aura-fed/BalancerStablepoolAdapter.sol new file mode 100644 index 0000000..14a634e --- /dev/null +++ b/src/aura-fed/BalancerStablepoolAdapter.sol @@ -0,0 +1,124 @@ +pragma solidity ^0.8.13; + +import "src/interfaces/balancer/IVault.sol"; +import "src/interfaces/IERC20.sol"; + +interface IBPT is IERC20{ + function getPoolId() external view returns (bytes32); + function getRate() external view returns (uint256); +} + +interface IBalancerHelper{ + function queryExit(bytes32 poolId, address sender, address recipient, IVault.ExitPoolRequest memory erp) external returns (uint256 BPTIn, uint256[] memory amountsOut); + function queryJoin(bytes32 poolId, address sender, address recipient, IVault.JoinPoolRequest memory jrp) external returns (uint256 BPTOut, uint256[] memory amountsIn); +} + +contract BalancerStablepoolAdapter { + + uint constant BPS = 10_000; + bytes32 poolId; + IERC20 constant dola = IERC20(0x865377367054516e17014CcdED1e7d814EDC9ce4); + IERC20 constant usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); + IBPT BPT; + IVault constant vault = IVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); + IAsset[] assets = new IAsset[](0); + uint dolaIndex = type(uint).max; + + function init( address _BPT) internal{ + BPT = IBPT(_BPT); + poolId = BPT.getPoolId(); + dola.approve(address(vault), type(uint).max); + BPT.approve(address(vault), type(uint).max); + (address[] memory tokens,,) = vault.getPoolTokens(poolId); + for(uint i; i BPTWanted - BPTWanted * maxSlippage / BPS, "Insufficient BPT received"); + return BPTOut; + } + + function _removeLiquidity(uint dolaAmount, uint maxSlippage) internal returns(uint){ + uint initial = dola.balanceOf(address(this)); + uint BPTNeeded = bptNeededForDola(dolaAmount); + uint minDolaOut = dolaAmount - dolaAmount * maxSlippage / BPS; + vault.exitPool(poolId, address(this), payable(address(this)), createExitExactPoolRequest(dolaIndex, BPTNeeded, minDolaOut)); + uint dolaOut = dola.balanceOf(address(this)) - initial; + return dolaOut; + } + + function _removeAllLiquidity(uint maxSlippage) internal returns(uint){ + uint BPTBal = BPT.balanceOf(address(this)); + uint expectedDolaOut = BPTBal * BPT.getRate() / 10**18; + uint minDolaOut = expectedDolaOut - expectedDolaOut * maxSlippage / BPS; + vault.exitPool(poolId, address(this), payable(address(this)), createExitExactPoolRequest(dolaIndex, BPTBal, minDolaOut)); + return dola.balanceOf(address(this)); + } + + function bptNeededForDola(uint dolaAmount) public view returns(uint) { + return dolaAmount * 10 ** 18 / BPT.getRate(); + } +} diff --git a/test/AuraFed.t.sol b/test/AuraFed.t.sol index 18d8e9e..bf69789 100644 --- a/test/AuraFed.t.sol +++ b/test/AuraFed.t.sol @@ -1,9 +1,8 @@ pragma solidity ^0.8.13; -import "ds-test/test.sol"; -import "forge-std/Vm.sol"; +import "forge-std/Test.sol"; import "src/aura-fed/AuraFed.sol"; -import "src/aura-fed/BalancerAdapter.sol"; +import {BalancerComposableStablepoolAdapter} from "src/aura-fed/BalancerAdapter.sol"; import "src/interfaces/IERC20.sol"; import "src/interfaces/aura/IAuraBalRewardPool.sol"; @@ -12,31 +11,33 @@ interface IMintable is IERC20 { } contract Swapper is BalancerComposableStablepoolAdapter { - constructor(address bpt_) {init(bpt_);} + constructor(address bpt_) { + init(bpt_); + IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48).approve(address(VAULT), type(uint).max); + } function swapExact(address assetIn, address assetOut, uint amount) public{ swapExactIn(assetIn, assetOut, amount, 1); } } -contract AuraFedTest is DSTest{ - Vm internal constant vm = Vm(HEVM_ADDRESS); +contract AuraFedTest is Test{ IMintable dola = IMintable(0x865377367054516e17014CcdED1e7d814EDC9ce4); - IERC20 bpt = IERC20(0x5b3240B6BE3E7487d61cd1AFdFC7Fe4Fa1D81e64); + IERC20 bpt = IERC20(0xFf4ce5AAAb5a627bf82f4A571AB1cE94Aa365eA6); IERC20 bal = IERC20(0xba100000625a3754423978a60c9317c58a424e3D); IERC20 aura = IERC20(0xC0c293ce456fF0ED870ADd98a0828Dd4d2903DBF); + address usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; address vault = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; - IAuraBalRewardPool baseRewardPool = IAuraBalRewardPool(0x99653d46D52eE41c7b35cbAd1aC408A00bad6A76); - address booster = 0x7818A1DA7BD1E64c199029E86Ba244a9798eEE10; + IAuraBalRewardPool baseRewardPool = IAuraBalRewardPool(0x22915f309EC0182c85cD8331C23bD187fd761360); + address booster = 0xA57b8d98dAE62B26Ec3bcC4a365338157060B234; address chair = address(0xA); address guardian = address(0xB); address minter = address(0xB); + address migrator = address(0xC); address gov = address(0x926dF14a23BE491164dCF93f4c468A50ef659D5B); uint maxLossExpansion = 20; - uint maxLossContraction = 20; + uint maxLossContraction = 40; uint maxLossTakeProfit = 20; - bytes32 poolId = bytes32(0x5b3240b6be3e7487d61cd1afdfc7fe4fa1d81e6400000000000000000000037b); - address holder = 0x4D2F01D281Dd0b98e75Ca3E1FdD36823B16a7dbf; AuraFed fed; Swapper swapper; @@ -76,7 +77,7 @@ contract AuraFedTest is DSTest{ } function testFailExpansion_fail_whenExpandedOutsideAcceptableSlippage() public { - uint amount = 1000_000 ether; + uint amount = 10_000_000 ether; vm.prank(chair); fed.expansion(amount); @@ -105,10 +106,11 @@ contract AuraFedTest is DSTest{ } function testContraction_succeed_whenContractedWithProfit() public { + balancePool(); uint amount = 1000 ether; vm.prank(chair); fed.expansion(amount); - washTrade(100, 1000_000 ether); + washTrade(100, 1_000_000 ether); uint initialDolaSupply = fed.debt(); uint initialDolaTotalSupply = dola.totalSupply(); uint initialBalLpSupply = fed.claims(); @@ -119,9 +121,10 @@ contract AuraFedTest is DSTest{ //Make sure basic accounting of contraction is correct: assertGt(initialBalLpSupply, fed.claims(), "BPT Supply didn't drop"); - assertEq(initialDolaSupply-amount, fed.debt(), "Internal Dola Supply didn't drop by test amount"); - assertEq(initialDolaTotalSupply, dola.totalSupply()+amount, "Total Dola Supply didn't drop by test amount"); - assertGt(dola.balanceOf(gov), initialGovDola, "Gov dola balance isn't higher"); + assertLe(initialDolaSupply, fed.debt()+amount, "Internal Dola Supply didn't drop by test amount"); + assertGt(initialDolaSupply, fed.debt()+(amount - amount * maxLossContraction / 10000), "Internal Dola Supply didn't drop by test amount"); + assertLe(initialDolaTotalSupply, dola.totalSupply()+amount, "Total Dola Supply didn't drop by test amount"); + assertGt(initialDolaTotalSupply, dola.totalSupply()+(amount - amount * maxLossContraction / 10000), "Total Dola Supply didn't drop by test amount"); } function testContractAll_succeed_whenContractedWithinAcceptableSlippage() public { @@ -201,44 +204,80 @@ contract AuraFedTest is DSTest{ assertEq(initialBalLpSupply, fed.claims(), "bpt supply changed"); assertEq(dola.balanceOf(gov), initialGovDola, "Gov DOLA supply changed"); } - - function testTakeProfit_IncreaseGovDolaBalance_whenDolaHasBeenSentToContract() public { - vm.startPrank(chair); - fed.expansion(1000 ether); - vm.stopPrank(); - vm.startPrank(minter); - dola.mint(address(fed), 1000 ether); - vm.stopPrank(); - vm.startPrank(chair); - uint initialAura = aura.balanceOf(gov); - uint initialAuraBal = bal.balanceOf(gov); - uint initialBalLpSupply = fed.claims(); - uint initialGovDola = dola.balanceOf(gov); - fed.contraction(200 ether); - assertEq(fed.debt(), 0); - //Pass time - washTrade(100, 10_000 ether); - vm.warp(baseRewardPool.periodFinish() + 1); - vm.startPrank(chair); - fed.takeProfit(1); - vm.stopPrank(); - - assertGt(aura.balanceOf(gov), initialAura, "treasury aura balance didn't increase"); - assertGt(bal.balanceOf(gov), initialAuraBal, "treasury bal balance din't increase"); - assertGt(initialBalLpSupply, fed.claims(), "bpt Supply wasn't reduced"); - assertGt(dola.balanceOf(gov), initialGovDola, "Gov DOLA balance didn't increase"); - } function testburnRemainingDolaSupply_Success() public { vm.startPrank(chair); fed.expansion(1000 ether); vm.stopPrank(); vm.startPrank(minter); - dola.mint(address(minter), 1000 ether); + dola.mint(minter, 1000 ether); dola.approve(address(fed), 1000 ether); + uint dolaSupplyBefore = dola.totalSupply(); + uint minterBalanceBefore = dola.balanceOf(minter); fed.repayDebt(fed.debt()); assertEq(fed.debt(), 0); + assertEq(dola.totalSupply(), dolaSupplyBefore - 1000 ether); + assertEq(dola.balanceOf(minter), minterBalanceBefore - 1000 ether); + } + + function testMigrateClaims_Success_WhenCalledByMigrator() public { + vm.prank(chair); + fed.expansion(1000 ether); + uint initialClaims = fed.claims(); + uint initialDebt = fed.debt(); + vm.prank(gov); + fed.setMigrator(migrator); + vm.prank(migrator); + uint debtToMigrate = fed.migrateTo(initialClaims); + + assertEq(bpt.balanceOf(migrator), initialClaims, "Migrator did not received correct amount of claims"); + assertEq(debtToMigrate, initialDebt, "debtToMigrate not equal debt"); + assertEq(fed.claims(), 0, "Fed claims not 0"); + assertEq(fed.debt(), 0, "Fed debt not 0"); + + } + + function testMigrateClaims_Fails_WhenCalledByNonMigrator(address caller) public { + vm.assume(caller != migrator); + vm.prank(chair); + fed.expansion(1000 ether); + uint claims = fed.claims(); + vm.prank(gov); + fed.setMigrator(migrator); + vm.prank(caller); + vm.expectRevert("ONLY MIGRATOR"); + uint debtToMigrate = fed.migrateTo(claims); + + } + + function testMigrateClaims_Success_WhenMigratingLessThanFullAmount(uint migrationAmount) public { + vm.assume(migrationAmount > 1 ether); + migrationAmount = migrationAmount % 1000 ether; + vm.prank(chair); + fed.expansion(1000 ether); + uint initialClaims = fed.claims(); + uint claimsMigrationAmount = fed.claims() * migrationAmount / 1000 ether; + uint initialDebt = fed.debt(); + vm.prank(gov); + fed.setMigrator(migrator); + vm.prank(migrator); + uint debtToMigrate = fed.migrateTo(claimsMigrationAmount); + + assertEq(bpt.balanceOf(migrator), claimsMigrationAmount, "Migrator did not received correct amount of claims"); + assertEq(fed.claims(), initialClaims - claimsMigrationAmount, "Fed claims not decreased correctly"); + assertEq(fed.debt(), initialDebt - debtToMigrate, "Fed debt not decreased correctly"); + } + + function testEmergencyWithdraw_Success_WhenCalledByGov() public { + vm.prank(chair); + fed.expansion(1000 ether); + uint initialClaimsSupply = fed.claimsSupply(); + vm.prank(gov); + fed.emergencyWithdraw(); + + assertEq(bpt.balanceOf(gov), initialClaimsSupply); + assertEq(fed.claimsSupply(), 0); } function testContraction_FailWithOnlyChair_whenCalledByOtherAddress() public { @@ -280,7 +319,7 @@ contract AuraFedTest is DSTest{ function testSetMaxLossExpansionBps_fail_whenCalledByNonGov() public { uint initial = fed.maxLossExpansionBps(); - vm.expectRevert("ONLY GOV"); + vm.expectRevert("NOT GOV"); fed.setMaxLossExpansionBps(1); assertEq(fed.maxLossExpansionBps(), initial); @@ -289,7 +328,7 @@ contract AuraFedTest is DSTest{ function testSetMaxLossContractionBps_fail_whenNotCalledByGov() public { uint initial = fed.maxLossContractionBps(); - vm.expectRevert("ONLY GOV"); + vm.expectRevert("ONLY GOV OR GUARDIAN"); fed.setMaxLossContractionBps(1); assertEq(fed.maxLossContractionBps(), initial); @@ -298,20 +337,28 @@ contract AuraFedTest is DSTest{ function testSetMaxLossTakeProfitBps_fail_whenNotCalledByGov() public { uint initial = fed.maxLossTakeProfitBps(); - vm.expectRevert("ONLY GOV"); + vm.expectRevert("NOT GOV"); fed.setMaxLossTakeProfitBps(1); assertEq(fed.maxLossTakeProfitBps(), initial); } + function balancePool() public { + (,uint[] memory balances,) = IVault(vault).getPoolTokens(IBPT(address(bpt)).getPoolId()); + uint inbalance = balances[0] - balances[1]*10**10; + uint swapAmount = inbalance / 10**18; + deal(usdc, address(swapper), swapAmount); + swapper.swapExact(usdc, address(dola), swapAmount); + + } + function washTrade(uint loops, uint amount) public { - vm.stopPrank(); vm.startPrank(minter); dola.mint(address(swapper), amount); //Trade back and forth to create a profit for(uint i; i < loops; i++){ - swapper.swapExact(address(dola), address(bpt), dola.balanceOf(address(swapper))); - swapper.swapExact(address(bpt), address(dola), bpt.balanceOf(address(swapper))); + swapper.swapExact(address(dola), usdc, dola.balanceOf(address(swapper))); + swapper.swapExact(usdc, address(dola), IERC20(usdc).balanceOf(address(swapper))); } vm.stopPrank(); } diff --git a/test/MinterFed.t.sol b/test/MinterFed.t.sol new file mode 100644 index 0000000..f53ade5 --- /dev/null +++ b/test/MinterFed.t.sol @@ -0,0 +1,135 @@ +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "src/MintingFed.sol"; + +contract MockToken { + mapping(address => uint) public balanceOf; + uint public totalSupply; + + function transfer(address to, uint amount) external returns(bool){ + balanceOf[msg.sender] -= amount; + balanceOf[to] += amount; + return true; + } + + function transferFrom(address from, address to, uint amount) external returns(bool){ + balanceOf[from] -= amount; + balanceOf[to] += amount; + return true; + } + + function mint(address to, uint amount) external { + balanceOf[to] += amount; + totalSupply += amount; + } + + function burn(uint amount) external { + balanceOf[msg.sender] -= amount; + totalSupply -= amount; + } +} + +contract MintingFedImplementation is MintingFed{ + uint lossFactor = 1 ether; + constructor(address dola, address gov, address chair) MintingFed(dola, gov, chair){ + + } + + function setLossFactor(uint newLossFactor) external { + lossFactor = newLossFactor; + } + + + function _deposit(uint dolaAmount) internal override returns(uint claimsReceived){ + return dolaAmount * lossFactor / 1 ether; + } + + function _withdraw(uint dolaAmount) internal override returns(uint claimsUsed, uint dolaReceived){ + return (dolaAmount, dolaAmount * lossFactor / 1 ether); + } + + function _withdrawAll() internal override returns(uint claimsUSed, uint dolaReceived){ + return(claims, claims * lossFactor / 1 ether); + } + + function takeProfit(uint flag) override external{} + + function claimsSupply() public view override returns(uint claims){return claims;} + +} + +contract MintingFedTest is Test{ + + address user = address(0xA); + MockToken dola; + address gov = 0x8F97cCA30Dbe80e7a8B462F1dD1a51C32accDfC8; + address chair = 0x8F97cCA30Dbe80e7a8B462F1dD1a51C32accDfC8; + MintingFedImplementation fed; + + function setUp() public { + vm.chainId(1); + dola = new MockToken(); + fed = new MintingFedImplementation(address(dola), gov, chair); + } + + function testExpansion_Succeeds_WhenCalledByFedChair(uint128 expansion) external { + uint totalSupplyBefore = dola.totalSupply(); + uint claimsBefore = fed.claims(); + vm.prank(chair); + fed.expansion(uint(expansion)); + assertEq(totalSupplyBefore + uint(expansion), dola.totalSupply(), "Total supply didn't increase"); + assertEq(fed.debt(), uint(expansion), "Fed debt did not increase by expansion"); + if(expansion > 0){ + assertGt(fed.claims(), claimsBefore, "Fed claims did not increase"); + } + } + + function testExpansion_Succeeds_WhenCalledTwice(uint128 expansion) external { + uint totalSupplyBefore = dola.totalSupply(); + uint claimsBefore = fed.claims(); + vm.startPrank(chair); + fed.expansion(uint(expansion)); + fed.expansion(uint(expansion)); + vm.stopPrank(); + assertEq(totalSupplyBefore + uint(expansion)*2, dola.totalSupply(), "Total supply didn't increase"); + assertEq(fed.debt(), uint(expansion)*2, "Fed debt did not increase by expansion"); + if(expansion > 0){ + assertGt(fed.claims(), claimsBefore, "Fed claims did not increase"); + } + } + + function testExpansion_Fails_WhenCalledByNonFedChair(address caller) external { + vm.assume(caller != chair && caller != gov); + vm.prank(caller); + vm.expectRevert("NOT PERMISSIONED"); + fed.expansion(1 ether); + } + + function testContraction_Succeeds_WhenCalledByFedChair(uint128 expansion, uint128 contraction) external { + vm.assume(expansion >= contraction); + uint totalSupplyBefore = dola.totalSupply(); + uint claimsBefore = fed.claims(); + vm.startPrank(chair); + fed.expansion(uint(expansion)); + uint claimsAfterExpansion = fed.claims(); + fed.contraction(uint(contraction)); + assertEq(totalSupplyBefore + uint(expansion - contraction), dola.totalSupply(), "Total supply didn't increase"); + assertEq(fed.debt(), uint(expansion - contraction), "Fed debt did not increase by expansion"); + if(expansion - contraction > 0){ + assertGt(fed.claims(), claimsBefore, "Fed claims did not increase"); + } + if(contraction > 0){ + assertLt(fed.claims(), claimsAfterExpansion, "Fed claims did not decrease with contraction"); + } + } + + function testContraction_Fails_WhenCalledByNonFedChair(address caller) external { + vm.assume(caller != chair && caller != gov); + vm.prank(chair); + fed.expansion(1 ether); + vm.prank(caller); + vm.expectRevert("NOT PERMISSIONED"); + fed.contraction(1 ether); + } +} From 6ecff7f479a34255640f52c6344498d61ae272e6 Mon Sep 17 00:00:00 2001 From: 08xmt Date: Thu, 8 Jun 2023 14:03:24 +0200 Subject: [PATCH 5/6] Add a few more access control tests --- test/AuraFed.t.sol | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/AuraFed.t.sol b/test/AuraFed.t.sol index bf69789..3794bd1 100644 --- a/test/AuraFed.t.sol +++ b/test/AuraFed.t.sol @@ -269,6 +269,16 @@ contract AuraFedTest is Test{ assertEq(fed.debt(), initialDebt - debtToMigrate, "Fed debt not decreased correctly"); } + function testSetMigrator_Fails_WhenCalledByNonGov(address caller) public { + vm.assume(caller != migrator); + vm.prank(chair); + fed.expansion(1000 ether); + vm.prank(caller); + vm.expectRevert("NOT GOV"); + fed.setMigrator(migrator); + + } + function testEmergencyWithdraw_Success_WhenCalledByGov() public { vm.prank(chair); fed.expansion(1000 ether); @@ -280,6 +290,14 @@ contract AuraFedTest is Test{ assertEq(fed.claimsSupply(), 0); } + function testEmergencyWithdraw_Fails_WhenCalledByNonGov(address caller) public { + vm.prank(chair); + fed.expansion(1000 ether); + vm.prank(caller); + vm.expectRevert("NOT GOV"); + fed.emergencyWithdraw(); + } + function testContraction_FailWithOnlyChair_whenCalledByOtherAddress() public { vm.prank(gov); vm.expectRevert("ONLY CHAIR"); From e7b4769fef417bee33042a130d44205b3381c242 Mon Sep 17 00:00:00 2001 From: 08xmt Date: Thu, 8 Jun 2023 14:04:01 +0200 Subject: [PATCH 6/6] Fix vm.assume in setMigrator test --- test/AuraFed.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/AuraFed.t.sol b/test/AuraFed.t.sol index 3794bd1..f12981a 100644 --- a/test/AuraFed.t.sol +++ b/test/AuraFed.t.sol @@ -270,7 +270,7 @@ contract AuraFedTest is Test{ } function testSetMigrator_Fails_WhenCalledByNonGov(address caller) public { - vm.assume(caller != migrator); + vm.assume(caller != gov); vm.prank(chair); fed.expansion(1000 ether); vm.prank(caller);