diff --git a/.gitmodules b/.gitmodules index e977ca2..dd3f022 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 + path = lib/forge-std + url = https://github.com/foundry-rs/forge-std + branch = v1.6.0 \ No newline at end of file diff --git a/contracts/strategies/ShortStrategy.sol b/contracts/strategies/ShortStrategy.sol index 9ef4e16..f8bc185 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 6bd90cf..5c227b2 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 0b70242..b9480f7 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "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": { "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 4a8c63f..8d0bccf 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 8248c60..87a0509 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'; diff --git a/test/strategies/BaseStrategy.test.ts b/test/strategies/BaseStrategy.test.ts index 71be228..2f31a0c 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 2acb520..986ef7d 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);