Skip to content

Commit

Permalink
Merge branch 'master' into add-coverage-back
Browse files Browse the repository at this point in the history
  • Loading branch information
lucas-manuel authored Jul 4, 2024
2 parents 54ca402 + ebef16e commit 747eb61
Show file tree
Hide file tree
Showing 16 changed files with 246 additions and 79 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
[submodule "lib/erc20-helpers"]
path = lib/erc20-helpers
url = https://github.com/marsfoundation/erc20-helpers
[submodule "lib/xchain-dsr-oracle"]
path = lib/xchain-dsr-oracle
url = https://github.com/marsfoundation/xchain-dsr-oracle
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ runs = 1000
[invariant]
runs = 20
depth = 1000
shrink_run_limit = 0
shrink_run_limit = 1000

[profile.pr.invariant]
runs = 200
Expand Down
1 change: 1 addition & 0 deletions lib/xchain-dsr-oracle
Submodule xchain-dsr-oracle added at a02e59
7 changes: 2 additions & 5 deletions src/PSM3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ import { IERC20 } from "erc20-helpers/interfaces/IERC20.sol";

import { SafeERC20 } from "erc20-helpers/SafeERC20.sol";

import { IPSM3 } from "src/interfaces/IPSM3.sol";

interface IRateProviderLike {
function getConversionRate() external view returns (uint256);
}
import { IPSM3 } from "src/interfaces/IPSM3.sol";
import { IRateProviderLike } from "src/interfaces/IRateProviderLike.sol";

