-
Notifications
You must be signed in to change notification settings - Fork 11
Vip 7 Dai PSM #97
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
Merged
Merged
Vip 7 Dai PSM #97
Changes from 2 commits
Commits
Show all changes
57 commits
Select commit
Hold shift + click to select a range
b8858aa
setup dai psm integration tests
k-xo 13c44c0
change test from fuzz test to normal test
k-xo e9d7f96
setup vip skeleton
k-xo 887bca5
simplify test
k-xo 85e8109
remove comment
k-xo 2ac0efc
fix failing test
k-xo cb3a614
add action to unpause redemptions on usdc psm
k-xo ce59749
remove mockpcvdeposit
k-xo 42fd1a7
add dai pricebound psm deploy script
k-xo 164751d
use address one instead of address zero as surplus target
k-xo a68ce9c
use vip framework for proposal
k-xo 8544bb8
delete unused deploy script
k-xo e5806b6
grantPCVController to daiPriceBoundPSM
k-xo 0a71534
Merge branch 'develop' into feat/vip-6-dai-psm
k-xo b8dd469
remove dai psm as pcv controller
k-xo 0957793
remove setup
k-xo 70e7e73
setup maker router
k-xo cc4b4a4
rename to vip 7
k-xo 3813b94
add helper contract for withdrawing tokens
k-xo 4789e91
remove unnecessary variables
k-xo cd92038
fix deployment parameters
k-xo 52a1e39
address feedback comments
k-xo 726f499
validate PSM parameters
k-xo 04dd4da
add natspec & deployment validation
k-xo ecb20dd
add fuzz test
k-xo 17459bd
add function to swap total balances
k-xo e7e7249
convert test to fuzz test
k-xo ac8c097
natspec fixes
k-xo 2d087c1
address feedback comments
k-xo 82524e2
expand fuzz test space
k-xo e5a0d77
Merge branch 'develop' into feat/vip-6-dai-psm
k-xo 1452cc2
remove test for l2 deprecated oracle
k-xo 6fbe7bc
review changes
k-xo b030692
add checks and withdraw function
k-xo f71d971
fix failing test
k-xo 46c0f9b
fix reserves threshold amount
k-xo 2ca4182
transfer volt to psm in setup
k-xo 138bf5f
use constants for token addresses
k-xo c56c254
withdraw & transfer volt in pre-proposal action
k-xo c6cde2f
Deploy Dai PSM and Maker Router
ElliotFriedman 4690df2
Merge branch 'develop' into feat/vip-6-dai-psm
k-xo e6d054f
remove unused imports
k-xo d8fcd3c
point to oracle pass through
k-xo b9708c7
bump container size to xlarge
ElliotFriedman 98ec10e
set oracle pass through on dai psm
k-xo f66c23b
simulate proposal
k-xo 70a5a92
natspec fixes
k-xo 671ffe6
fix failing test
k-xo eed08f4
external imports at top
k-xo 5a56498
add simulation to psm, add tests for router
k-xo 878d906
tidy imports
k-xo 69719ae
remove console.log
k-xo 33d3667
fix failing role integration test
ElliotFriedman 7f3f093
remove maxmint test
k-xo 37bab1b
remove l2scaling price oracle address
k-xo 8b7e307
remove console.sol import
k-xo 33c80fe
increase fuzz runs, remove unused variables
k-xo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
340 changes: 340 additions & 0 deletions
340
contracts/test/integration/IntegrationTestPriceBoundPSMDai.t.sol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,340 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
pragma solidity ^0.8.4; | ||
|
||
import {ERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; | ||
import {MockPCVDepositV2} from "../../mock/MockPCVDepositV2.sol"; | ||
import {IPCVDeposit} from "../../pcv/IPCVDeposit.sol"; | ||
import {MockERC20} from "../../mock/MockERC20.sol"; | ||
import {OraclePassThrough} from "../../oracle/OraclePassThrough.sol"; | ||
import {ScalingPriceOracle} from "../../oracle/ScalingPriceOracle.sol"; | ||
import {MockScalingPriceOracle} from "../../mock/MockScalingPriceOracle.sol"; | ||
import {ICore} from "../../core/ICore.sol"; | ||
import {Core} from "../../core/Core.sol"; | ||
import {IVolt, Volt} from "../../volt/Volt.sol"; | ||
import {PriceBoundPSM, PegStabilityModule} from "../../peg/PriceBoundPSM.sol"; | ||
import {getCore, getMainnetAddresses, VoltTestAddresses} from "../unit/utils/Fixtures.sol"; | ||
import {ERC20CompoundPCVDeposit} from "../../pcv/compound/ERC20CompoundPCVDeposit.sol"; | ||
import {Vm} from "./../unit/utils/Vm.sol"; | ||
import {DSTest} from "./../unit/utils/DSTest.sol"; | ||
import {MainnetAddresses} from "./fixtures/MainnetAddresses.sol"; | ||
import {Constants} from "../../Constants.sol"; | ||
|
||
contract IntegrationTestPriceBoundPSMDaiTest is DSTest { | ||
using SafeCast for *; | ||
PriceBoundPSM private psm; | ||
ICore private core = ICore(MainnetAddresses.CORE); | ||
IVolt private volt = IVolt(MainnetAddresses.VOLT); | ||
IVolt private dai = IVolt(MainnetAddresses.DAI); | ||
IVolt private underlyingToken = dai; | ||
|
||
MockPCVDepositV2 public pcvDeposit; | ||
|
||
/// ------------ Minting and RateLimited System Params ------------ | ||
|
||
uint256 public constant mintAmount = 10_000_000e18; | ||
uint256 public constant mintAmountPSM = 10_000_000e18; | ||
uint256 public constant bufferCap = 10_000_000e18; | ||
uint256 public constant individualMaxBufferCap = 5_000_000e18; | ||
uint256 public constant rps = 10_000e18; | ||
k-xo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// ------------ Oracle System Params ------------ | ||
|
||
/// @notice prices during test will increase 1% monthly | ||
int256 public constant monthlyChangeRateBasisPoints = 100; | ||
uint256 public constant maxDeviationThresholdBasisPoints = 1_000; | ||
|
||
/// @notice Oracle Pass Through contract | ||
OraclePassThrough public oracle = | ||
OraclePassThrough(MainnetAddresses.ORACLE_PASS_THROUGH); | ||
|
||
Vm public constant vm = Vm(HEVM_ADDRESS); | ||
|
||
uint256 voltFloorPrice = 9_000; /// 1 volt for .9 dai is the max allowable price | ||
uint256 voltCeilingPrice = 10_000; /// 1 volt for 1 dai is the minimum price | ||
|
||
function setUp() public { | ||
PegStabilityModule.OracleParams memory oracleParams; | ||
|
||
pcvDeposit = new MockPCVDepositV2( | ||
k-xo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
address(core), | ||
address(underlyingToken), | ||
0, | ||
0 | ||
); | ||
|
||
oracleParams = PegStabilityModule.OracleParams({ | ||
coreAddress: address(core), | ||
oracleAddress: address(oracle), | ||
backupOracle: address(0), | ||
decimalsNormalizer: 0, | ||
doInvert: true | ||
}); | ||
|
||
/// create PSM | ||
psm = new PriceBoundPSM( | ||
k-xo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
voltFloorPrice, | ||
voltCeilingPrice, | ||
oracleParams, | ||
0, | ||
0, | ||
10_000_000_000e18, | ||
k-xo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
10_000e18, | ||
10_000_000e18, | ||
IERC20(address(dai)), | ||
pcvDeposit | ||
); | ||
|
||
vm.startPrank(MainnetAddresses.DAI_USDC_USDT_CURVE_POOL); | ||
dai.transfer(address(this), mintAmount); | ||
dai.transfer(address(psm), mintAmount * 2); | ||
vm.stopPrank(); | ||
|
||
vm.startPrank(MainnetAddresses.GOVERNOR); | ||
|
||
/// grant the PSM the PCV Controller role | ||
k-xo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
core.grantMinter(MainnetAddresses.GOVERNOR); | ||
ElliotFriedman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// mint VOLT to the user | ||
volt.mint(address(psm), mintAmount); | ||
volt.mint(address(this), mintAmount); | ||
vm.stopPrank(); | ||
} | ||
|
||
/// @notice PSM inverts price | ||
function testDoInvert() public { | ||
assertTrue(psm.doInvert()); | ||
} | ||
|
||
/// @notice PSM is set up correctly and view functions are working | ||
function testGetRedeemAmountOut(uint128 amountVoltIn) public { | ||
uint256 currentPegPrice = oracle.getCurrentOraclePrice(); | ||
|
||
uint256 amountOut = (amountVoltIn * currentPegPrice) / 1e18; | ||
|
||
assertApproxEq( | ||
psm.getRedeemAmountOut(amountVoltIn).toInt256(), | ||
amountOut.toInt256(), | ||
0 | ||
); | ||
} | ||
|
||
/// @notice PSM is set up correctly and view functions are working | ||
function testGetMaxMintAmountOut() public { | ||
uint256 startingBalance = volt.balanceOf(address(psm)); | ||
assertEq(psm.getMaxMintAmountOut(), bufferCap + startingBalance); | ||
|
||
vm.startPrank(MainnetAddresses.GOVERNOR); | ||
ElliotFriedman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
volt.mint(address(psm), mintAmount); | ||
vm.stopPrank(); | ||
|
||
assertEq( | ||
psm.getMaxMintAmountOut(), | ||
bufferCap + mintAmount + startingBalance | ||
); | ||
} | ||
|
||
/// @notice PSM is set up correctly and view functions are working | ||
function testGetMintAmountOut() public { | ||
k-xo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
uint256 currentPegPrice = oracle.getCurrentOraclePrice(); | ||
|
||
uint256 amountOut = (mintAmount * 1e18) / currentPegPrice; | ||
|
||
assertApproxEq( | ||
psm.getMintAmountOut(mintAmount).toInt256(), | ||
amountOut.toInt256(), | ||
0 | ||
); | ||
} | ||
|
||
/// @notice pcv deposit receives underlying token on mint | ||
function testSwapUnderlyingForVoltAfterPriceIncrease() public { | ||
uint256 amountStableIn = 101_000; | ||
uint256 amountVoltOut = psm.getMintAmountOut(amountStableIn); | ||
|
||
underlyingToken.approve(address(psm), amountStableIn); | ||
psm.mint(address(this), amountStableIn, amountVoltOut); | ||
|
||
uint256 endingUserVoltBalance = volt.balanceOf(address(this)); | ||
uint256 endingPSMUnderlyingBalance = underlyingToken.balanceOf( | ||
address(psm) | ||
); | ||
|
||
assertEq(endingPSMUnderlyingBalance, amountStableIn + mintAmount * 2); | ||
assertEq(endingUserVoltBalance, mintAmount + amountVoltOut); | ||
} | ||
|
||
/// @notice pcv deposit receives underlying token on mint | ||
function testSwapUnderlyingForVolt() public { | ||
uint256 userStartingVoltBalance = volt.balanceOf(address(this)); | ||
uint256 minAmountOut = psm.getMintAmountOut(mintAmount); | ||
|
||
underlyingToken.approve(address(psm), mintAmount); | ||
uint256 amountVoltOut = psm.mint( | ||
address(this), | ||
mintAmount, | ||
minAmountOut | ||
); | ||
|
||
uint256 endingUserVOLTBalance = volt.balanceOf(address(this)); | ||
uint256 endingPSMUnderlyingBalance = underlyingToken.balanceOf( | ||
address(psm) | ||
); | ||
|
||
assertEq( | ||
endingUserVOLTBalance, | ||
amountVoltOut + userStartingVoltBalance | ||
); | ||
assertApproxEq( | ||
k-xo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
endingPSMUnderlyingBalance.toInt256(), | ||
(mintAmount + mintAmount * 2).toInt256(), | ||
0 | ||
); /// allow 1 basis point of error | ||
} | ||
|
||
/// @notice pcv deposit gets depleted on redeem | ||
function testSwapVoltForUnderlying() public { | ||
uint256 startingUserUnderlyingBalance = underlyingToken.balanceOf( | ||
address(this) | ||
); | ||
volt.approve(address(psm), mintAmount); | ||
uint256 amountOut = psm.redeem(address(this), mintAmount, mintAmount); | ||
|
||
uint256 endingUserVOLTBalance = volt.balanceOf(address(this)); | ||
uint256 endingUserUnderlyingBalance = underlyingToken.balanceOf( | ||
address(this) | ||
); | ||
uint256 endingPSMUnderlyingBalance = underlyingToken.balanceOf( | ||
address(psm) | ||
); | ||
|
||
assertEq(endingPSMUnderlyingBalance, mintAmount * 2 - amountOut); | ||
assertEq(endingUserVOLTBalance, 0); | ||
assertEq( | ||
endingUserUnderlyingBalance, | ||
startingUserUnderlyingBalance + amountOut | ||
); | ||
} | ||
|
||
/// @notice redeem fails without approval | ||
function testSwapVoltForUnderlyingFailsWithoutApproval() public { | ||
vm.expectRevert(bytes("ERC20: transfer amount exceeds allowance")); | ||
|
||
psm.redeem(address(this), mintAmount, mintAmount); | ||
} | ||
|
||
/// @notice mint fails without approval | ||
function testSwapUnderlyingForVoltFailsWithoutApproval() public { | ||
vm.expectRevert(bytes("Dai/insufficient-allowance")); | ||
|
||
psm.mint(address(this), mintAmount, 0); | ||
} | ||
|
||
/// @notice withdraw erc20 fails without correct permissions | ||
function testERC20WithdrawFailure() public { | ||
vm.expectRevert(bytes("CoreRef: Caller is not a PCV controller")); | ||
|
||
psm.withdrawERC20(address(underlyingToken), address(this), 100); | ||
} | ||
|
||
/// @notice withdraw erc20 succeeds with correct permissions | ||
function testERC20WithdrawSuccess() public { | ||
vm.prank(MainnetAddresses.GOVERNOR); | ||
core.grantPCVController(address(this)); | ||
|
||
uint256 startingBalance = underlyingToken.balanceOf(address(this)); | ||
psm.withdrawERC20(address(underlyingToken), address(this), mintAmount); | ||
uint256 endingBalance = underlyingToken.balanceOf(address(this)); | ||
|
||
assertEq(endingBalance - startingBalance, mintAmount); | ||
} | ||
|
||
/// @notice set global rate limited minter fails when caller is governor and new address is 0 | ||
function testSetPCVDepositFailureZeroAddress() public { | ||
vm.startPrank(MainnetAddresses.GOVERNOR); | ||
|
||
vm.expectRevert( | ||
bytes("PegStabilityModule: Invalid new surplus target") | ||
); | ||
psm.setSurplusTarget(IPCVDeposit(address(0))); | ||
|
||
vm.stopPrank(); | ||
} | ||
|
||
/// @notice set PCV deposit fails when caller is governor and new address is 0 | ||
function testSetPCVDepositFailureNonGovernor() public { | ||
vm.expectRevert( | ||
bytes("CoreRef: Caller is not a governor or contract admin") | ||
); | ||
psm.setSurplusTarget(IPCVDeposit(address(0))); | ||
} | ||
|
||
/// @notice set PCV Deposit succeeds when caller is governor and underlying tokens match | ||
function testSetPCVDepositSuccess() public { | ||
vm.startPrank(MainnetAddresses.GOVERNOR); | ||
|
||
MockPCVDepositV2 newPCVDeposit = new MockPCVDepositV2( | ||
address(core), | ||
address(underlyingToken), | ||
0, | ||
0 | ||
); | ||
|
||
psm.setSurplusTarget(IPCVDeposit(address(newPCVDeposit))); | ||
|
||
vm.stopPrank(); | ||
|
||
assertEq(address(newPCVDeposit), address(psm.surplusTarget())); | ||
} | ||
|
||
/// @notice set mint fee succeeds | ||
function testSetMintFeeSuccess() public { | ||
vm.prank(MainnetAddresses.GOVERNOR); | ||
psm.setMintFee(100); | ||
|
||
assertEq(psm.mintFeeBasisPoints(), 100); | ||
} | ||
|
||
/// @notice set mint fee fails unauthorized | ||
function testSetMintFeeFailsWithoutCorrectRoles() public { | ||
vm.expectRevert( | ||
bytes("CoreRef: Caller is not a governor or contract admin") | ||
); | ||
|
||
psm.setMintFee(100); | ||
} | ||
|
||
/// @notice set redeem fee succeeds | ||
function testSetRedeemFeeSuccess() public { | ||
vm.prank(MainnetAddresses.GOVERNOR); | ||
psm.setRedeemFee(100); | ||
|
||
assertEq(psm.redeemFeeBasisPoints(), 100); | ||
} | ||
|
||
/// @notice set redeem fee fails unauthorized | ||
function testSetRedeemFeeFailsWithoutCorrectRoles() public { | ||
vm.expectRevert( | ||
bytes("CoreRef: Caller is not a governor or contract admin") | ||
); | ||
|
||
psm.setRedeemFee(100); | ||
} | ||
|
||
/// @notice redeem fails when paused | ||
function testRedeemFailsWhenPaused() public { | ||
vm.prank(MainnetAddresses.GOVERNOR); | ||
psm.pauseRedeem(); | ||
|
||
vm.expectRevert(bytes("PegStabilityModule: Redeem paused")); | ||
psm.redeem(address(this), 100, 100); | ||
} | ||
|
||
/// @notice mint fails when paused | ||
function testMintFailsWhenPaused() public { | ||
vm.prank(MainnetAddresses.GOVERNOR); | ||
psm.pauseMint(); | ||
|
||
vm.expectRevert(bytes("PegStabilityModule: Minting paused")); | ||
psm.mint(address(this), 100, 100); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.