From 4a8be46389000f276ec39b49b897f868a221ddb2 Mon Sep 17 00:00:00 2001 From: danielalcarraz Date: Sun, 16 Feb 2025 14:49:21 -0500 Subject: [PATCH 01/10] add round up invariant to lp tokens function --- contracts/strategies/ShortStrategy.sol | 2 +- contracts/strategies/base/BaseStrategy.sol | 14 ++++++++++++-- package.json | 2 +- test/strategies/BaseStrategy.test.ts | 2 +- test/strategies/ShortStrategy.test.ts | 2 +- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/contracts/strategies/ShortStrategy.sol b/contracts/strategies/ShortStrategy.sol index 9ef4e16e..f8bc185e 100644 --- a/contracts/strategies/ShortStrategy.sol +++ b/contracts/strategies/ShortStrategy.sol @@ -61,7 +61,7 @@ abstract contract ShortStrategy is IShortStrategy, BaseStrategy { function getLatestBalances(uint256 lastFeeIndex, uint256 borrowedInvariant, uint256 lpBalance, uint256 lastCFMMInvariant, uint256 lastCFMMTotalSupply) public virtual override view returns(uint256 lastLPBalance, uint256 lastBorrowedLPBalance, uint256 lastBorrowedInvariant) { lastBorrowedInvariant = accrueBorrowedInvariant(borrowedInvariant, lastFeeIndex); - lastBorrowedLPBalance = convertInvariantToLP(lastBorrowedInvariant, lastCFMMTotalSupply, lastCFMMInvariant); + lastBorrowedLPBalance = convertInvariantToLPRoundUp(lastBorrowedInvariant, lastCFMMTotalSupply, lastCFMMInvariant); lastLPBalance = lpBalance + lastBorrowedLPBalance; } diff --git a/contracts/strategies/base/BaseStrategy.sol b/contracts/strategies/base/BaseStrategy.sol index 6bd90cf9..5c227b2c 100644 --- a/contracts/strategies/base/BaseStrategy.sol +++ b/contracts/strategies/base/BaseStrategy.sol @@ -171,7 +171,7 @@ abstract contract BaseStrategy is AppStorage, AbstractRateModel { return borrowedInvariant * lastFeeIndex / 1e18; } - /// @notice Convert CFMM LP tokens into liquidity invariant units. + /// @notice Convert liquidity invariant units into CFMM LP tokens. /// @dev In case of CFMM where convertInvariantToLP calculation is different from convertLPToInvariant /// @param liquidityInvariant - liquidity invariant borrowed in the GammaPool /// @param lastCFMMTotalSupply - total supply of LP tokens issued by CFMM @@ -181,6 +181,16 @@ abstract contract BaseStrategy is AppStorage, AbstractRateModel { return lastCFMMInvariant == 0 ? 0 : (liquidityInvariant * lastCFMMTotalSupply) / lastCFMMInvariant; } + /// @notice Convert liquidity invariant units into CFMM LP tokens rounded up. + /// @dev In case of CFMM where convertInvariantToLP calculation is different from convertLPToInvariant + /// @param liquidityInvariant - liquidity invariant borrowed in the GammaPool + /// @param lastCFMMTotalSupply - total supply of LP tokens issued by CFMM + /// @param lastCFMMInvariant - liquidity invariant in CFMM + /// @return lpTokens - liquidity invariant in terms of LP tokens + function convertInvariantToLPRoundUp(uint256 liquidityInvariant, uint256 lastCFMMTotalSupply, uint256 lastCFMMInvariant) internal virtual pure returns(uint256) { + return lastCFMMInvariant == 0 ? 0 : (liquidityInvariant * lastCFMMTotalSupply + (lastCFMMInvariant - 1)) / lastCFMMInvariant; + } + /// @notice Convert CFMM LP tokens into liquidity invariant units. /// @dev In case of CFMM where convertLPToInvariant calculation is different from convertInvariantToLP /// @param lpTokens - liquidity invariant borrowed in the GammaPool @@ -215,7 +225,7 @@ abstract contract BaseStrategy is AppStorage, AbstractRateModel { s.BORROWED_INVARIANT = uint128(newBorrowedInvariant); // Convert borrowed liquidity to corresponding CFMM LP tokens using current conversion rate - s.LP_TOKEN_BORROWED_PLUS_INTEREST = convertInvariantToLP(newBorrowedInvariant, lastCFMMTotalSupply, lastCFMMInvariant); + s.LP_TOKEN_BORROWED_PLUS_INTEREST = convertInvariantToLPRoundUp(newBorrowedInvariant, lastCFMMTotalSupply, lastCFMMInvariant); uint256 lpInvariant = convertLPToInvariant(s.LP_TOKEN_BALANCE, lastCFMMInvariant, lastCFMMTotalSupply); s.LP_INVARIANT = uint128(lpInvariant); diff --git a/package.json b/package.json index 0b70242c..11ea1028 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@gammaswap/v1-core", - "version": "1.2.13", + "version": "1.2.14", "description": "Core smart contracts for the GammaSwap V1 protocol", "homepage": "https://gammaswap.com", "scripts": { diff --git a/test/strategies/BaseStrategy.test.ts b/test/strategies/BaseStrategy.test.ts index 71be228b..2f31a0ce 100644 --- a/test/strategies/BaseStrategy.test.ts +++ b/test/strategies/BaseStrategy.test.ts @@ -145,7 +145,7 @@ describe("BaseStrategy", function () { if (lastCFMMInvariant.eq(0)) { return BigNumber.from(0); } - return borrowedInvariant.mul(lastCFMMTotalSupply).div(lastCFMMInvariant); + return borrowedInvariant.mul(lastCFMMTotalSupply).add(lastCFMMInvariant.sub(1)).div(lastCFMMInvariant); } function calcLPInvariant( diff --git a/test/strategies/ShortStrategy.test.ts b/test/strategies/ShortStrategy.test.ts index 2acb5206..986ef7d3 100644 --- a/test/strategies/ShortStrategy.test.ts +++ b/test/strategies/ShortStrategy.test.ts @@ -849,7 +849,7 @@ describe("ShortStrategy", function () { ); const expTotAssets = params1.lpBalance.add( - params1.borrowedInvariant.mul(cfmmTotalSupply1).div(cfmmInvariant1) + params1.borrowedInvariant.mul(cfmmTotalSupply1).add(cfmmInvariant1.sub(1)).div(cfmmInvariant1) ); expect(cfmmTotalSupply1).to.equal(cfmmTotalSupply0); From 11d804e9c9bfd651c202c85edb61d6aa38f41273 Mon Sep 17 00:00:00 2001 From: danielalcarraz Date: Sun, 16 Feb 2025 15:00:01 -0500 Subject: [PATCH 02/10] update .gitmodules to v1.5.3 --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index e977ca25..140d7df9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std - branch = v1.5.5 + branch = v1.5.3 From 911f7e92870f322610feec6d9e75731de4fc5f30 Mon Sep 17 00:00:00 2001 From: danielalcarraz Date: Sun, 16 Feb 2025 15:09:52 -0500 Subject: [PATCH 03/10] use local forge-std --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 140d7df9..1a9f172a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "lib/forge-std"] path = lib/forge-std - url = https://github.com/foundry-rs/forge-std - branch = v1.5.3 + #url = https://github.com/foundry-rs/forge-std + #branch = v1.5.3 From d280e867211c03182c84591048f6e346dd1fcb08 Mon Sep 17 00:00:00 2001 From: danielalcarraz Date: Sun, 16 Feb 2025 15:14:47 -0500 Subject: [PATCH 04/10] update forge-std path --- .gitmodules | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitmodules b/.gitmodules index 1a9f172a..55e255cc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,5 @@ [submodule "lib/forge-std"] path = lib/forge-std + url = ../lib/forge-std #url = https://github.com/foundry-rs/forge-std #branch = v1.5.3 From 053858604e2ac1b1b9d09fda11e34908e1c3beed Mon Sep 17 00:00:00 2001 From: danielalcarraz Date: Sun, 16 Feb 2025 15:21:41 -0500 Subject: [PATCH 05/10] update .gitmodules --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 55e255cc..8357bfa4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,5 +1,5 @@ [submodule "lib/forge-std"] path = lib/forge-std - url = ../lib/forge-std + url = ./lib/forge-std #url = https://github.com/foundry-rs/forge-std #branch = v1.5.3 From e5cc10d9cf35b1d1c7e7862d758b2242c5ad6927 Mon Sep 17 00:00:00 2001 From: danielalcarraz Date: Sun, 16 Feb 2025 15:29:47 -0500 Subject: [PATCH 06/10] prevent forge updates --- .gitmodules | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index 8357bfa4..eaf4450a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,5 +1,5 @@ [submodule "lib/forge-std"] - path = lib/forge-std - url = ./lib/forge-std - #url = https://github.com/foundry-rs/forge-std - #branch = v1.5.3 + path = lib/forge-std + url = https://github.com/foundry-rs/forge-std + branch = v1.5.3 + update = none # Prevents automatic updates \ No newline at end of file From 5efec77edbbbc9134d20d4622b456c8ebafef464 Mon Sep 17 00:00:00 2001 From: danielalcarraz Date: Sun, 16 Feb 2025 15:34:30 -0500 Subject: [PATCH 07/10] update forge module --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index eaf4450a..2209ed23 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,5 +1,5 @@ [submodule "lib/forge-std"] path = lib/forge-std - url = https://github.com/foundry-rs/forge-std + url = https://github.com/foundry-rs/forge-std.git branch = v1.5.3 update = none # Prevents automatic updates \ No newline at end of file From 81b28cd767d0b4fa4715f1c5c71c1572fdf36d2a Mon Sep 17 00:00:00 2001 From: danielalcarraz Date: Sun, 16 Feb 2025 15:39:48 -0500 Subject: [PATCH 08/10] comment .gitmodules --- .gitmodules | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.gitmodules b/.gitmodules index 2209ed23..11192182 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,5 +1,4 @@ -[submodule "lib/forge-std"] - path = lib/forge-std - url = https://github.com/foundry-rs/forge-std.git - branch = v1.5.3 - update = none # Prevents automatic updates \ No newline at end of file +#[submodule "lib/forge-std"] +# path = lib/forge-std +# url = https://github.com/foundry-rs/forge-std +# branch = v1.5.5 \ No newline at end of file From 1bcc6051c9d4fc8de08f697056b76f3a3ef1ae4f Mon Sep 17 00:00:00 2001 From: danielalcarraz Date: Sun, 16 Feb 2025 15:53:41 -0500 Subject: [PATCH 09/10] update .gitmodule --- .gitmodules | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index 11192182..dd3f0228 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ -#[submodule "lib/forge-std"] -# path = lib/forge-std -# url = https://github.com/foundry-rs/forge-std -# branch = v1.5.5 \ No newline at end of file +[submodule "lib/forge-std"] + path = lib/forge-std + url = https://github.com/foundry-rs/forge-std + branch = v1.6.0 \ No newline at end of file From 917c13b2d65e6a8ced4900d7d01ffcccfd6eed79 Mon Sep 17 00:00:00 2001 From: danielalcarraz Date: Sun, 16 Feb 2025 17:59:50 -0500 Subject: [PATCH 10/10] updates for new foundry version --- package.json | 2 +- test/foundry/RateParamsStore.t.sol | 18 ++++++--- test/foundry/TokenMetaData.t.sol | 61 ++++++++++++++++++++---------- 3 files changed, 55 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index 11ea1028..b9480f7e 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "homepage": "https://gammaswap.com", "scripts": { "test": "hardhat test", - "fuzz": "forge test" + "fuzz": "forge test --via-ir" }, "engines": { "node": ">=18" diff --git a/test/foundry/RateParamsStore.t.sol b/test/foundry/RateParamsStore.t.sol index 4a8c63fe..8d0bccfb 100644 --- a/test/foundry/RateParamsStore.t.sol +++ b/test/foundry/RateParamsStore.t.sol @@ -62,19 +62,22 @@ contract RateParamsStoreTest is Test { TestLinearKinkedRateModel(address(rateModel2)).setRateParamsStore(address(paramsStore)); } - function testFailSetRateParams() public { + function testSetRateParamsError() public { TestParams memory params = TestParams({ num1: 10, num2: 20, num3: 40 }); vm.prank(addr1); + vm.expectRevert("FORBIDDEN"); paramsStore.setRateParams(address(rateModel), abi.encode(params), false); } - function testFailSetRateParams1() public { + function testSetRateParamsError1() public { TestFailParams1 memory params = TestFailParams1({ num1: 12345 }); + vm.expectRevert("VALIDATE"); paramsStore.setRateParams(address(rateModel), abi.encode(params), false); } - function testFailSetRateParams2() public { + function testSetRateParamsError2() public { TestFailParams2 memory params = TestFailParams2({ num1: 100, num2: 200, num3: 1e36 }); + vm.expectRevert("VALIDATE"); paramsStore.setRateParams(address(rateModel), abi.encode(params), false); } @@ -118,19 +121,22 @@ contract RateParamsStoreTest is Test { assertEq(maxApy, 40); } - function testFailSetRate2Params() public { + function testSetRate2ParamsError() public { TestParams2 memory params = TestParams2({ num1: 10, num2: 20, num3: 40 , num4: 50}); vm.prank(addr1); + vm.expectRevert("FORBIDDEN"); paramsStore.setRateParams(address(rateModel2), abi.encode(params), false); } - function testFailSetRate2Params1() public { + function testSetRate2ParamsError1() public { TestFail2Params1 memory params = TestFail2Params1({ num1: 12345 }); + vm.expectRevert("VALIDATE"); paramsStore.setRateParams(address(rateModel2), abi.encode(params), false); } - function testFailSetRate2Params2() public { + function testSetRate2ParamsError2() public { TestFail2Params2 memory params = TestFail2Params2({ num1: 100, num2: 200, num3: 1e18, num4: 1e38 }); + vm.expectRevert("VALIDATE"); paramsStore.setRateParams(address(rateModel2), abi.encode(params), false); } diff --git a/test/foundry/TokenMetaData.t.sol b/test/foundry/TokenMetaData.t.sol index 8248c60a..87a05090 100644 --- a/test/foundry/TokenMetaData.t.sol +++ b/test/foundry/TokenMetaData.t.sol @@ -15,21 +15,25 @@ contract TokenMetaDataTest is Test { Token2 token2 = new Token2(); Token3 token3 = new Token3(); - function testFailDecimalsToken1() public { - GammaSwapLibrary.decimals(address(token1)); + function testDecimalsToken1Error() public { + vm.expectRevert(); + token1.getDecimals(); } - function testFailDecimals256() public { + function testDecimals256Error() public { Token4 token = new Token4(256); - GammaSwapLibrary.decimals(address(token)); + vm.expectRevert(); + token.getDecimals(); } - function testFailTokenDecimalsToken1() public { + function testTokenDecimalsToken1Error() public { + vm.expectRevert(); viewer.getTokenDecimals(address(token1)); } - function testFailTokenDecimals256() public { + function testTokenDecimals256Error() public { Token4 token = new Token4(256); + vm.expectRevert(); viewer.getTokenDecimals(address(token)); } @@ -49,12 +53,14 @@ contract TokenMetaDataTest is Test { assertEq(viewer.getTokenDecimals(address(token3)),6); } - function testFailNameToken1() public { - GammaSwapLibrary.name(address(token1)); + function testNameToken1Fail() public { + vm.expectRevert(); + token1.getName(); } - function testFailNameToken3() public { - GammaSwapLibrary.name(address(token3)); + function testNameToken3Fail() public { + vm.expectRevert(); + token3.getName(); } function testName() public { @@ -67,12 +73,14 @@ contract TokenMetaDataTest is Test { assertEq(viewer.getTokenName(address(token3)),''); } - function testFailSymbolToken1() public { - GammaSwapLibrary.symbol(address(token1)); + function testSymbolToken1Fail() public { + vm.expectRevert(); + token1.getSymbol(); } - function testFailSymbolToken3() public { - GammaSwapLibrary.symbol(address(token3)); + function testSymbolToken3Fail() public { + vm.expectRevert(); + token3.getSymbol(); } function testSymbol() external { @@ -86,7 +94,22 @@ contract TokenMetaDataTest is Test { } } -contract Token0 { +abstract contract AbstractToken { + + function getName() external view returns(string memory) { + return GammaSwapLibrary.name(address(this)); + } + + function getSymbol() external view returns(string memory) { + return GammaSwapLibrary.symbol(address(this)); + } + + function getDecimals() external view returns(uint8) { + return GammaSwapLibrary.decimals(address(this)); + } +} + +contract Token0 is AbstractToken { string public name = 'Test Token0'; string public symbol = 'TERC0'; @@ -97,7 +120,7 @@ contract Token0 { } -contract Token1 { +contract Token1 is AbstractToken { bytes32 public name = 'Test Token1'; bytes32 public symbol = 'TERC1'; @@ -108,7 +131,7 @@ contract Token1 { } -contract Token2 { +contract Token2 is AbstractToken { bytes public name = 'Test Token2'; bytes public symbol = 'TERC2'; @@ -119,7 +142,7 @@ contract Token2 { } -contract Token3 { +contract Token3 is AbstractToken { uint256 public name = 513; uint256 public symbol = 354; @@ -130,7 +153,7 @@ contract Token3 { } -contract Token4 { +contract Token4 is AbstractToken { string public name = 'Test Token0'; string public symbol = 'TERC0';