Skip to content

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 57 commits into from
Aug 24, 2022
Merged
Show file tree
Hide file tree
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 Aug 4, 2022
13c44c0
change test from fuzz test to normal test
k-xo Aug 4, 2022
e9d7f96
setup vip skeleton
k-xo Aug 4, 2022
887bca5
simplify test
k-xo Aug 4, 2022
85e8109
remove comment
k-xo Aug 4, 2022
2ac0efc
fix failing test
k-xo Aug 4, 2022
cb3a614
add action to unpause redemptions on usdc psm
k-xo Aug 4, 2022
ce59749
remove mockpcvdeposit
k-xo Aug 4, 2022
42fd1a7
add dai pricebound psm deploy script
k-xo Aug 4, 2022
164751d
use address one instead of address zero as surplus target
k-xo Aug 5, 2022
a68ce9c
use vip framework for proposal
k-xo Aug 5, 2022
8544bb8
delete unused deploy script
k-xo Aug 5, 2022
e5806b6
grantPCVController to daiPriceBoundPSM
k-xo Aug 5, 2022
0a71534
Merge branch 'develop' into feat/vip-6-dai-psm
k-xo Aug 5, 2022
b8dd469
remove dai psm as pcv controller
k-xo Aug 5, 2022
0957793
remove setup
k-xo Aug 6, 2022
70e7e73
setup maker router
k-xo Aug 10, 2022
cc4b4a4
rename to vip 7
k-xo Aug 10, 2022
3813b94
add helper contract for withdrawing tokens
k-xo Aug 10, 2022
4789e91
remove unnecessary variables
k-xo Aug 10, 2022
cd92038
fix deployment parameters
k-xo Aug 10, 2022
52a1e39
address feedback comments
k-xo Aug 10, 2022
726f499
validate PSM parameters
k-xo Aug 10, 2022
04dd4da
add natspec & deployment validation
k-xo Aug 11, 2022
ecb20dd
add fuzz test
k-xo Aug 11, 2022
17459bd
add function to swap total balances
k-xo Aug 11, 2022
e7e7249
convert test to fuzz test
k-xo Aug 11, 2022
ac8c097
natspec fixes
k-xo Aug 11, 2022
2d087c1
address feedback comments
k-xo Aug 12, 2022
82524e2
expand fuzz test space
k-xo Aug 12, 2022
e5a0d77
Merge branch 'develop' into feat/vip-6-dai-psm
k-xo Aug 12, 2022
1452cc2
remove test for l2 deprecated oracle
k-xo Aug 12, 2022
6fbe7bc
review changes
k-xo Aug 13, 2022
b030692
add checks and withdraw function
k-xo Aug 13, 2022
f71d971
fix failing test
k-xo Aug 13, 2022
46c0f9b
fix reserves threshold amount
k-xo Aug 15, 2022
2ca4182
transfer volt to psm in setup
k-xo Aug 15, 2022
138bf5f
use constants for token addresses
k-xo Aug 15, 2022
c56c254
withdraw & transfer volt in pre-proposal action
k-xo Aug 16, 2022
c6cde2f
Deploy Dai PSM and Maker Router
ElliotFriedman Aug 17, 2022
4690df2
Merge branch 'develop' into feat/vip-6-dai-psm
k-xo Aug 17, 2022
e6d054f
remove unused imports
k-xo Aug 17, 2022
d8fcd3c
point to oracle pass through
k-xo Aug 17, 2022
b9708c7
bump container size to xlarge
ElliotFriedman Aug 17, 2022
98ec10e
set oracle pass through on dai psm
k-xo Aug 17, 2022
f66c23b
simulate proposal
k-xo Aug 17, 2022
70a5a92
natspec fixes
k-xo Aug 22, 2022
671ffe6
fix failing test
k-xo Aug 22, 2022
eed08f4
external imports at top
k-xo Aug 23, 2022
5a56498
add simulation to psm, add tests for router
k-xo Aug 23, 2022
878d906
tidy imports
k-xo Aug 23, 2022
69719ae
remove console.log
k-xo Aug 23, 2022
33d3667
fix failing role integration test
ElliotFriedman Aug 24, 2022
7f3f093
remove maxmint test
k-xo Aug 24, 2022
37bab1b
remove l2scaling price oracle address
k-xo Aug 24, 2022
8b7e307
remove console.sol import
k-xo Aug 24, 2022
33c80fe
increase fuzz runs, remove unused variables
k-xo Aug 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
340 changes: 340 additions & 0 deletions contracts/test/integration/IntegrationTestPriceBoundPSMDai.t.sol
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;

/// ------------ 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(
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(
voltFloorPrice,
voltCeilingPrice,
oracleParams,
0,
0,
10_000_000_000e18,
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
core.grantMinter(MainnetAddresses.GOVERNOR);
/// 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);
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 {
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(
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);
}
}
7 changes: 7 additions & 0 deletions contracts/test/integration/fixtures/MainnetAddresses.sol
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ library MainnetAddresses {
0xAe2D4617c862309A3d75A0fFB358c7a5009c673F;
address public constant FEI = 0x956F47F50A910163D8BF957Cf5846D573E7f87CA;

address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;

// ---------- CHAINLINK ADDRESSES ----------

address public constant CHAINLINK_ORACLE_ADDRESS =
Expand All @@ -89,4 +91,9 @@ library MainnetAddresses {

address public constant RARI_VOLT_PCV_DEPOSIT =
0xFeBDf448C8484834bb399d930d7E1bdC773E23bA;

// ---------- CURVE ADDRESSES ----------

address public constant DAI_USDC_USDT_CURVE_POOL =
0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7;
}