From 474fd4680299a1cba5b04c893dc400ff61b4d3ad Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Mon, 8 Jul 2024 15:12:59 +0200 Subject: [PATCH 1/7] feat: add first tests --- test/unit/PreviewDeposit.t.sol | 88 +++++++++++ test/unit/SwapPreviews.t.sol | 275 +++++++++++++++++++++++++++++++++ 2 files changed, 363 insertions(+) create mode 100644 test/unit/PreviewDeposit.t.sol create mode 100644 test/unit/SwapPreviews.t.sol diff --git a/test/unit/PreviewDeposit.t.sol b/test/unit/PreviewDeposit.t.sol new file mode 100644 index 0000000..1251a67 --- /dev/null +++ b/test/unit/PreviewDeposit.t.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; + +import { MockRateProvider, PSMTestBase } from "test/PSMTestBase.sol"; + +contract PSMPreviewDeposit_FailureTests is PSMTestBase { + + function test_previewDeposit_invalidAsset() public { + vm.expectRevert("PSM3/invalid-asset"); + psm.previewDeposit(makeAddr("other-token"), 1); + } + +} + +contract PSMPreviewDeposit_SuccessTests is PSMTestBase { + + address depositor = makeAddr("depositor"); + + function test_previewDeposit_usdc_firstDeposit() public view { + assertEq(psm.previewDeposit(address(usdc), 1), 1e12); + assertEq(psm.previewDeposit(address(usdc), 2), 2e12); + assertEq(psm.previewDeposit(address(usdc), 3), 3e12); + + assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + assertEq(psm.previewDeposit(address(usdc), 2e6), 2e18); + assertEq(psm.previewDeposit(address(usdc), 3e6), 3e18); + } + + function testFuzz_previewDeposit_usdc_firstDeposit(uint256 amount) public view { + amount = _bound(amount, 0, USDC_TOKEN_MAX); + assertEq(psm.previewDeposit(address(usdc), amount), amount * 1e12); + } + + function test_previewDeposit_usdc_notOneToOne() public { + assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + + _deposit(address(dai), depositor, 1e18); + assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + + _deposit(address(usdc), depositor, 1e6); + assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + + _deposit(address(sDai), depositor, 0.8e18); + assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + + mockRateProvider.__setConversionRate(2e27); + + // $300 dollars of value deposited, 300 shares minted. + // sDAI portion becomes worth $160, full pool worth $360, each share worth $1.20 + // 1 USDC = 1/1.20 = 0.833... + assertEq(psm.previewDeposit(address(usdc), 1e6), 0.833333333333333333e18); + } + + function test_previewDeposit_usdc_notOneToOne( + uint256 amount1, + uint256 amount2, + uint256 amount3, + uint256 conversionRate, + uint256 previewAmount + ) public { + amount1 = _bound(amount1, 1, DAI_TOKEN_MAX); + amount2 = _bound(amount2, 1, USDC_TOKEN_MAX); + amount3 = _bound(amount3, 1, SDAI_TOKEN_MAX); + conversionRate = _bound(conversionRate, 1.00e27, 1000e27); + previewAmount = _bound(previewAmount, 0, USDC_TOKEN_MAX); + + assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + + _deposit(address(dai), depositor, 1e18); + assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + + _deposit(address(usdc), depositor, 1e6); + assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + + _deposit(address(sDai), depositor, 0.8e18); + assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + + mockRateProvider.__setConversionRate(2e27); + + // $300 dollars of value deposited, 300 shares minted. + // sDAI portion becomes worth $160, full pool worth $360, each share worth $1.20 + // 1 USDC = 1/1.20 = 0.833... + assertEq(psm.previewDeposit(address(usdc), 1e6), 0.833333333333333333e18); + } + +} diff --git a/test/unit/SwapPreviews.t.sol b/test/unit/SwapPreviews.t.sol new file mode 100644 index 0000000..999e4ba --- /dev/null +++ b/test/unit/SwapPreviews.t.sol @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; + +import { MockRateProvider, PSMTestBase } from "test/PSMTestBase.sol"; + +contract PSMPreviewSwapExactIn_FailureTests is PSMTestBase { + + function test_previewSwapExactIn_invalidAssetIn() public { + vm.expectRevert("PSM3/invalid-asset"); + psm.previewSwapExactIn(makeAddr("other-token"), address(usdc), 1); + } + + function test_previewSwapExactIn_invalidAssetOut() public { + vm.expectRevert("PSM3/invalid-asset"); + psm.previewSwapExactIn(address(usdc), makeAddr("other-token"), 1); + } + + function test_previewSwapExactIn_bothAsset0() public { + vm.expectRevert("PSM3/invalid-asset"); + psm.previewSwapExactIn(address(dai), address(dai), 1); + } + + function test_previewSwapExactIn_bothAsset1() public { + vm.expectRevert("PSM3/invalid-asset"); + psm.previewSwapExactIn(address(usdc), address(usdc), 1); + } + + function test_previewSwapExactIn_bothAsset2() public { + vm.expectRevert("PSM3/invalid-asset"); + psm.previewSwapExactIn(address(sDai), address(sDai), 1); + } + +} + +contract PSMPreviewSwapExactOut_FailureTests is PSMTestBase { + + function test_previewSwapExactIn_invalidAssetIn() public { + vm.expectRevert("PSM3/invalid-asset"); + psm.previewSwapExactOut(makeAddr("other-token"), address(usdc), 1); + } + + function test_previewSwapExactOut_invalidAssetOut() public { + vm.expectRevert("PSM3/invalid-asset"); + psm.previewSwapExactOut(address(usdc), makeAddr("other-token"), 1); + } + + function test_previewSwapExactOut_bothAsset0() public { + vm.expectRevert("PSM3/invalid-asset"); + psm.previewSwapExactOut(address(dai), address(dai), 1); + } + + function test_previewSwapExactOut_bothAsset1() public { + vm.expectRevert("PSM3/invalid-asset"); + psm.previewSwapExactOut(address(usdc), address(usdc), 1); + } + + function test_previewSwapExactOut_bothAsset2() public { + vm.expectRevert("PSM3/invalid-asset"); + psm.previewSwapExactOut(address(sDai), address(sDai), 1); + } + +} + +contract PSMPreviewSwapExactIn_DaiAssetInTests is PSMTestBase { + + function test_previewSwapExactIn_daiToUsdc() public view { + assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 1e12 - 1), 0); + assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 1e12), 1); + + assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 1e18), 1e6); + assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 2e18), 2e6); + assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 3e18), 3e6); + } + + function testFuzz_previewSwapExactIn_daiToUsdc(uint256 amountIn) public view { + amountIn = _bound(amountIn, 0, DAI_TOKEN_MAX); + + assertEq(psm.previewSwapExactIn(address(dai), address(usdc), amountIn), amountIn / 1e12); + } + + function test_previewSwapExactIn_daiToSDai() public view { + assertEq(psm.previewSwapExactIn(address(dai), address(sDai), 1e18), 0.8e18); + assertEq(psm.previewSwapExactIn(address(dai), address(sDai), 2e18), 1.6e18); + assertEq(psm.previewSwapExactIn(address(dai), address(sDai), 3e18), 2.4e18); + } + + function testFuzz_previewSwapExactIn_daiToSDai(uint256 amountIn, uint256 conversionRate) public { + amountIn = _bound(amountIn, 1, DAI_TOKEN_MAX); + conversionRate = _bound(conversionRate, 0.0001e27, 1000e27); // 0.01% to 100,000% conversion rate + + mockRateProvider.__setConversionRate(conversionRate); + + uint256 amountOut = amountIn * 1e27 / conversionRate; + + assertEq(psm.previewSwapExactIn(address(dai), address(sDai), amountIn), amountOut); + } + +} + +contract PSMPreviewSwapExactOut_DaiAssetInTests is PSMTestBase { + + function test_previewSwapExactOut_daiToUsdc() public view { + assertEq(psm.previewSwapExactOut(address(dai), address(usdc), 1e6), 1e18); + assertEq(psm.previewSwapExactOut(address(dai), address(usdc), 2e6), 2e18); + assertEq(psm.previewSwapExactOut(address(dai), address(usdc), 3e6), 3e18); + } + + function testFuzz_previewSwapExactOut_daiToUsdc(uint256 amountOut) public view { + amountOut = _bound(amountOut, 0, USDC_TOKEN_MAX); + + assertEq(psm.previewSwapExactOut(address(dai), address(usdc), amountOut), amountOut * 1e12); + } + + function test_previewSwapExactOut_daiToSDai() public view { + assertEq(psm.previewSwapExactOut(address(dai), address(sDai), 0.8e18), 1e18); + assertEq(psm.previewSwapExactOut(address(dai), address(sDai), 1.6e18), 2e18); + assertEq(psm.previewSwapExactOut(address(dai), address(sDai), 2.4e18), 3e18); + } + + function testFuzz_previewSwapExactOut_daiToSDai(uint256 amountOut, uint256 conversionRate) public { + amountOut = _bound(amountOut, 1, USDC_TOKEN_MAX); + conversionRate = _bound(conversionRate, 0.0001e27, 1000e27); // 0.01% to 100,000% conversion rate + + mockRateProvider.__setConversionRate(conversionRate); + + uint256 amountIn = amountOut * conversionRate / 1e27; + + assertEq(psm.previewSwapExactOut(address(dai), address(sDai), amountOut), amountIn); + } + +} + +contract PSMPreviewSwapExactIn_USDCAssetInTests is PSMTestBase { + + function test_previewSwapExactIn_usdcToDai() public view { + assertEq(psm.previewSwapExactIn(address(usdc), address(dai), 1e6), 1e18); + assertEq(psm.previewSwapExactIn(address(usdc), address(dai), 2e6), 2e18); + assertEq(psm.previewSwapExactIn(address(usdc), address(dai), 3e6), 3e18); + } + + function testFuzz_previewSwapExactIn_usdcToDai(uint256 amountIn) public view { + amountIn = _bound(amountIn, 0, USDC_TOKEN_MAX); + + assertEq(psm.previewSwapExactIn(address(usdc), address(dai), amountIn), amountIn * 1e12); + } + + function test_previewSwapExactIn_usdcToSDai() public view { + assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), 1e6), 0.8e18); + assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), 2e6), 1.6e18); + assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), 3e6), 2.4e18); + } + + function testFuzz_previewSwapExactIn_usdcToSDai(uint256 amountIn, uint256 conversionRate) public { + amountIn = _bound(amountIn, 1, USDC_TOKEN_MAX); + conversionRate = _bound(conversionRate, 0.0001e27, 1000e27); // 0.01% to 100,000% conversion rate + + mockRateProvider.__setConversionRate(conversionRate); + + uint256 amountOut = amountIn * 1e27 / conversionRate * 1e12; + + assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), amountIn), amountOut); + } + +} + +contract PSMPreviewSwapExactOut_USDCAssetInTests is PSMTestBase { + + function test_previewSwapExactOut_usdcToDai() public view { + assertEq(psm.previewSwapExactOut(address(usdc), address(dai), 1e18), 1e6); + assertEq(psm.previewSwapExactOut(address(usdc), address(dai), 2e18), 2e6); + assertEq(psm.previewSwapExactOut(address(usdc), address(dai), 3e18), 3e6); + } + + function testFuzz_previewSwapExactOut_usdcToDai(uint256 amountOut) public view { + amountOut = _bound(amountOut, 0, DAI_TOKEN_MAX); + + assertEq(psm.previewSwapExactOut(address(usdc), address(dai), amountOut), amountOut / 1e12); + } + + function test_previewSwapExactOut_usdcToSDai() public view { + assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), 0.8e18), 1e6); + assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), 1.6e18), 2e6); + assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), 2.4e18), 3e6); + } + + function testFuzz_previewSwapExactOut_usdcToSDai(uint256 amountOut, uint256 conversionRate) public { + amountOut = _bound(amountOut, 1, SDAI_TOKEN_MAX); + conversionRate = _bound(conversionRate, 0.0001e27, 1000e27); // 0.01% to 100,000% conversion rate + + mockRateProvider.__setConversionRate(conversionRate); + + uint256 amountIn = amountOut * conversionRate / 1e27 / 1e12; + + assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), amountOut), amountIn); + } + +} + +contract PSMPreviewSwapExactIn_SDaiAssetInTests is PSMTestBase { + + function test_previewSwapExactIn_sDaiToDai() public view { + assertEq(psm.previewSwapExactIn(address(sDai), address(dai), 1e18), 1.25e18); + assertEq(psm.previewSwapExactIn(address(sDai), address(dai), 2e18), 2.5e18); + assertEq(psm.previewSwapExactIn(address(sDai), address(dai), 3e18), 3.75e18); + } + + function testFuzz_previewSwapExactIn_sDaiToDai(uint256 amountIn, uint256 conversionRate) public { + amountIn = _bound(amountIn, 1, SDAI_TOKEN_MAX); + conversionRate = _bound(conversionRate, 0.0001e27, 1000e27); // 0.01% to 100,000% conversion rate + + mockRateProvider.__setConversionRate(conversionRate); + + uint256 amountOut = amountIn * conversionRate / 1e27; + + assertEq(psm.previewSwapExactIn(address(sDai), address(dai), amountIn), amountOut); + } + + function test_previewSwapExactIn_sDaiToUsdc() public view { + assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), 1e18), 1.25e6); + assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), 2e18), 2.5e6); + assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), 3e18), 3.75e6); + } + + function testFuzz_previewSwapExactIn_sDaiToUsdc(uint256 amountIn, uint256 conversionRate) public { + amountIn = _bound(amountIn, 1, SDAI_TOKEN_MAX); + conversionRate = _bound(conversionRate, 0.0001e27, 1000e27); // 0.01% to 100,000% conversion rate + + mockRateProvider.__setConversionRate(conversionRate); + + uint256 amountOut = amountIn * conversionRate / 1e27 / 1e12; + + assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), amountIn), amountOut); + } + +} + +contract PSMPreviewSwapExactOut_SDaiAssetInTests is PSMTestBase { + + function test_previewSwapExactOut_sDaiToDai() public view { + assertEq(psm.previewSwapExactOut(address(sDai), address(dai), 1.25e18), 1e18); + assertEq(psm.previewSwapExactOut(address(sDai), address(dai), 2.5e18), 2e18); + assertEq(psm.previewSwapExactOut(address(sDai), address(dai), 3.75e18), 3e18); + } + + function testFuzz_previewSwapExactOut_sDaiToDai(uint256 amountOut, uint256 conversionRate) public { + amountOut = _bound(amountOut, 1, DAI_TOKEN_MAX); + conversionRate = _bound(conversionRate, 0.0001e27, 1000e27); // 0.01% to 100,000% conversion rate + + mockRateProvider.__setConversionRate(conversionRate); + + uint256 amountIn = amountOut * 1e27 / conversionRate; + + assertEq(psm.previewSwapExactOut(address(sDai), address(dai), amountOut), amountIn); + } + + function test_previewSwapExactOut_sDaiToUsdc() public view { + assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), 1.25e6), 1e18); + assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), 2.5e6), 2e18); + assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), 3.75e6), 3e18); + } + + function testFuzz_previewSwapExactOut_sDaiToUsdc(uint256 amountOut, uint256 conversionRate) public { + amountOut = bound(amountOut, 1, USDC_TOKEN_MAX); + conversionRate = bound(conversionRate, 0.0001e27, 1000e27); // 0.01% to 100,000% conversion rate + + mockRateProvider.__setConversionRate(conversionRate); + + uint256 amountIn = amountOut * 1e27 / conversionRate * 1e12; + + assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), amountOut), amountIn); + } + +} From 4354c89bb77dacda5894153f1c79916cdc0629b5 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Tue, 23 Jul 2024 09:37:08 -0400 Subject: [PATCH 2/7] fix: rm duplicate file --- test/unit/Previews.t.sol | 375 --------------------------------------- 1 file changed, 375 deletions(-) delete mode 100644 test/unit/Previews.t.sol diff --git a/test/unit/Previews.t.sol b/test/unit/Previews.t.sol deleted file mode 100644 index 6269cc2..0000000 --- a/test/unit/Previews.t.sol +++ /dev/null @@ -1,375 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity ^0.8.13; - -import "forge-std/Test.sol"; - -import { MockRateProvider, PSMTestBase } from "test/PSMTestBase.sol"; - -contract PSMPreviewSwapExactIn_FailureTests is PSMTestBase { - - function test_previewSwapExactIn_invalidAssetIn() public { - vm.expectRevert("PSM3/invalid-asset"); - psm.previewSwapExactIn(makeAddr("other-token"), address(usdc), 1); - } - - function test_previewSwapExactIn_invalidAssetOut() public { - vm.expectRevert("PSM3/invalid-asset"); - psm.previewSwapExactIn(address(usdc), makeAddr("other-token"), 1); - } - - function test_previewSwapExactIn_bothAsset0() public { - vm.expectRevert("PSM3/invalid-asset"); - psm.previewSwapExactIn(address(dai), address(dai), 1); - } - - function test_previewSwapExactIn_bothAsset1() public { - vm.expectRevert("PSM3/invalid-asset"); - psm.previewSwapExactIn(address(usdc), address(usdc), 1); - } - - function test_previewSwapExactIn_bothAsset2() public { - vm.expectRevert("PSM3/invalid-asset"); - psm.previewSwapExactIn(address(sDai), address(sDai), 1); - } - -} - -contract PSMPreviewSwapExactOut_FailureTests is PSMTestBase { - - function test_previewSwapExactIn_invalidAssetIn() public { - vm.expectRevert("PSM3/invalid-asset"); - psm.previewSwapExactOut(makeAddr("other-token"), address(usdc), 1); - } - - function test_previewSwapExactOut_invalidAssetOut() public { - vm.expectRevert("PSM3/invalid-asset"); - psm.previewSwapExactOut(address(usdc), makeAddr("other-token"), 1); - } - - function test_previewSwapExactOut_bothAsset0() public { - vm.expectRevert("PSM3/invalid-asset"); - psm.previewSwapExactOut(address(dai), address(dai), 1); - } - - function test_previewSwapExactOut_bothAsset1() public { - vm.expectRevert("PSM3/invalid-asset"); - psm.previewSwapExactOut(address(usdc), address(usdc), 1); - } - - function test_previewSwapExactOut_bothAsset2() public { - vm.expectRevert("PSM3/invalid-asset"); - psm.previewSwapExactOut(address(sDai), address(sDai), 1); - } - -} - -contract PSMPreviewSwapExactIn_DaiAssetInTests is PSMTestBase { - - function test_previewSwapExactIn_daiToUsdc() public view { - // Demo rounding down - assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 1e18 - 1), 1e6 - 1); - assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 1e18), 1e6); - assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 1e18 + 1), 1e6); - - assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 1e12 - 1), 0); - assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 1e12), 1); - - assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 1e18), 1e6); - assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 2e18), 2e6); - assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 3e18), 3e6); - } - - function testFuzz_previewSwapExactIn_daiToUsdc(uint256 amountIn) public view { - amountIn = _bound(amountIn, 0, DAI_TOKEN_MAX); - - assertEq(psm.previewSwapExactIn(address(dai), address(usdc), amountIn), amountIn / 1e12); - } - - function test_previewSwapExactIn_daiToSDai() public view { - // Demo rounding down - assertEq(psm.previewSwapExactIn(address(dai), address(sDai), 1e18 - 1), 0.8e18 - 1); - assertEq(psm.previewSwapExactIn(address(dai), address(sDai), 1e18), 0.8e18); - assertEq(psm.previewSwapExactIn(address(dai), address(sDai), 1e18 + 1), 0.8e18); - - assertEq(psm.previewSwapExactIn(address(dai), address(sDai), 1e18), 0.8e18); - assertEq(psm.previewSwapExactIn(address(dai), address(sDai), 2e18), 1.6e18); - assertEq(psm.previewSwapExactIn(address(dai), address(sDai), 3e18), 2.4e18); - } - - function testFuzz_previewSwapExactIn_daiToSDai(uint256 amountIn, uint256 conversionRate) public { - amountIn = _bound(amountIn, 1, DAI_TOKEN_MAX); - conversionRate = _bound(conversionRate, 0.0001e27, 1000e27); // 0.01% to 100,000% conversion rate - - mockRateProvider.__setConversionRate(conversionRate); - - uint256 amountOut = amountIn * 1e27 / conversionRate; - - assertEq(psm.previewSwapExactIn(address(dai), address(sDai), amountIn), amountOut); - } - -} - -contract PSMPreviewSwapExactOut_DaiAssetInTests is PSMTestBase { - - function test_previewSwapExactOut_daiToUsdc() public view { - // Demo rounding up - assertEq(psm.previewSwapExactOut(address(dai), address(usdc), 1e6 - 1), 0.999999e18); - assertEq(psm.previewSwapExactOut(address(dai), address(usdc), 1e6), 1e18); - assertEq(psm.previewSwapExactOut(address(dai), address(usdc), 1e6 + 1), 1.000001e18); - - assertEq(psm.previewSwapExactOut(address(dai), address(usdc), 1e6), 1e18); - assertEq(psm.previewSwapExactOut(address(dai), address(usdc), 2e6), 2e18); - assertEq(psm.previewSwapExactOut(address(dai), address(usdc), 3e6), 3e18); - } - - function testFuzz_previewSwapExactOut_daiToUsdc(uint256 amountOut) public view { - amountOut = _bound(amountOut, 0, USDC_TOKEN_MAX); - - assertEq(psm.previewSwapExactOut(address(dai), address(usdc), amountOut), amountOut * 1e12); - } - - function test_previewSwapExactOut_daiToSDai() public view { - // Demo rounding up - assertEq(psm.previewSwapExactOut(address(dai), address(sDai), 1e18 - 1), 1.25e18 - 1); - assertEq(psm.previewSwapExactOut(address(dai), address(sDai), 1e18), 1.25e18); - assertEq(psm.previewSwapExactOut(address(dai), address(sDai), 1e18 + 1), 1.25e18 + 2); - - assertEq(psm.previewSwapExactOut(address(dai), address(sDai), 0.8e18), 1e18); - assertEq(psm.previewSwapExactOut(address(dai), address(sDai), 1.6e18), 2e18); - assertEq(psm.previewSwapExactOut(address(dai), address(sDai), 2.4e18), 3e18); - } - - function testFuzz_previewSwapExactOut_daiToSDai(uint256 amountOut, uint256 conversionRate) public { - amountOut = _bound(amountOut, 1, USDC_TOKEN_MAX); - conversionRate = _bound(conversionRate, 0.0001e27, 1000e27); // 0.01% to 100,000% conversion rate - - mockRateProvider.__setConversionRate(conversionRate); - - uint256 expectedAmountIn = amountOut * conversionRate / 1e27; - - uint256 amountIn = psm.previewSwapExactOut(address(dai), address(sDai), amountOut); - - // Allow for rounding error of 1 unit upwards - assertLe(amountIn - expectedAmountIn, 1); - } - -} - -contract PSMPreviewSwapExactIn_USDCAssetInTests is PSMTestBase { - - function test_previewSwapExactIn_usdcToDai() public view { - // Demo rounding down - assertEq(psm.previewSwapExactIn(address(usdc), address(dai), 1e6 - 1), 0.999999e18); - assertEq(psm.previewSwapExactIn(address(usdc), address(dai), 1e6), 1e18); - assertEq(psm.previewSwapExactIn(address(usdc), address(dai), 1e6 + 1), 1.000001e18); - - assertEq(psm.previewSwapExactIn(address(usdc), address(dai), 1e6), 1e18); - assertEq(psm.previewSwapExactIn(address(usdc), address(dai), 2e6), 2e18); - assertEq(psm.previewSwapExactIn(address(usdc), address(dai), 3e6), 3e18); - } - - function testFuzz_previewSwapExactIn_usdcToDai(uint256 amountIn) public view { - amountIn = _bound(amountIn, 0, USDC_TOKEN_MAX); - - assertEq(psm.previewSwapExactIn(address(usdc), address(dai), amountIn), amountIn * 1e12); - } - - function test_previewSwapExactIn_usdcToSDai() public view { - // Demo rounding down - assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), 1e6 - 1), 0.799999e18); - assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), 1e6), 0.8e18); - assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), 1e6 + 1), 0.8e18); - - assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), 1e6), 0.8e18); - assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), 2e6), 1.6e18); - assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), 3e6), 2.4e18); - } - - function testFuzz_previewSwapExactIn_usdcToSDai(uint256 amountIn, uint256 conversionRate) public { - amountIn = _bound(amountIn, 1, USDC_TOKEN_MAX); - conversionRate = _bound(conversionRate, 0.0001e27, 1000e27); // 0.01% to 100,000% conversion rate - - mockRateProvider.__setConversionRate(conversionRate); - - uint256 amountOut = amountIn * 1e27 / conversionRate * 1e12; - - assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), amountIn), amountOut); - } - -} - -contract PSMPreviewSwapExactOut_USDCAssetInTests is PSMTestBase { - - function test_previewSwapExactOut_usdcToDai() public view { - // Demo rounding up - assertEq(psm.previewSwapExactOut(address(usdc), address(dai), 1e18 - 1), 1e6); - assertEq(psm.previewSwapExactOut(address(usdc), address(dai), 1e18), 1e6); - assertEq(psm.previewSwapExactOut(address(usdc), address(dai), 1e18 + 1), 1e6 + 1); - - assertEq(psm.previewSwapExactOut(address(usdc), address(dai), 1e18), 1e6); - assertEq(psm.previewSwapExactOut(address(usdc), address(dai), 2e18), 2e6); - assertEq(psm.previewSwapExactOut(address(usdc), address(dai), 3e18), 3e6); - } - - function testFuzz_previewSwapExactOut_usdcToDai(uint256 amountOut) public view { - amountOut = _bound(amountOut, 0, DAI_TOKEN_MAX); - - uint256 amountIn = psm.previewSwapExactOut(address(usdc), address(dai), amountOut); - - // Allow for rounding error of 1 unit upwards - assertLe(amountIn - amountOut / 1e12, 1); - } - - function test_previewSwapExactOut_usdcToSDai() public view { - // Demo rounding up - assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), 1e18 - 1), 1.25e6); - assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), 1e18), 1.25e6); - assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), 1e18 + 1), 1.25e6 + 1); - - assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), 0.8e18), 1e6); - assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), 1.6e18), 2e6); - assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), 2.4e18), 3e6); - } - - function testFuzz_previewSwapExactOut_usdcToSDai(uint256 amountOut, uint256 conversionRate) public { - amountOut = _bound(amountOut, 1, SDAI_TOKEN_MAX); - conversionRate = _bound(conversionRate, 0.0001e27, 1000e27); // 0.01% to 100,000% conversion rate - - mockRateProvider.__setConversionRate(conversionRate); - - // Using raw calculation to demo rounding - uint256 expectedAmountIn = amountOut * conversionRate / 1e27 / 1e12; - - uint256 amountIn = psm.previewSwapExactOut(address(usdc), address(sDai), amountOut); - - // Allow for rounding error of 1 unit upwards - assertLe(amountIn - expectedAmountIn, 1); - } - - function test_demoRoundingUp_usdcToSDai() public view { - uint256 expectedAmountIn1 = psm.previewSwapExactOut(address(usdc), address(sDai), 0.8e18); - uint256 expectedAmountIn2 = psm.previewSwapExactOut(address(usdc), address(sDai), 0.8e18 + 1); - uint256 expectedAmountIn3 = psm.previewSwapExactOut(address(usdc), address(sDai), 0.8e18 + 0.8e12); - uint256 expectedAmountIn4 = psm.previewSwapExactOut(address(usdc), address(sDai), 0.8e18 + 0.8e12 + 1); - - assertEq(expectedAmountIn1, 1e6); - assertEq(expectedAmountIn2, 1e6 + 1); - assertEq(expectedAmountIn3, 1e6 + 1); - assertEq(expectedAmountIn4, 1e6 + 2); - } - - function test_demoRoundingUp_usdcToDai() public view { - uint256 expectedAmountIn1 = psm.previewSwapExactOut(address(usdc), address(dai), 1e18); - uint256 expectedAmountIn2 = psm.previewSwapExactOut(address(usdc), address(dai), 1e18 + 1); - uint256 expectedAmountIn3 = psm.previewSwapExactOut(address(usdc), address(dai), 1e18 + 1e12); - uint256 expectedAmountIn4 = psm.previewSwapExactOut(address(usdc), address(dai), 1e18 + 1e12 + 1); - - assertEq(expectedAmountIn1, 1e6); - assertEq(expectedAmountIn2, 1e6 + 1); - assertEq(expectedAmountIn3, 1e6 + 1); - assertEq(expectedAmountIn4, 1e6 + 2); - } - -} - -contract PSMPreviewSwapExactIn_SDaiAssetInTests is PSMTestBase { - - function test_previewSwapExactIn_sDaiToDai() public view { - // Demo rounding down - assertEq(psm.previewSwapExactIn(address(sDai), address(dai), 1e18 - 1), 1.25e18 - 2); - assertEq(psm.previewSwapExactIn(address(sDai), address(dai), 1e18), 1.25e18); - assertEq(psm.previewSwapExactIn(address(sDai), address(dai), 1e18 + 1), 1.25e18 + 1); - - assertEq(psm.previewSwapExactIn(address(sDai), address(dai), 1e18), 1.25e18); - assertEq(psm.previewSwapExactIn(address(sDai), address(dai), 2e18), 2.5e18); - assertEq(psm.previewSwapExactIn(address(sDai), address(dai), 3e18), 3.75e18); - } - - function testFuzz_previewSwapExactIn_sDaiToDai(uint256 amountIn, uint256 conversionRate) public { - amountIn = _bound(amountIn, 1, SDAI_TOKEN_MAX); - conversionRate = _bound(conversionRate, 0.0001e27, 1000e27); // 0.01% to 100,000% conversion rate - - mockRateProvider.__setConversionRate(conversionRate); - - uint256 amountOut = amountIn * conversionRate / 1e27; - - assertEq(psm.previewSwapExactIn(address(sDai), address(dai), amountIn), amountOut); - } - - function test_previewSwapExactIn_sDaiToUsdc() public view { - // Demo rounding down - assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), 1e18 - 1), 1.25e6 - 1); - assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), 1e18), 1.25e6); - assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), 1e18 + 1), 1.25e6); - - assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), 1e18), 1.25e6); - assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), 2e18), 2.5e6); - assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), 3e18), 3.75e6); - } - - function testFuzz_previewSwapExactIn_sDaiToUsdc(uint256 amountIn, uint256 conversionRate) public { - amountIn = _bound(amountIn, 1, SDAI_TOKEN_MAX); - conversionRate = _bound(conversionRate, 0.0001e27, 1000e27); // 0.01% to 100,000% conversion rate - - mockRateProvider.__setConversionRate(conversionRate); - - uint256 amountOut = amountIn * conversionRate / 1e27 / 1e12; - - assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), amountIn), amountOut); - } - -} - -contract PSMPreviewSwapExactOut_SDaiAssetInTests is PSMTestBase { - - function test_previewSwapExactOut_sDaiToDai() public view { - // Demo rounding up - assertEq(psm.previewSwapExactOut(address(sDai), address(dai), 1e18 - 1), 0.8e18); - assertEq(psm.previewSwapExactOut(address(sDai), address(dai), 1e18), 0.8e18); - assertEq(psm.previewSwapExactOut(address(sDai), address(dai), 1e18 + 1), 0.8e18 + 1); - - assertEq(psm.previewSwapExactOut(address(sDai), address(dai), 1.25e18), 1e18); - assertEq(psm.previewSwapExactOut(address(sDai), address(dai), 2.5e18), 2e18); - assertEq(psm.previewSwapExactOut(address(sDai), address(dai), 3.75e18), 3e18); - } - - function testFuzz_previewSwapExactOut_sDaiToDai(uint256 amountOut, uint256 conversionRate) public { - amountOut = _bound(amountOut, 1, DAI_TOKEN_MAX); - conversionRate = _bound(conversionRate, 0.0001e27, 1000e27); // 0.01% to 100,000% conversion rate - - mockRateProvider.__setConversionRate(conversionRate); - - uint256 expectedAmountIn = amountOut * 1e27 / conversionRate; - - uint256 amountIn = psm.previewSwapExactOut(address(sDai), address(dai), amountOut); - - // Allow for rounding error of 1 unit upwards - assertLe(amountIn - expectedAmountIn, 1); - } - - function test_previewSwapExactOut_sDaiToUsdc() public view { - // Demo rounding up - assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), 1e6 - 1), 0.8e18); - assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), 1e6), 0.8e18); - assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), 1e6 + 1), 0.800001e18); - - assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), 1.25e6), 1e18); - assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), 2.5e6), 2e18); - assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), 3.75e6), 3e18); - } - - function testFuzz_previewSwapExactOut_sDaiToUsdc(uint256 amountOut, uint256 conversionRate) public { - amountOut = bound(amountOut, 1, USDC_TOKEN_MAX); - conversionRate = bound(conversionRate, 0.0001e27, 1000e27); // 0.01% to 100,000% conversion rate - - mockRateProvider.__setConversionRate(conversionRate); - - uint256 expectedAmountIn = amountOut * 1e27 / conversionRate * 1e12; - - uint256 amountIn = psm.previewSwapExactOut(address(sDai), address(usdc), amountOut); - - // Allow for rounding error of 1e12 upwards - assertLe(amountIn - expectedAmountIn, 1e12); - } - -} From 5086898e1a379335f7ee585db600c7f8e969a417 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Tue, 23 Jul 2024 10:31:27 -0400 Subject: [PATCH 3/7] feat: update preview deposit coverage --- test/unit/PreviewDeposit.t.sol | 83 ++++++++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/test/unit/PreviewDeposit.t.sol b/test/unit/PreviewDeposit.t.sol index 1251a67..17382b9 100644 --- a/test/unit/PreviewDeposit.t.sol +++ b/test/unit/PreviewDeposit.t.sol @@ -18,6 +18,21 @@ contract PSMPreviewDeposit_SuccessTests is PSMTestBase { address depositor = makeAddr("depositor"); + function test_previewDeposit_dai_firstDeposit() public view { + assertEq(psm.previewDeposit(address(dai), 1), 1); + assertEq(psm.previewDeposit(address(dai), 2), 2); + assertEq(psm.previewDeposit(address(dai), 3), 3); + + assertEq(psm.previewDeposit(address(dai), 1e18), 1e18); + assertEq(psm.previewDeposit(address(dai), 2e18), 2e18); + assertEq(psm.previewDeposit(address(dai), 3e18), 3e18); + } + + function testFuzz_previewDeposit_dai_firstDeposit(uint256 amount) public view { + amount = _bound(amount, 0, DAI_TOKEN_MAX); + assertEq(psm.previewDeposit(address(dai), amount), amount); + } + function test_previewDeposit_usdc_firstDeposit() public view { assertEq(psm.previewDeposit(address(usdc), 1), 1e12); assertEq(psm.previewDeposit(address(usdc), 2), 2e12); @@ -33,27 +48,46 @@ contract PSMPreviewDeposit_SuccessTests is PSMTestBase { assertEq(psm.previewDeposit(address(usdc), amount), amount * 1e12); } - function test_previewDeposit_usdc_notOneToOne() public { - assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + function test_previewDeposit_sDai_firstDeposit() public view { + assertEq(psm.previewDeposit(address(sDai), 1), 1); + assertEq(psm.previewDeposit(address(sDai), 2), 2); + assertEq(psm.previewDeposit(address(sDai), 3), 3); + assertEq(psm.previewDeposit(address(sDai), 4), 5); + + assertEq(psm.previewDeposit(address(sDai), 1e18), 1.25e18); + assertEq(psm.previewDeposit(address(sDai), 2e18), 2.50e18); + assertEq(psm.previewDeposit(address(sDai), 3e18), 3.75e18); + assertEq(psm.previewDeposit(address(sDai), 4e18), 5.00e18); + } + + function testFuzz_previewDeposit_sDai_firstDeposit(uint256 amount) public view { + amount = _bound(amount, 0, SDAI_TOKEN_MAX); + assertEq(psm.previewDeposit(address(sDai), amount), amount * 1.25e27 / 1e27); + } + + function test_previewDeposit_afterDepositsAndExchangeRateIncrease() public { + _assertOneToOne(); _deposit(address(dai), depositor, 1e18); - assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + _assertOneToOne(); _deposit(address(usdc), depositor, 1e6); - assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + _assertOneToOne(); _deposit(address(sDai), depositor, 0.8e18); - assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + _assertOneToOne(); mockRateProvider.__setConversionRate(2e27); // $300 dollars of value deposited, 300 shares minted. // sDAI portion becomes worth $160, full pool worth $360, each share worth $1.20 // 1 USDC = 1/1.20 = 0.833... - assertEq(psm.previewDeposit(address(usdc), 1e6), 0.833333333333333333e18); + assertEq(psm.previewDeposit(address(dai), 1e18), 0.833333333333333333e18); + assertEq(psm.previewDeposit(address(usdc), 1e6), 0.833333333333333333e18); + assertEq(psm.previewDeposit(address(sDai), 1e18), 1.666666666666666666e18); // 1 sDAI = $2 } - function test_previewDeposit_usdc_notOneToOne( + function testFuzz_previewDeposit_afterDepositsAndExchangeRateIncrease( uint256 amount1, uint256 amount2, uint256 amount3, @@ -64,25 +98,34 @@ contract PSMPreviewDeposit_SuccessTests is PSMTestBase { amount2 = _bound(amount2, 1, USDC_TOKEN_MAX); amount3 = _bound(amount3, 1, SDAI_TOKEN_MAX); conversionRate = _bound(conversionRate, 1.00e27, 1000e27); - previewAmount = _bound(previewAmount, 0, USDC_TOKEN_MAX); + previewAmount = _bound(previewAmount, 0, DAI_TOKEN_MAX); - assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + _assertOneToOne(); - _deposit(address(dai), depositor, 1e18); - assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + _deposit(address(dai), depositor, amount1); + _assertOneToOne(); - _deposit(address(usdc), depositor, 1e6); - assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + _deposit(address(usdc), depositor, amount2); + _assertOneToOne(); - _deposit(address(sDai), depositor, 0.8e18); - assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + _deposit(address(sDai), depositor, amount3); + _assertOneToOne(); - mockRateProvider.__setConversionRate(2e27); + mockRateProvider.__setConversionRate(conversionRate); - // $300 dollars of value deposited, 300 shares minted. - // sDAI portion becomes worth $160, full pool worth $360, each share worth $1.20 - // 1 USDC = 1/1.20 = 0.833... - assertEq(psm.previewDeposit(address(usdc), 1e6), 0.833333333333333333e18); + uint256 totalSharesMinted = amount1 + amount2 * 1e12 + amount3 * 1.25e27 / 1e27; + uint256 totalValue = amount1 + amount2 * 1e12 + amount3 * conversionRate / 1e27; + uint256 usdcPreviewAmount = previewAmount / 1e12; + + assertEq(psm.previewDeposit(address(dai), previewAmount), previewAmount * totalSharesMinted / totalValue); + assertEq(psm.previewDeposit(address(usdc), usdcPreviewAmount), usdcPreviewAmount * 1e12 * totalSharesMinted / totalValue); // Divide then multiply to replicate rounding + assertEq(psm.previewDeposit(address(sDai), previewAmount), (previewAmount * conversionRate / 1e27) * totalSharesMinted / totalValue); + } + + function _assertOneToOne() internal view { + assertEq(psm.previewDeposit(address(dai), 1e18), 1e18); + assertEq(psm.previewDeposit(address(usdc), 1e6), 1e18); + assertEq(psm.previewDeposit(address(sDai), 1e18), 1.25e18); } } From 4035cd117d6a979cd6bb7d6f5ab86dc4902d2d77 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Tue, 23 Jul 2024 11:51:33 -0400 Subject: [PATCH 4/7] feat: start on withdraw tests --- test/unit/PreviewWIthdraw.t.sol | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 test/unit/PreviewWIthdraw.t.sol diff --git a/test/unit/PreviewWIthdraw.t.sol b/test/unit/PreviewWIthdraw.t.sol new file mode 100644 index 0000000..37c6454 --- /dev/null +++ b/test/unit/PreviewWIthdraw.t.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; + +import { MockRateProvider, PSMTestBase } from "test/PSMTestBase.sol"; + +contract PSMPreviewWithdraw_FailureTests is PSMTestBase { + + function test_previewWithdraw_invalidAsset() public { + vm.expectRevert("PSM3/invalid-asset"); + psm.previewWithdraw(makeAddr("other-token"), 1); + } + +} + +contract PSMPreviewWithdraw_SuccessTests is PSMTestBase { + + function setUp() public override { + super.setUp(); + _deposit(address(this), address(dai), 100e18); + _deposit(makeAddr("other-user"), address(usdc), 10e6); + } + + function test_previewWithdraw_amountLtPsmBalance_amountLtShares() public { + assertEq(psm.previewWithdraw(address(dai), 1), 1); + + + } +} From 19ff22d898b14fbb9058bb7cd1eb13b9e9255ad5 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Wed, 24 Jul 2024 12:43:30 -0400 Subject: [PATCH 5/7] feat: unit tests complete --- test/invariant/handlers/RateSetterHandler.sol | 1 + test/unit/PreviewWIthdraw.t.sol | 90 ++++++++++++++++++- 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/test/invariant/handlers/RateSetterHandler.sol b/test/invariant/handlers/RateSetterHandler.sol index 5d58109..9eb0702 100644 --- a/test/invariant/handlers/RateSetterHandler.sol +++ b/test/invariant/handlers/RateSetterHandler.sol @@ -26,4 +26,5 @@ contract RateSetterHandler is StdUtils { setRateCount++; } + } diff --git a/test/unit/PreviewWIthdraw.t.sol b/test/unit/PreviewWIthdraw.t.sol index 37c6454..dc88cff 100644 --- a/test/unit/PreviewWIthdraw.t.sol +++ b/test/unit/PreviewWIthdraw.t.sol @@ -14,17 +14,99 @@ contract PSMPreviewWithdraw_FailureTests is PSMTestBase { } +contract PSMPreviewWithdraw_ZeroAssetsTests is PSMTestBase { + + function test_previewWithdraw_zeroTotalAssets() public { + ( uint256 shares1, uint256 assets1 ) = psm.previewWithdraw(address(dai), 1e18); + ( uint256 shares2, uint256 assets2 ) = psm.previewWithdraw(address(usdc), 1e6); + ( uint256 shares3, uint256 assets3 ) = psm.previewWithdraw(address(sDai), 1e18); + + assertEq(shares1, 0); + assertEq(assets1, 0); + assertEq(shares2, 0); + assertEq(assets2, 0); + assertEq(shares3, 0); + assertEq(assets3, 0); + + mockRateProvider.__setConversionRate(2e27); + + ( shares1, assets1 ) = psm.previewWithdraw(address(dai), 1e18); + ( shares2, assets2 ) = psm.previewWithdraw(address(usdc), 1e6); + ( shares3, assets3 ) = psm.previewWithdraw(address(sDai), 1e18); + + assertEq(shares1, 0); + assertEq(assets1, 0); + assertEq(shares2, 0); + assertEq(assets2, 0); + assertEq(shares3, 0); + assertEq(assets3, 0); + } + +} + contract PSMPreviewWithdraw_SuccessTests is PSMTestBase { function setUp() public override { super.setUp(); - _deposit(address(this), address(dai), 100e18); - _deposit(makeAddr("other-user"), address(usdc), 10e6); + // Setup so that address(this) has the most shares, higher underlying balance than PSM + // balance of sDAI and USDC + _deposit(address(dai), address(this), 100e18); + _deposit(address(usdc), makeAddr("usdc-user"), 10e6); + _deposit(address(sDai), makeAddr("sDai-user"), 1e18); + } + + function test_previewWithdraw_dai_amountLtUnderlyingBalance() public view { + ( uint256 shares, uint256 assets ) = psm.previewWithdraw(address(dai), 100e18 - 1); + assertEq(shares, 100e18 - 1); + assertEq(assets, 100e18 - 1); + } + + function test_previewWithdraw_dai_amountEqUnderlyingBalance() public view { + ( uint256 shares, uint256 assets ) = psm.previewWithdraw(address(dai), 100e18); + assertEq(shares, 100e18); + assertEq(assets, 100e18); + } + + function test_previewWithdraw_dai_amountGtUnderlyingBalance() public view { + ( uint256 shares, uint256 assets ) = psm.previewWithdraw(address(dai), 100e18 + 1); + assertEq(shares, 100e18); + assertEq(assets, 100e18); + } + + function test_previewWithdraw_usdc_amountLtUnderlyingBalanceAndLtPsmBalance() public view { + ( uint256 shares, uint256 assets ) = psm.previewWithdraw(address(usdc), 10e6 - 1); + assertEq(shares, 10e18 - 1e12); + assertEq(assets, 10e6 - 1); + } + + function test_previewWithdraw_usdc_amountLtUnderlyingBalanceAndEqPsmBalance() public view { + ( uint256 shares, uint256 assets ) = psm.previewWithdraw(address(usdc), 10e6); + assertEq(shares, 10e18); + assertEq(assets, 10e6); + } + + function test_previewWithdraw_usdc_amountLtUnderlyingBalanceAndGtPsmBalance() public view { + ( uint256 shares, uint256 assets ) = psm.previewWithdraw(address(usdc), 10e6 + 1); + assertEq(shares, 10e18); + assertEq(assets, 10e6); } - function test_previewWithdraw_amountLtPsmBalance_amountLtShares() public { - assertEq(psm.previewWithdraw(address(dai), 1), 1); + function test_previewWithdraw_sdai_amountLtUnderlyingBalanceAndLtPsmBalance() public view { + ( uint256 shares, uint256 assets ) = psm.previewWithdraw(address(sDai), 1e18 - 1); + assertEq(shares, 1.25e18 - 2); + assertEq(assets, 1e18 - 1); + } + function test_previewWithdraw_sdai_amountLtUnderlyingBalanceAndEqPsmBalance() public view { + ( uint256 shares, uint256 assets ) = psm.previewWithdraw(address(sDai), 1e18); + assertEq(shares, 1.25e18); + assertEq(assets, 1e18); + } + function test_previewWithdraw_sdai_amountLtUnderlyingBalanceAndGtPsmBalance() public view { + ( uint256 shares, uint256 assets ) = psm.previewWithdraw(address(sDai), 1e18 + 1); + assertEq(shares, 1.25e18); + assertEq(assets, 1e18); } + } From b7c34175f036e1800f011d6825b9fdf7daecb138 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Wed, 24 Jul 2024 13:03:11 -0400 Subject: [PATCH 6/7] feat: add fuzz test --- test/unit/PreviewWIthdraw.t.sol | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/test/unit/PreviewWIthdraw.t.sol b/test/unit/PreviewWIthdraw.t.sol index dc88cff..64777da 100644 --- a/test/unit/PreviewWIthdraw.t.sol +++ b/test/unit/PreviewWIthdraw.t.sol @@ -16,6 +16,7 @@ contract PSMPreviewWithdraw_FailureTests is PSMTestBase { contract PSMPreviewWithdraw_ZeroAssetsTests is PSMTestBase { + // Always returns zero because there is no balance of assets in the PSM in this case function test_previewWithdraw_zeroTotalAssets() public { ( uint256 shares1, uint256 assets1 ) = psm.previewWithdraw(address(dai), 1e18); ( uint256 shares2, uint256 assets2 ) = psm.previewWithdraw(address(usdc), 1e6); @@ -110,3 +111,73 @@ contract PSMPreviewWithdraw_SuccessTests is PSMTestBase { } } + +contract PSMPreviewWithdraw_SuccessFuzzTests is PSMTestBase { + + struct TestParams { + uint256 amount1; + uint256 amount2; + uint256 amount3; + uint256 previewAmount1; + uint256 previewAmount2; + uint256 previewAmount3; + uint256 conversionRate; + } + + function testFuzz_previewWithdraw(TestParams memory params) public { + params.amount1 = _bound(params.amount1, 1, DAI_TOKEN_MAX); + params.amount2 = _bound(params.amount2, 1, USDC_TOKEN_MAX); + params.amount3 = _bound(params.amount3, 1, SDAI_TOKEN_MAX); + + // Only covering case of amount being below underlying to focus on value conversion + // and avoid reimplementation of contract logic for dealing with capping amounts + params.previewAmount1 = _bound(params.previewAmount1, 0, params.amount1); + params.previewAmount2 = _bound(params.previewAmount2, 0, params.amount2); + params.previewAmount3 = _bound(params.previewAmount3, 0, params.amount3); + + _deposit(address(dai), address(this), params.amount1); + _deposit(address(usdc), address(this), params.amount2); + _deposit(address(sDai), address(this), params.amount3); + + ( uint256 shares1, uint256 assets1 ) = psm.previewWithdraw(address(dai), params.previewAmount1); + ( uint256 shares2, uint256 assets2 ) = psm.previewWithdraw(address(usdc), params.previewAmount2); + ( uint256 shares3, uint256 assets3 ) = psm.previewWithdraw(address(sDai), params.previewAmount3); + + uint256 totalSharesMinted = params.amount1 + params.amount2 * 1e12 + params.amount3 * 1.25e27 / 1e27; + uint256 totalValue = totalSharesMinted; + + assertEq(shares1, params.previewAmount1 * totalSharesMinted / totalValue); + assertEq(shares2, params.previewAmount2 * 1e12 * totalSharesMinted / totalValue); + assertEq(shares3, params.previewAmount3 * 1.25e27 / 1e27 * totalSharesMinted / totalValue); + + assertEq(assets1, params.previewAmount1); + assertEq(assets2, params.previewAmount2); + assertEq(assets3, params.previewAmount3); + + params.conversionRate = _bound(params.conversionRate, 0.001e27, 1000e27); + mockRateProvider.__setConversionRate(params.conversionRate); + + // sDai value accrual changes the value of shares in the PSM + totalValue = params.amount1 + params.amount2 * 1e12 + params.amount3 * params.conversionRate / 1e27; + + ( shares1, assets1 ) = psm.previewWithdraw(address(dai), params.previewAmount1); + ( shares2, assets2 ) = psm.previewWithdraw(address(usdc), params.previewAmount2); + ( shares3, assets3 ) = psm.previewWithdraw(address(sDai), params.previewAmount3); + + uint256 sDaiConvertedAmount = params.previewAmount3 * params.conversionRate / 1e27; + + assertApproxEqAbs(shares1, params.previewAmount1 * totalSharesMinted / totalValue, 1); + assertApproxEqAbs(shares2, params.previewAmount2 * 1e12 * totalSharesMinted / totalValue, 1); + assertApproxEqAbs(shares3, sDaiConvertedAmount * totalSharesMinted / totalValue, 1); + + // Assert shares are always rounded up + assertGe(shares1, params.previewAmount1 * totalSharesMinted / totalValue); + assertGe(shares2, params.previewAmount2 * 1e12 * totalSharesMinted / totalValue); + assertGe(shares3, sDaiConvertedAmount * totalSharesMinted / totalValue); + + assertApproxEqAbs(assets1, params.previewAmount1, 1); + assertApproxEqAbs(assets2, params.previewAmount2, 1); + assertApproxEqAbs(assets3, params.previewAmount3, 1); + } + +} From ec1e149bc0937cbbc0a572d7513968429d518633 Mon Sep 17 00:00:00 2001 From: lucas-manuel Date: Wed, 24 Jul 2024 13:12:35 -0400 Subject: [PATCH 7/7] fix: update file names --- test/unit/SwapPreviews.t.sol | 118 ++++++++++++++++++++++++++++++++--- 1 file changed, 109 insertions(+), 9 deletions(-) diff --git a/test/unit/SwapPreviews.t.sol b/test/unit/SwapPreviews.t.sol index 999e4ba..6269cc2 100644 --- a/test/unit/SwapPreviews.t.sol +++ b/test/unit/SwapPreviews.t.sol @@ -66,6 +66,11 @@ contract PSMPreviewSwapExactOut_FailureTests is PSMTestBase { contract PSMPreviewSwapExactIn_DaiAssetInTests is PSMTestBase { function test_previewSwapExactIn_daiToUsdc() public view { + // Demo rounding down + assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 1e18 - 1), 1e6 - 1); + assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 1e18), 1e6); + assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 1e18 + 1), 1e6); + assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 1e12 - 1), 0); assertEq(psm.previewSwapExactIn(address(dai), address(usdc), 1e12), 1); @@ -81,6 +86,11 @@ contract PSMPreviewSwapExactIn_DaiAssetInTests is PSMTestBase { } function test_previewSwapExactIn_daiToSDai() public view { + // Demo rounding down + assertEq(psm.previewSwapExactIn(address(dai), address(sDai), 1e18 - 1), 0.8e18 - 1); + assertEq(psm.previewSwapExactIn(address(dai), address(sDai), 1e18), 0.8e18); + assertEq(psm.previewSwapExactIn(address(dai), address(sDai), 1e18 + 1), 0.8e18); + assertEq(psm.previewSwapExactIn(address(dai), address(sDai), 1e18), 0.8e18); assertEq(psm.previewSwapExactIn(address(dai), address(sDai), 2e18), 1.6e18); assertEq(psm.previewSwapExactIn(address(dai), address(sDai), 3e18), 2.4e18); @@ -102,6 +112,11 @@ contract PSMPreviewSwapExactIn_DaiAssetInTests is PSMTestBase { contract PSMPreviewSwapExactOut_DaiAssetInTests is PSMTestBase { function test_previewSwapExactOut_daiToUsdc() public view { + // Demo rounding up + assertEq(psm.previewSwapExactOut(address(dai), address(usdc), 1e6 - 1), 0.999999e18); + assertEq(psm.previewSwapExactOut(address(dai), address(usdc), 1e6), 1e18); + assertEq(psm.previewSwapExactOut(address(dai), address(usdc), 1e6 + 1), 1.000001e18); + assertEq(psm.previewSwapExactOut(address(dai), address(usdc), 1e6), 1e18); assertEq(psm.previewSwapExactOut(address(dai), address(usdc), 2e6), 2e18); assertEq(psm.previewSwapExactOut(address(dai), address(usdc), 3e6), 3e18); @@ -114,6 +129,11 @@ contract PSMPreviewSwapExactOut_DaiAssetInTests is PSMTestBase { } function test_previewSwapExactOut_daiToSDai() public view { + // Demo rounding up + assertEq(psm.previewSwapExactOut(address(dai), address(sDai), 1e18 - 1), 1.25e18 - 1); + assertEq(psm.previewSwapExactOut(address(dai), address(sDai), 1e18), 1.25e18); + assertEq(psm.previewSwapExactOut(address(dai), address(sDai), 1e18 + 1), 1.25e18 + 2); + assertEq(psm.previewSwapExactOut(address(dai), address(sDai), 0.8e18), 1e18); assertEq(psm.previewSwapExactOut(address(dai), address(sDai), 1.6e18), 2e18); assertEq(psm.previewSwapExactOut(address(dai), address(sDai), 2.4e18), 3e18); @@ -125,9 +145,12 @@ contract PSMPreviewSwapExactOut_DaiAssetInTests is PSMTestBase { mockRateProvider.__setConversionRate(conversionRate); - uint256 amountIn = amountOut * conversionRate / 1e27; + uint256 expectedAmountIn = amountOut * conversionRate / 1e27; + + uint256 amountIn = psm.previewSwapExactOut(address(dai), address(sDai), amountOut); - assertEq(psm.previewSwapExactOut(address(dai), address(sDai), amountOut), amountIn); + // Allow for rounding error of 1 unit upwards + assertLe(amountIn - expectedAmountIn, 1); } } @@ -135,6 +158,11 @@ contract PSMPreviewSwapExactOut_DaiAssetInTests is PSMTestBase { contract PSMPreviewSwapExactIn_USDCAssetInTests is PSMTestBase { function test_previewSwapExactIn_usdcToDai() public view { + // Demo rounding down + assertEq(psm.previewSwapExactIn(address(usdc), address(dai), 1e6 - 1), 0.999999e18); + assertEq(psm.previewSwapExactIn(address(usdc), address(dai), 1e6), 1e18); + assertEq(psm.previewSwapExactIn(address(usdc), address(dai), 1e6 + 1), 1.000001e18); + assertEq(psm.previewSwapExactIn(address(usdc), address(dai), 1e6), 1e18); assertEq(psm.previewSwapExactIn(address(usdc), address(dai), 2e6), 2e18); assertEq(psm.previewSwapExactIn(address(usdc), address(dai), 3e6), 3e18); @@ -147,6 +175,11 @@ contract PSMPreviewSwapExactIn_USDCAssetInTests is PSMTestBase { } function test_previewSwapExactIn_usdcToSDai() public view { + // Demo rounding down + assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), 1e6 - 1), 0.799999e18); + assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), 1e6), 0.8e18); + assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), 1e6 + 1), 0.8e18); + assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), 1e6), 0.8e18); assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), 2e6), 1.6e18); assertEq(psm.previewSwapExactIn(address(usdc), address(sDai), 3e6), 2.4e18); @@ -168,6 +201,11 @@ contract PSMPreviewSwapExactIn_USDCAssetInTests is PSMTestBase { contract PSMPreviewSwapExactOut_USDCAssetInTests is PSMTestBase { function test_previewSwapExactOut_usdcToDai() public view { + // Demo rounding up + assertEq(psm.previewSwapExactOut(address(usdc), address(dai), 1e18 - 1), 1e6); + assertEq(psm.previewSwapExactOut(address(usdc), address(dai), 1e18), 1e6); + assertEq(psm.previewSwapExactOut(address(usdc), address(dai), 1e18 + 1), 1e6 + 1); + assertEq(psm.previewSwapExactOut(address(usdc), address(dai), 1e18), 1e6); assertEq(psm.previewSwapExactOut(address(usdc), address(dai), 2e18), 2e6); assertEq(psm.previewSwapExactOut(address(usdc), address(dai), 3e18), 3e6); @@ -176,10 +214,18 @@ contract PSMPreviewSwapExactOut_USDCAssetInTests is PSMTestBase { function testFuzz_previewSwapExactOut_usdcToDai(uint256 amountOut) public view { amountOut = _bound(amountOut, 0, DAI_TOKEN_MAX); - assertEq(psm.previewSwapExactOut(address(usdc), address(dai), amountOut), amountOut / 1e12); + uint256 amountIn = psm.previewSwapExactOut(address(usdc), address(dai), amountOut); + + // Allow for rounding error of 1 unit upwards + assertLe(amountIn - amountOut / 1e12, 1); } function test_previewSwapExactOut_usdcToSDai() public view { + // Demo rounding up + assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), 1e18 - 1), 1.25e6); + assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), 1e18), 1.25e6); + assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), 1e18 + 1), 1.25e6 + 1); + assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), 0.8e18), 1e6); assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), 1.6e18), 2e6); assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), 2.4e18), 3e6); @@ -191,9 +237,37 @@ contract PSMPreviewSwapExactOut_USDCAssetInTests is PSMTestBase { mockRateProvider.__setConversionRate(conversionRate); - uint256 amountIn = amountOut * conversionRate / 1e27 / 1e12; + // Using raw calculation to demo rounding + uint256 expectedAmountIn = amountOut * conversionRate / 1e27 / 1e12; + + uint256 amountIn = psm.previewSwapExactOut(address(usdc), address(sDai), amountOut); - assertEq(psm.previewSwapExactOut(address(usdc), address(sDai), amountOut), amountIn); + // Allow for rounding error of 1 unit upwards + assertLe(amountIn - expectedAmountIn, 1); + } + + function test_demoRoundingUp_usdcToSDai() public view { + uint256 expectedAmountIn1 = psm.previewSwapExactOut(address(usdc), address(sDai), 0.8e18); + uint256 expectedAmountIn2 = psm.previewSwapExactOut(address(usdc), address(sDai), 0.8e18 + 1); + uint256 expectedAmountIn3 = psm.previewSwapExactOut(address(usdc), address(sDai), 0.8e18 + 0.8e12); + uint256 expectedAmountIn4 = psm.previewSwapExactOut(address(usdc), address(sDai), 0.8e18 + 0.8e12 + 1); + + assertEq(expectedAmountIn1, 1e6); + assertEq(expectedAmountIn2, 1e6 + 1); + assertEq(expectedAmountIn3, 1e6 + 1); + assertEq(expectedAmountIn4, 1e6 + 2); + } + + function test_demoRoundingUp_usdcToDai() public view { + uint256 expectedAmountIn1 = psm.previewSwapExactOut(address(usdc), address(dai), 1e18); + uint256 expectedAmountIn2 = psm.previewSwapExactOut(address(usdc), address(dai), 1e18 + 1); + uint256 expectedAmountIn3 = psm.previewSwapExactOut(address(usdc), address(dai), 1e18 + 1e12); + uint256 expectedAmountIn4 = psm.previewSwapExactOut(address(usdc), address(dai), 1e18 + 1e12 + 1); + + assertEq(expectedAmountIn1, 1e6); + assertEq(expectedAmountIn2, 1e6 + 1); + assertEq(expectedAmountIn3, 1e6 + 1); + assertEq(expectedAmountIn4, 1e6 + 2); } } @@ -201,6 +275,11 @@ contract PSMPreviewSwapExactOut_USDCAssetInTests is PSMTestBase { contract PSMPreviewSwapExactIn_SDaiAssetInTests is PSMTestBase { function test_previewSwapExactIn_sDaiToDai() public view { + // Demo rounding down + assertEq(psm.previewSwapExactIn(address(sDai), address(dai), 1e18 - 1), 1.25e18 - 2); + assertEq(psm.previewSwapExactIn(address(sDai), address(dai), 1e18), 1.25e18); + assertEq(psm.previewSwapExactIn(address(sDai), address(dai), 1e18 + 1), 1.25e18 + 1); + assertEq(psm.previewSwapExactIn(address(sDai), address(dai), 1e18), 1.25e18); assertEq(psm.previewSwapExactIn(address(sDai), address(dai), 2e18), 2.5e18); assertEq(psm.previewSwapExactIn(address(sDai), address(dai), 3e18), 3.75e18); @@ -218,6 +297,11 @@ contract PSMPreviewSwapExactIn_SDaiAssetInTests is PSMTestBase { } function test_previewSwapExactIn_sDaiToUsdc() public view { + // Demo rounding down + assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), 1e18 - 1), 1.25e6 - 1); + assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), 1e18), 1.25e6); + assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), 1e18 + 1), 1.25e6); + assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), 1e18), 1.25e6); assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), 2e18), 2.5e6); assertEq(psm.previewSwapExactIn(address(sDai), address(usdc), 3e18), 3.75e6); @@ -239,6 +323,11 @@ contract PSMPreviewSwapExactIn_SDaiAssetInTests is PSMTestBase { contract PSMPreviewSwapExactOut_SDaiAssetInTests is PSMTestBase { function test_previewSwapExactOut_sDaiToDai() public view { + // Demo rounding up + assertEq(psm.previewSwapExactOut(address(sDai), address(dai), 1e18 - 1), 0.8e18); + assertEq(psm.previewSwapExactOut(address(sDai), address(dai), 1e18), 0.8e18); + assertEq(psm.previewSwapExactOut(address(sDai), address(dai), 1e18 + 1), 0.8e18 + 1); + assertEq(psm.previewSwapExactOut(address(sDai), address(dai), 1.25e18), 1e18); assertEq(psm.previewSwapExactOut(address(sDai), address(dai), 2.5e18), 2e18); assertEq(psm.previewSwapExactOut(address(sDai), address(dai), 3.75e18), 3e18); @@ -250,12 +339,20 @@ contract PSMPreviewSwapExactOut_SDaiAssetInTests is PSMTestBase { mockRateProvider.__setConversionRate(conversionRate); - uint256 amountIn = amountOut * 1e27 / conversionRate; + uint256 expectedAmountIn = amountOut * 1e27 / conversionRate; + + uint256 amountIn = psm.previewSwapExactOut(address(sDai), address(dai), amountOut); - assertEq(psm.previewSwapExactOut(address(sDai), address(dai), amountOut), amountIn); + // Allow for rounding error of 1 unit upwards + assertLe(amountIn - expectedAmountIn, 1); } function test_previewSwapExactOut_sDaiToUsdc() public view { + // Demo rounding up + assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), 1e6 - 1), 0.8e18); + assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), 1e6), 0.8e18); + assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), 1e6 + 1), 0.800001e18); + assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), 1.25e6), 1e18); assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), 2.5e6), 2e18); assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), 3.75e6), 3e18); @@ -267,9 +364,12 @@ contract PSMPreviewSwapExactOut_SDaiAssetInTests is PSMTestBase { mockRateProvider.__setConversionRate(conversionRate); - uint256 amountIn = amountOut * 1e27 / conversionRate * 1e12; + uint256 expectedAmountIn = amountOut * 1e27 / conversionRate * 1e12; + + uint256 amountIn = psm.previewSwapExactOut(address(sDai), address(usdc), amountOut); - assertEq(psm.previewSwapExactOut(address(sDai), address(usdc), amountOut), amountIn); + // Allow for rounding error of 1e12 upwards + assertLe(amountIn - expectedAmountIn, 1e12); } }