From 8272ca6324dde8d3c1a5b60044b12ed1a48a5b70 Mon Sep 17 00:00:00 2001 From: YBM Date: Fri, 29 Nov 2024 18:40:20 -0600 Subject: [PATCH] chore: clean up dependencies, remove USTB test: try to isolate rounding issues for USDC converter --- .gitmodules | 5 +- lib/ustb | 1 - .../gsm/converter/USTBGsmConverter.sol | 45 ++++++++-------- .../gsm/dependencies/USTB/ISubscription.sol | 16 +++++- src/test/TestGhoBase.t.sol | 31 ++++++++++- src/test/TestUSTBGsmConverterEdge.t.sol | 53 ++++++++++--------- src/test/mocks/MockUSTBSubscription.sol | 4 +- 7 files changed, 94 insertions(+), 61 deletions(-) delete mode 160000 lib/ustb diff --git a/.gitmodules b/.gitmodules index 5f050423..7febbb46 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,7 +25,4 @@ url = https://github.com/aave/safety-module [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts - url = https://github.com/OpenZeppelin/openzeppelin-contracts -[submodule "lib/ustb"] - path = lib/ustb - url = https://github.com/superstateinc/ustb + url = https://github.com/OpenZeppelin/openzeppelin-contracts \ No newline at end of file diff --git a/lib/ustb b/lib/ustb deleted file mode 160000 index 39489395..00000000 --- a/lib/ustb +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 394893951a4da402928b58dcc8bbe4d576f485da diff --git a/src/contracts/facilitators/gsm/converter/USTBGsmConverter.sol b/src/contracts/facilitators/gsm/converter/USTBGsmConverter.sol index b3a28835..93152830 100644 --- a/src/contracts/facilitators/gsm/converter/USTBGsmConverter.sol +++ b/src/contracts/facilitators/gsm/converter/USTBGsmConverter.sol @@ -183,9 +183,8 @@ contract USTBGsmConverter is Ownable, EIP712, IGsmConverter { uint256 initialIssuedAssetBalance = IERC20(ISSUED_ASSET).balanceOf(address(this)); uint256 initialRedeemedAssetBalance = IERC20(REDEEMED_ASSET).balanceOf(address(this)); - (uint256 minUSTBAmount, ) = ISubscriptionRedemption(REDEMPTION_CONTRACT).calculateUstbIn( - minAmount - ); + // (uint256 minUSTBAmount, ) = ISubscription(REDEMPTION_CONTRACT).calculateUstbIn(minAmount); + uint256 minUSTBAmount = 0; (, uint256 ghoAmount, , ) = IGsm(GSM).getGhoAmountForBuyAsset(minUSTBAmount); @@ -196,21 +195,21 @@ contract USTBGsmConverter is Ownable, EIP712, IGsmConverter { IGhoToken(GHO_TOKEN).approve(address(GSM), 0); IERC20(ISSUED_ASSET).approve(address(REDEMPTION_CONTRACT), boughtAssetAmount); - ISubscriptionRedemption(REDEMPTION_CONTRACT).redeem(boughtAssetAmount); - IERC20(ISSUED_ASSET).approve(address(REDEMPTION_CONTRACT), 0); - IERC20(REDEEMED_ASSET).safeTransfer(receiver, minAmount); - - require( - IGhoToken(GHO_TOKEN).balanceOf(address(this)) == initialGhoBalance, - 'INVALID_REMAINING_GHO_BALANCE' - ); - require( - IERC20(ISSUED_ASSET).balanceOf(address(this)) == initialIssuedAssetBalance, - 'INVALID_REMAINING_ISSUED_ASSET_BALANCE' - ); - - emit BuyAssetThroughRedemption(originator, receiver, boughtAssetAmount, ghoSold); - return (boughtAssetAmount, ghoSold); + // ISubscription(REDEMPTION_CONTRACT).redeem(boughtAssetAmount); + // IERC20(ISSUED_ASSET).approve(address(REDEMPTION_CONTRACT), 0); + // IERC20(REDEEMED_ASSET).safeTransfer(receiver, minAmount); + + // require( + // IGhoToken(GHO_TOKEN).balanceOf(address(this)) == initialGhoBalance, + // 'INVALID_REMAINING_GHO_BALANCE' + // ); + // require( + // IERC20(ISSUED_ASSET).balanceOf(address(this)) == initialIssuedAssetBalance, + // 'INVALID_REMAINING_ISSUED_ASSET_BALANCE' + // ); + + // emit BuyAssetThroughRedemption(originator, receiver, boughtAssetAmount, ghoSold); + // return (boughtAssetAmount, ghoSold); } /** @@ -230,15 +229,14 @@ contract USTBGsmConverter is Ownable, EIP712, IGsmConverter { uint256 initialIssuedAssetBalance = IERC20(ISSUED_ASSET).balanceOf(address(this)); uint256 initialRedeemedAssetBalance = IERC20(REDEEMED_ASSET).balanceOf(address(this)); - (uint256 redeemedAssetAmount, , , ) = IGsm(GSM).getGhoAmountForSellAsset(maxAmount); // asset is BUIDL + (uint256 redeemedAssetAmount, , , ) = IGsm(GSM).getGhoAmountForSellAsset(maxAmount); // asset is USTB IERC20(REDEEMED_ASSET).transferFrom(originator, address(this), redeemedAssetAmount); IERC20(REDEEMED_ASSET).approve(SUBSCRIPTION_CONTRACT, redeemedAssetAmount); - (uint256 subscribedAssetAmount, , ) = IERC20(SUBSCRIPTION_CONTRACT).calculateSuperstateTokenOut( - redeemedAssetAmount, - REDEEMED_ASSET - ); + (uint256 subscribedAssetAmount, , ) = ISubscription(SUBSCRIPTION_CONTRACT) + .calculateSuperstateTokenOut(redeemedAssetAmount, REDEEMED_ASSET); ISubscription(SUBSCRIPTION_CONTRACT).subscribe(redeemedAssetAmount, REDEEMED_ASSET); + require( IERC20(ISSUED_ASSET).balanceOf(address(this)) == initialIssuedAssetBalance + subscribedAssetAmount, @@ -249,6 +247,7 @@ contract USTBGsmConverter is Ownable, EIP712, IGsmConverter { (redeemedAssetAmount, , , ) = IGsm(GSM).getGhoAmountForSellAsset(subscribedAssetAmount); // recalculate based on actual issuance amount, < maxAmount IERC20(ISSUED_ASSET).approve(GSM, redeemedAssetAmount); + (uint256 soldAssetAmount, uint256 ghoBought) = IGsm(GSM).sellAsset( subscribedAssetAmount, receiver diff --git a/src/contracts/facilitators/gsm/dependencies/USTB/ISubscription.sol b/src/contracts/facilitators/gsm/dependencies/USTB/ISubscription.sol index b4718594..4799272f 100644 --- a/src/contracts/facilitators/gsm/dependencies/USTB/ISubscription.sol +++ b/src/contracts/facilitators/gsm/dependencies/USTB/ISubscription.sol @@ -3,9 +3,21 @@ pragma solidity ^0.8.10; interface ISubscription { /** - * @notice Subscribes an amount of USTB in exchange for USDC - * @param amount The amount of USDC to subscribe + * @notice The ```subscribe``` function takes in stablecoins and mints SuperstateToken in the proper amount for the msg.sender depending on the current Net Asset Value per Share. + * @param inAmount The amount of the stablecoin in * @param stablecoin The address of the stablecoin to calculate with */ function subscribe(uint256 inAmount, address stablecoin) external; + + function calculateSuperstateTokenOut( + uint256 inAmount, + address stablecoin + ) + external + view + returns ( + uint256 superstateTokenOutAmount, + uint256 stablecoinInAmountAfterFee, + uint256 feeOnStablecoinInAmount + ); } diff --git a/src/test/TestGhoBase.t.sol b/src/test/TestGhoBase.t.sol index b9dd5cf7..a1ff5e50 100644 --- a/src/test/TestGhoBase.t.sol +++ b/src/test/TestGhoBase.t.sol @@ -96,6 +96,7 @@ import {IGhoCcipSteward} from '../contracts/misc/interfaces/IGhoCcipSteward.sol' import {GhoCcipSteward} from '../contracts/misc/GhoCcipSteward.sol'; import {GhoBucketSteward} from '../contracts/misc/GhoBucketSteward.sol'; import {GsmConverter} from '../contracts/facilitators/gsm/converter/GsmConverter.sol'; +import {USTBGsmConverter} from '../contracts/facilitators/gsm/converter/USTBGsmConverter.sol'; contract TestGhoBase is Test, Constants, Events { using WadRayMath for uint256; @@ -145,9 +146,12 @@ contract TestGhoBase is Test, Constants, Events { Gsm GHO_GSM; Gsm4626 GHO_GSM_4626; Gsm GHO_BUIDL_GSM; + Gsm GHO_USTB_GSM; GsmConverter GSM_CONVERTER; + USTBGsmConverter USTB_GSM_CONVERTER; FixedPriceStrategy GHO_GSM_FIXED_PRICE_STRATEGY; FixedPriceStrategy GHO_BUIDL_GSM_FIXED_PRICE_STRATEGY; + FixedPriceStrategy GHO_USTB_GSM_FIXED_PRICE_STRATEGY; FixedPriceStrategy4626 GHO_GSM_4626_FIXED_PRICE_STRATEGY; FixedFeeStrategy GHO_GSM_FIXED_FEE_STRATEGY; SampleLiquidator GHO_GSM_LAST_RESORT_LIQUIDATOR; @@ -295,7 +299,7 @@ contract TestGhoBase is Test, Constants, Events { ); GHO_USTB_GSM_FIXED_PRICE_STRATEGY = new FixedPriceStrategy( DEFAULT_FIXED_PRICE, - address(BUIDL_TOKEN), + address(USTB_TOKEN), 6 ); GHO_GSM_FIXED_FEE_STRATEGY = new FixedFeeStrategy(DEFAULT_GSM_BUY_FEE, DEFAULT_GSM_SELL_FEE); @@ -334,10 +338,24 @@ contract TestGhoBase is Test, Constants, Events { GHO_BUIDL_GSM = Gsm(address(buidlGsmProxy)); GHO_BUIDL_GSM.initialize(address(this), TREASURY, DEFAULT_GSM_BUIDL_EXPOSURE); + Gsm ustbGsm = new Gsm( + address(GHO_TOKEN), + address(USTB_TOKEN), + address(GHO_USTB_GSM_FIXED_PRICE_STRATEGY) + ); + AdminUpgradeabilityProxy ustbGsmProxy = new AdminUpgradeabilityProxy( + address(ustbGsm), + SHORT_EXECUTOR, + '' + ); + GHO_USTB_GSM = Gsm(address(ustbGsmProxy)); + GHO_USTB_GSM.initialize(address(this), TREASURY, DEFAULT_GSM_BUIDL_EXPOSURE); + GHO_GSM_FIXED_FEE_STRATEGY = new FixedFeeStrategy(DEFAULT_GSM_BUY_FEE, DEFAULT_GSM_SELL_FEE); GHO_GSM.updateFeeStrategy(address(GHO_GSM_FIXED_FEE_STRATEGY)); GHO_GSM_4626.updateFeeStrategy(address(GHO_GSM_FIXED_FEE_STRATEGY)); GHO_BUIDL_GSM.updateFeeStrategy(address(GHO_GSM_FIXED_FEE_STRATEGY)); + GHO_USTB_GSM.updateFeeStrategy(address(GHO_GSM_FIXED_FEE_STRATEGY)); GHO_GSM.grantRole(GSM_LIQUIDATOR_ROLE, address(GHO_GSM_LAST_RESORT_LIQUIDATOR)); GHO_GSM.grantRole(GSM_SWAP_FREEZER_ROLE, address(GHO_GSM_SWAP_FREEZER)); @@ -435,7 +453,16 @@ contract TestGhoBase is Test, Constants, Events { address(USDC_TOKEN) ); - USTB_SUBCRIPTION = new MockUSTBSubscription(address(USDC_TOKEN), address(USDC_TOKEN)); + // USTB + USTB_SUBCRIPTION = new MockUSTBSubscription(address(USTB_TOKEN), address(USDC_TOKEN), 1e8); + USTB_GSM_CONVERTER = new USTBGsmConverter( + address(this), + address(GHO_USTB_GSM), + address(BUIDL_USDC_REDEMPTION), + address(USTB_SUBCRIPTION), + address(USTB_TOKEN), + address(USDC_TOKEN) + ); } function ghoFaucet(address to, uint256 amount) public { diff --git a/src/test/TestUSTBGsmConverterEdge.t.sol b/src/test/TestUSTBGsmConverterEdge.t.sol index 5aa7070a..bc3c136c 100644 --- a/src/test/TestUSTBGsmConverterEdge.t.sol +++ b/src/test/TestUSTBGsmConverterEdge.t.sol @@ -5,7 +5,7 @@ import './TestGhoBase.t.sol'; contract TestGsmConverterEdge is TestGhoBase { function setUp() public { - setUSTBPrice(1_100_000_000); + USTB_SUBCRIPTION.setUSTBPrice(9.5e8); } /// @dev test buyAsset with zero fee to simulate errors seen by TokenLogic @@ -17,7 +17,7 @@ contract TestGsmConverterEdge is TestGhoBase { // Left: 99_999_999_000_000_000_000 // Right: 100_000_000_000_000_000_000 function testSellAssetZeroFee_roundingError() public { - GHO_BUIDL_GSM.updateFeeStrategy(address(0)); + GHO_USTB_GSM.updateFeeStrategy(address(0)); // Alice sells USDC for GHO // - GSM converter swaps USDC for BUIDL @@ -28,34 +28,35 @@ contract TestGsmConverterEdge is TestGhoBase { console2.log('GHO_TOKEN.balanceOf(ALICE) %e', GHO_TOKEN.balanceOf(ALICE)); - // vm.startPrank(FAUCET); - // // Supply BUIDL to issuance contract - // BUIDL_TOKEN.mint(address(BUIDL_USDC_ISSUANCE), DEFAULT_GSM_USDC_AMOUNT); - // vm.stopPrank(); + vm.startPrank(FAUCET); + // Supply USTB to issuance contract + USTB_TOKEN.mint(address(USTB_SUBCRIPTION), DEFAULT_GSM_USDC_AMOUNT * 100); + vm.stopPrank(); - // vm.startPrank(ALICE); - // USDC_TOKEN.approve(address(GSM_CONVERTER), DEFAULT_GSM_USDC_AMOUNT); + vm.startPrank(ALICE); + USTB_TOKEN.approve(address(USTB_GSM_CONVERTER), DEFAULT_GSM_USDC_AMOUNT * 100); + USDC_TOKEN.approve(address(USTB_GSM_CONVERTER), DEFAULT_GSM_USDC_AMOUNT); - // console2.log( - // 'BUIDL_TOKEN.balanceOf(address(GHO_BUIDL_GSM)) %e', - // BUIDL_TOKEN.balanceOf(address(GHO_BUIDL_GSM)) - // ); + console2.log( + 'USTB_TOKEN.balanceOf(address(GHO_USTB_GSM)) %e', + USTB_TOKEN.balanceOf(address(GHO_USTB_GSM)) + ); - // (uint256 assetAmount, uint256 ghoBought) = GSM_CONVERTER.sellAsset( - // DEFAULT_GSM_USDC_AMOUNT, - // ALICE - // ); - // vm.stopPrank(); + (uint256 assetAmount, uint256 ghoBought) = USTB_GSM_CONVERTER.sellAsset( + DEFAULT_GSM_USDC_AMOUNT, + ALICE + ); + vm.stopPrank(); - // console2.log('------after sellAsset------'); - // console2.log('calculated assetAmount %e', assetAmount); - // console2.log('calculated ghoBought %e', ghoBought); - // console2.log('GHO_TOKEN.balanceOf(ALICE) %e', GHO_TOKEN.balanceOf(ALICE)); - // console2.log('DEFAULT_GSM_GHO_AMOUNT %e', DEFAULT_GSM_GHO_AMOUNT); - // console2.log( - // 'BUIDL_TOKEN.balanceOf(address(GHO_BUIDL_GSM)) %e', - // BUIDL_TOKEN.balanceOf(address(GHO_BUIDL_GSM)) - // ); + console2.log('------after sellAsset------'); + console2.log('calculated assetAmount %e', assetAmount); + console2.log('calculated ghoBought %e', ghoBought); + console2.log('GHO_TOKEN.balanceOf(ALICE) %e', GHO_TOKEN.balanceOf(ALICE)); + console2.log('DEFAULT_GSM_GHO_AMOUNT %e', DEFAULT_GSM_GHO_AMOUNT); + console2.log( + 'USTB_TOKEN.balanceOf(address(GHO_USTB_GSM)) %e', + USTB_TOKEN.balanceOf(address(GHO_USTB_GSM)) + ); // assertEq(GHO_TOKEN.balanceOf(ALICE), DEFAULT_GSM_GHO_AMOUNT, 'Unexpected final GHO balance'); } diff --git a/src/test/mocks/MockUSTBSubscription.sol b/src/test/mocks/MockUSTBSubscription.sol index f465020f..0021d5e4 100644 --- a/src/test/mocks/MockUSTBSubscription.sol +++ b/src/test/mocks/MockUSTBSubscription.sol @@ -68,12 +68,10 @@ contract MockUSTBSubscription { uint256 feeOnStablecoinInAmount ) { - StablecoinConfig memory config = supportedStablecoins[stablecoin]; - feeOnStablecoinInAmount = 0; stablecoinInAmountAfterFee = inAmount - feeOnStablecoinInAmount; - usdPerSuperstateTokenChainlinkRaw = USTBPrice; // 9.5 USDC/SUPERSTATE_TOKEN + uint256 usdPerSuperstateTokenChainlinkRaw = USTBPrice; // 9.5 USDC/SUPERSTATE_TOKEN uint256 stablecoinPrecision = 10 ** 6; uint256 chainlinkFeedPrecision = 10 ** 8;