contract PSM3 is IPSM3 {

Expand Down
6 changes: 6 additions & 0 deletions src/interfaces/IRateProviderLike.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.13;

interface IRateProviderLike {
function getConversionRate() external view returns (uint256);
}
12 changes: 9 additions & 3 deletions test/PSMTestBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import "forge-std/Test.sol";

import { PSM3 } from "src/PSM3.sol";

import { IRateProviderLike } from "src/interfaces/IRateProviderLike.sol";

import { MockERC20 } from "erc20-helpers/MockERC20.sol";

import { MockRateProvider } from "test/mocks/MockRateProvider.sol";
Expand All @@ -18,7 +20,9 @@ contract PSMTestBase is Test {
MockERC20 public usdc;
MockERC20 public sDai;

MockRateProvider public rateProvider;
IRateProviderLike public rateProvider; // Can be overridden by dsrOracle using same interface

MockRateProvider public mockRateProvider; // Interface used for mocking

modifier assertAtomicPsmValueDoesNotChange {
uint256 beforeValue = _getPsmValue();
Expand All @@ -36,10 +40,12 @@ contract PSMTestBase is Test {
usdc = new MockERC20("usdc", "usdc", 6);
sDai = new MockERC20("sDai", "sDai", 18);

rateProvider = new MockRateProvider();
mockRateProvider = new MockRateProvider();

// NOTE: Using 1.25 for easy two way conversions
rateProvider.__setConversionRate(1.25e27);
mockRateProvider.__setConversionRate(1.25e27);

rateProvider = IRateProviderLike(address(mockRateProvider));

psm = new PSM3(address(dai), address(usdc), address(sDai), address(rateProvider));

Expand Down
130 changes: 119 additions & 11 deletions test/invariant/Invariants.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,27 @@ pragma solidity ^0.8.13;

import "forge-std/Test.sol";

import { DSRAuthOracle } from "lib/xchain-dsr-oracle/src/DSRAuthOracle.sol";

import { PSM3 } from "src/PSM3.sol";

import { IRateProviderLike } from "src/interfaces/IRateProviderLike.sol";

import { PSMTestBase } from "test/PSMTestBase.sol";

import { LpHandler } from "test/invariant/handlers/LpHandler.sol";
import { RateSetterHandler } from "test/invariant/handlers/RateSetterHandler.sol";
import { SwapperHandler } from "test/invariant/handlers/SwapperHandler.sol";
import { TransferHandler } from "test/invariant/handlers/TransferHandler.sol";
import { LpHandler } from "test/invariant/handlers/LpHandler.sol";
import { RateSetterHandler } from "test/invariant/handlers/RateSetterHandler.sol";
import { SwapperHandler } from "test/invariant/handlers/SwapperHandler.sol";
import { TimeBasedRateHandler } from "test/invariant/handlers/TimeBasedRateHandler.sol";
import { TransferHandler } from "test/invariant/handlers/TransferHandler.sol";

abstract contract PSMInvariantTestBase is PSMTestBase {

LpHandler public lpHandler;
RateSetterHandler public rateSetterHandler;
SwapperHandler public swapperHandler;
TransferHandler public transferHandler;
LpHandler public lpHandler;
RateSetterHandler public rateSetterHandler;
SwapperHandler public swapperHandler;
TransferHandler public transferHandler;
TimeBasedRateHandler public timeBasedRateHandler;

address BURN_ADDRESS = makeAddr("burn-address");

Expand Down Expand Up @@ -47,7 +55,7 @@ abstract contract PSMInvariantTestBase is PSMTestBase {
assertApproxEqAbs(
psm.getPsmTotalValue(),
psm.convertToAssetValue(psm.totalShares()),
3
4
);
}

Expand Down Expand Up @@ -260,7 +268,7 @@ contract PSMInvariants_RateSetting_NoTransfer is PSMInvariantTestBase {
super.setUp();

lpHandler = new LpHandler(psm, dai, usdc, sDai, 3);
rateSetterHandler = new RateSetterHandler(rateProvider, 1.25e27);
rateSetterHandler = new RateSetterHandler(address(rateProvider), 1.25e27);
swapperHandler = new SwapperHandler(psm, dai, usdc, sDai, 3);

targetContract(address(lpHandler));
Expand Down Expand Up @@ -292,7 +300,7 @@ contract PSMInvariants_RateSetting_WithTransfers is PSMInvariantTestBase {
super.setUp();

lpHandler = new LpHandler(psm, dai, usdc, sDai, 3);
rateSetterHandler = new RateSetterHandler(rateProvider, 1.25e27);
rateSetterHandler = new RateSetterHandler(address(rateProvider), 1.25e27);
swapperHandler = new SwapperHandler(psm, dai, usdc, sDai, 3);
transferHandler = new TransferHandler(psm, dai, usdc, sDai);

Expand All @@ -319,3 +327,103 @@ contract PSMInvariants_RateSetting_WithTransfers is PSMInvariantTestBase {
}

}

contract PSMInvariants_TimeBasedRateSetting_NoTransfer is PSMInvariantTestBase {

function setUp() public override {
super.setUp();

DSRAuthOracle dsrOracle = new DSRAuthOracle();

// Redeploy PSM with new rate provider
psm = new PSM3(address(dai), address(usdc), address(sDai), address(dsrOracle));

// Seed the new PSM with 1e18 shares (1e18 of value)
_deposit(address(dai), BURN_ADDRESS, 1e18);

lpHandler = new LpHandler(psm, dai, usdc, sDai, 3);
swapperHandler = new SwapperHandler(psm, dai, usdc, sDai, 3);
timeBasedRateHandler = new TimeBasedRateHandler(dsrOracle);

// Handler acts in the same way as a receiver on L2, so add as a data provider to the
// oracle.
dsrOracle.grantRole(dsrOracle.DATA_PROVIDER_ROLE(), address(timeBasedRateHandler));

rateProvider = IRateProviderLike(address(dsrOracle));

// Manually set initial values for the oracle through the handler to start
timeBasedRateHandler.setPotData(1e27, block.timestamp);

targetContract(address(lpHandler));
targetContract(address(swapperHandler));
targetContract(address(timeBasedRateHandler));
}

function invariant_A() public view {
_checkInvariant_A();
}

function invariant_B() public view {
_checkInvariant_B();
}

function invariant_C() public view {
_checkInvariant_C();
}

function afterInvariant() public {
_withdrawAllPositions();
}

}

contract PSMInvariants_TimeBasedRateSetting_WithTransfers is PSMInvariantTestBase {

function setUp() public override {
super.setUp();

DSRAuthOracle dsrOracle = new DSRAuthOracle();

// Redeploy PSM with new rate provider
psm = new PSM3(address(dai), address(usdc), address(sDai), address(dsrOracle));

// Seed the new PSM with 1e18 shares (1e18 of value)
_deposit(address(dai), BURN_ADDRESS, 1e18);

lpHandler = new LpHandler(psm, dai, usdc, sDai, 3);
swapperHandler = new SwapperHandler(psm, dai, usdc, sDai, 3);
timeBasedRateHandler = new TimeBasedRateHandler(dsrOracle);
transferHandler = new TransferHandler(psm, dai, usdc, sDai);

// Handler acts in the same way as a receiver on L2, so add as a data provider to the
// oracle.
dsrOracle.grantRole(dsrOracle.DATA_PROVIDER_ROLE(), address(timeBasedRateHandler));

rateProvider = IRateProviderLike(address(dsrOracle));

// Manually set initial values for the oracle through the handler to start
timeBasedRateHandler.setPotData(1e27, block.timestamp);

targetContract(address(lpHandler));
targetContract(address(swapperHandler));
targetContract(address(timeBasedRateHandler));
targetContract(address(transferHandler));
}

function invariant_A() public view {
_checkInvariant_A();
}

function invariant_B() public view {
_checkInvariant_B();
}

function invariant_C() public view {
_checkInvariant_C();
}

function afterInvariant() public {
_withdrawAllPositions();
}

}
5 changes: 2 additions & 3 deletions test/invariant/handlers/RateSetterHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ contract RateSetterHandler is StdUtils {

uint256 public setRateCount;

constructor(MockRateProvider rateProvider_, uint256 initialRate) {
rateProvider = rateProvider_;
constructor(address rateProvider_, uint256 initialRate) {
rateProvider = MockRateProvider(rateProvider_);
rate = initialRate;
}

Expand All @@ -26,5 +26,4 @@ contract RateSetterHandler is StdUtils {

setRateCount++;
}

}
47 changes: 47 additions & 0 deletions test/invariant/handlers/TimeBasedRateHandler.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.13;

import { StdCheats } from "forge-std/StdCheats.sol";
import { StdUtils } from "forge-std/StdUtils.sol";

import { DSRAuthOracle } from "lib/xchain-dsr-oracle/src/DSRAuthOracle.sol";
import { IDSROracle } from "lib/xchain-dsr-oracle/src/interfaces/IDSROracle.sol";

contract TimeBasedRateHandler is StdCheats, StdUtils {

uint256 public dsr;
uint256 public chi;
uint256 public rho;

uint256 constant ONE_HUNDRED_PCT_APY_DSR = 1.000000021979553151239153027e27;

DSRAuthOracle public dsrOracle;

uint256 public setRateCount;

constructor(DSRAuthOracle dsrOracle_) {
dsrOracle = dsrOracle_;
}

// This acts as a receiver on an L2.
function setPotData(uint256 newDsr, uint256 newRho) external {
dsr = _bound(newDsr, 1e27, ONE_HUNDRED_PCT_APY_DSR);
rho = _bound(newRho, rho, block.timestamp);

// If chi hasn't been set yet, set to 1e27, else recalculate it in the same way it would
// happen during a refresh.
uint256 rate = dsrOracle.getConversionRate();
uint256 chi = rate == 0 ? 1e27 : rate;

dsrOracle.setPotData(IDSROracle.PotData({
dsr: uint96(dsr),
chi: uint120(chi),
rho: uint40(rho)
}));
}

function warp(uint256 skipTime) external {
skip(_bound(skipTime, 0, 45 days));
}

}
Loading

0 comments on commit 747eb61

Please sign in to comment.