Skip to content

Commit

Permalink
fix: Fix rounding of Gsm4626 price strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelmtzinf committed Dec 12, 2023
1 parent f3cf2e1 commit f32a647
Show file tree
Hide file tree
Showing 8 changed files with 277 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {IGsmFeeStrategy} from './interfaces/IGsmFeeStrategy.sol';
contract FixedFeeStrategy is IGsmFeeStrategy {
using Math for uint256;

uint256 internal constant MAXIMUM_FEE_PERCENT = 5000;

uint256 internal immutable _buyFee;
uint256 internal immutable _sellFee;

Expand All @@ -23,8 +25,8 @@ contract FixedFeeStrategy is IGsmFeeStrategy {
* @param sellFee The fee paid when selling the underlying asset in exchange for GHO, expressed in bps
*/
constructor(uint256 buyFee, uint256 sellFee) {
require(buyFee < 5000, 'INVALID_BUY_FEE');
require(sellFee < 5000, 'INVALID_SELL_FEE');
require(buyFee < MAXIMUM_FEE_PERCENT, 'INVALID_BUY_FEE');
require(sellFee < MAXIMUM_FEE_PERCENT, 'INVALID_SELL_FEE');
require(buyFee > 0 || sellFee > 0, 'MUST_HAVE_ONE_NONZERO_FEE');
_buyFee = buyFee;
_sellFee = sellFee;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ contract FixedPriceStrategy4626 is IGsmPriceStrategy {

/// @inheritdoc IGsmPriceStrategy
function getAssetPriceInGho(uint256 assetAmount, bool roundUp) external view returns (uint256) {
// conversion from 4626 shares to 4626 assets (rounding down)
uint256 vaultAssets = IERC4626(UNDERLYING_ASSET).previewRedeem(assetAmount);
// conversion from 4626 shares to 4626 assets
uint256 vaultAssets = roundUp
? IERC4626(UNDERLYING_ASSET).previewMint(assetAmount) // round up
: IERC4626(UNDERLYING_ASSET).convertToAssets(assetAmount); // round down
return
vaultAssets.mulDiv(
PRICE_RATIO,
Expand All @@ -62,7 +64,10 @@ contract FixedPriceStrategy4626 is IGsmPriceStrategy {
PRICE_RATIO,
roundUp ? Math.Rounding.Up : Math.Rounding.Down
);
// conversion of 4626 assets to the number of 4626 shares burned to pull that amount
return IERC4626(UNDERLYING_ASSET).previewWithdraw(vaultAssets);
// conversion of 4626 assets to 4626 shares
return
roundUp
? IERC4626(UNDERLYING_ASSET).previewWithdraw(vaultAssets) // round up
: IERC4626(UNDERLYING_ASSET).convertToShares(vaultAssets); // round down
}
}
21 changes: 18 additions & 3 deletions src/test/TestGhoBase.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -565,8 +565,8 @@ contract TestGhoBase is Test, Constants, Events {
return ghoBought;
}

/// Helper function to mint an amount of shares of an ERC4626 token
function _mintShares(
/// Helper function to mint an amount of assets of an ERC4626 token
function _mintVaultAssets(
MockERC4626 vault,
TestnetERC20 token,
address receiver,
Expand All @@ -579,6 +579,21 @@ contract TestGhoBase is Test, Constants, Events {
vm.stopPrank();
}

/// Helper function to mint an amount of shares of an ERC4626 token
function _mintVaultShares(
MockERC4626 vault,
TestnetERC20 token,
address receiver,
uint256 sharesAmount
) internal {
uint256 assets = vault.previewMint(sharesAmount);
vm.startPrank(FAUCET);
token.mint(FAUCET, assets);
token.approve(address(vault), assets);
vault.deposit(assets, receiver);
vm.stopPrank();
}

/// Helper function to sell shares of an ERC4626 token in the GSM
function _sellAsset(
Gsm4626 gsm,
Expand All @@ -588,7 +603,7 @@ contract TestGhoBase is Test, Constants, Events {
uint256 amount
) internal returns (uint256) {
uint256 assetsToMint = vault.previewRedeem(amount);
_mintShares(vault, token, address(this), assetsToMint);
_mintVaultAssets(vault, token, address(this), assetsToMint);
vault.approve(address(gsm), amount);
(, uint256 ghoBought) = gsm.sellAsset(amount, receiver);
return ghoBought;
Expand Down
50 changes: 25 additions & 25 deletions src/test/TestGsm4626.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ contract TestGsm4626 is TestGhoBase {
emit FeeStrategyUpdated(address(GHO_GSM_FIXED_FEE_STRATEGY), address(0));
GHO_GSM_4626.updateFeeStrategy(address(0));

_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);

Expand All @@ -91,7 +91,7 @@ contract TestGsm4626 is TestGhoBase {
uint256 fee = DEFAULT_GSM_GHO_AMOUNT.percentMul(DEFAULT_GSM_SELL_FEE);
uint256 ghoOut = DEFAULT_GSM_GHO_AMOUNT - fee;

_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
assertEq(
USDC_4626_TOKEN.previewRedeem(USDC_4626_TOKEN.balanceOf(ALICE)),
DEFAULT_GSM_USDC_AMOUNT
Expand Down Expand Up @@ -128,7 +128,7 @@ contract TestGsm4626 is TestGhoBase {
uint256 fee = DEFAULT_GSM_GHO_AMOUNT.percentMul(DEFAULT_GSM_SELL_FEE);
uint256 ghoOut = DEFAULT_GSM_GHO_AMOUNT - fee;

_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);

vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
Expand All @@ -154,7 +154,7 @@ contract TestGsm4626 is TestGhoBase {
gsm.initialize(address(this), TREASURY, DEFAULT_GSM_USDC_EXPOSURE - 1);
GHO_TOKEN.addFacilitator(address(gsm), 'GSM Modified Exposure Cap', DEFAULT_CAPACITY);

_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_EXPOSURE);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_EXPOSURE);

vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(gsm), DEFAULT_GSM_USDC_EXPOSURE);
Expand Down Expand Up @@ -234,7 +234,7 @@ contract TestGsm4626 is TestGhoBase {
GHO_GSM_4626.updateFeeStrategy(address(0));

// Supply assets to the GSM first
_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
vm.expectEmit(true, true, true, true, address(GHO_GSM_4626));
Expand Down Expand Up @@ -267,7 +267,7 @@ contract TestGsm4626 is TestGhoBase {
uint256 ghoOut = DEFAULT_GSM_GHO_AMOUNT - sellFee;

// Supply assets to the GSM first
_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
vm.expectEmit(true, true, true, true, address(GHO_GSM_4626));
Expand Down Expand Up @@ -311,7 +311,7 @@ contract TestGsm4626 is TestGhoBase {
uint256 ghoOut = DEFAULT_GSM_GHO_AMOUNT - sellFee;

// Supply assets to the GSM first
_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
vm.expectEmit(true, true, true, true, address(GHO_GSM_4626));
Expand Down Expand Up @@ -354,7 +354,7 @@ contract TestGsm4626 is TestGhoBase {
GHO_GSM_4626.updateFeeStrategy(address(0));

// Supply assets to the GSM first
_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_EXPOSURE);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_EXPOSURE);
vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_EXPOSURE);
vm.expectEmit(true, true, true, true, address(GHO_GSM_4626));
Expand Down Expand Up @@ -416,7 +416,7 @@ contract TestGsm4626 is TestGhoBase {
uint256 buyFee = DEFAULT_GSM_GHO_AMOUNT.percentMul(DEFAULT_GSM_BUY_FEE);

// Supply assets to the GSM first
_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
vm.expectEmit(true, true, true, true, address(GHO_GSM_4626));
Expand All @@ -436,7 +436,7 @@ contract TestGsm4626 is TestGhoBase {
uint256 buyFee = DEFAULT_GSM_GHO_AMOUNT.percentMul(DEFAULT_GSM_BUY_FEE);

// Supply assets to the GSM first
_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
vm.expectEmit(true, true, true, true, address(GHO_GSM_4626));
Expand Down Expand Up @@ -710,7 +710,7 @@ contract TestGsm4626 is TestGhoBase {
uint256 fee = DEFAULT_GSM_GHO_AMOUNT.percentMul(DEFAULT_GSM_SELL_FEE);
assertGt(fee, 0, 'Fee not greater than zero');

_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
vm.startPrank(ALICE);

USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
Expand Down Expand Up @@ -749,7 +749,7 @@ contract TestGsm4626 is TestGhoBase {
function testRescueUnderlyingTokens() public {
GHO_GSM_4626.grantRole(GSM_TOKEN_RESCUER_ROLE, address(this));

_mintShares(USDC_4626_TOKEN, USDC_TOKEN, address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);

assertEq(USDC_4626_TOKEN.balanceOf(ALICE), 0, 'Unexpected USDC balance before');
vm.expectEmit(true, true, true, true, address(GHO_GSM_4626));
Expand All @@ -765,7 +765,7 @@ contract TestGsm4626 is TestGhoBase {
function testRescueUnderlyingTokensWithAccruedFees() public {
GHO_GSM_4626.grantRole(GSM_TOKEN_RESCUER_ROLE, address(this));

_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
GHO_GSM_4626.sellAsset(DEFAULT_GSM_USDC_AMOUNT, ALICE);
Expand All @@ -778,7 +778,7 @@ contract TestGsm4626 is TestGhoBase {
'Unexpected GSM USDC balance before'
);

_mintShares(USDC_4626_TOKEN, USDC_TOKEN, address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);

assertEq(
USDC_4626_TOKEN.balanceOf(address(GHO_GSM_4626)),
Expand Down Expand Up @@ -812,7 +812,7 @@ contract TestGsm4626 is TestGhoBase {
function testSeize() public {
assertEq(GHO_GSM_4626.getIsSeized(), false, 'Unexpected seize status before');

_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
GHO_GSM_4626.sellAsset(DEFAULT_GSM_USDC_AMOUNT, ALICE);
Expand Down Expand Up @@ -844,7 +844,7 @@ contract TestGsm4626 is TestGhoBase {
}

function testRevertMethodsAfterSeizure() public {
_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
GHO_GSM_4626.sellAsset(DEFAULT_GSM_USDC_AMOUNT, ALICE);
Expand All @@ -871,7 +871,7 @@ contract TestGsm4626 is TestGhoBase {
}

function testBurnAfterSeize() public {
_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
GHO_GSM_4626.sellAsset(DEFAULT_GSM_USDC_AMOUNT, ALICE);
Expand Down Expand Up @@ -899,7 +899,7 @@ contract TestGsm4626 is TestGhoBase {
}

function testBurnAfterSeizeGreaterAmount() public {
_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
GHO_GSM_4626.sellAsset(DEFAULT_GSM_USDC_AMOUNT, ALICE);
Expand Down Expand Up @@ -939,7 +939,7 @@ contract TestGsm4626 is TestGhoBase {
}

function testInjectGho() public {
_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);

vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
Expand Down Expand Up @@ -979,7 +979,7 @@ contract TestGsm4626 is TestGhoBase {
}

function testInjectGhoMoreThanNeeded() public {
_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);

vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
Expand Down Expand Up @@ -1015,7 +1015,7 @@ contract TestGsm4626 is TestGhoBase {
}

function testInjectUnderlying() public {
_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);

vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
Expand All @@ -1034,7 +1034,7 @@ contract TestGsm4626 is TestGhoBase {

GHO_GSM_4626.grantRole(GSM_CONFIGURATOR_ROLE, BOB);

_mintShares(USDC_4626_TOKEN, USDC_TOKEN, BOB, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, BOB, DEFAULT_GSM_USDC_AMOUNT);

vm.startPrank(BOB);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
Expand All @@ -1056,7 +1056,7 @@ contract TestGsm4626 is TestGhoBase {
}

function testInjectUnderlyingMoreThanNeeded() public {
_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);

vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
Expand All @@ -1075,7 +1075,7 @@ contract TestGsm4626 is TestGhoBase {

GHO_GSM_4626.grantRole(GSM_CONFIGURATOR_ROLE, BOB);

_mintShares(USDC_4626_TOKEN, USDC_TOKEN, BOB, DEFAULT_GSM_USDC_AMOUNT + 1);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, BOB, DEFAULT_GSM_USDC_AMOUNT + 1);

vm.startPrank(BOB);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT + 1);
Expand Down Expand Up @@ -1125,7 +1125,7 @@ contract TestGsm4626 is TestGhoBase {
function testDistributeFeesToTreasury() public {
uint256 fee = DEFAULT_GSM_GHO_AMOUNT.percentMul(DEFAULT_GSM_SELL_FEE);

_mintShares(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
_mintVaultAssets(USDC_4626_TOKEN, USDC_TOKEN, ALICE, DEFAULT_GSM_USDC_AMOUNT);
vm.startPrank(ALICE);
USDC_4626_TOKEN.approve(address(GHO_GSM_4626), DEFAULT_GSM_USDC_AMOUNT);
vm.expectEmit(true, true, true, true, address(GHO_GSM_4626));
Expand Down
Loading

0 comments on commit f32a647

Please sign in to comment.