Skip to content

Commit

Permalink
Merge branch 'feat/gsm-buidl' into feat/gsm-ustb
Browse files Browse the repository at this point in the history
  • Loading branch information
yan-man committed Oct 15, 2024
2 parents 9a686d3 + 0201eea commit a32f7ee
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 24 deletions.
50 changes: 32 additions & 18 deletions src/contracts/facilitators/gsm/converter/GsmConverter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -185,20 +185,20 @@ contract GsmConverter is Ownable, EIP712, IGsmConverter {

IGhoToken(GHO_TOKEN).transferFrom(originator, address(this), ghoAmount);
IGhoToken(GHO_TOKEN).approve(address(GSM), ghoAmount);
(uint256 issuedAssetAmount, uint256 ghoSold) = IGsm(GSM).buyAsset(minAmount, address(this));
(uint256 boughtAssetAmount, uint256 ghoSold) = IGsm(GSM).buyAsset(minAmount, address(this));
require(ghoAmount == ghoSold, 'INVALID_GHO_SOLD');
IGhoToken(GHO_TOKEN).approve(address(GSM), 0);

IERC20(ISSUED_ASSET).approve(address(REDEMPTION_CONTRACT), issuedAssetAmount);
IRedemption(REDEMPTION_CONTRACT).redeem(issuedAssetAmount);
// redeemedAssetAmount matches issuedAssetAmount because Redemption exchanges in 1:1 ratio
IERC20(ISSUED_ASSET).approve(address(REDEMPTION_CONTRACT), boughtAssetAmount);
IRedemption(REDEMPTION_CONTRACT).redeem(boughtAssetAmount);
// Redemption exchanges in 1:1 ratio between BUIDL and USDC
require(
IERC20(REDEEMED_ASSET).balanceOf(address(this)) ==
initialRedeemedAssetBalance + issuedAssetAmount,
initialRedeemedAssetBalance + boughtAssetAmount,
'INVALID_REDEMPTION'
);
IERC20(ISSUED_ASSET).approve(address(REDEMPTION_CONTRACT), 0);
IERC20(REDEEMED_ASSET).safeTransfer(receiver, issuedAssetAmount);
IERC20(REDEEMED_ASSET).safeTransfer(receiver, boughtAssetAmount);

require(
IGhoToken(GHO_TOKEN).balanceOf(address(this)) == initialGhoBalance,
Expand All @@ -209,8 +209,8 @@ contract GsmConverter is Ownable, EIP712, IGsmConverter {
'INVALID_REMAINING_ISSUED_ASSET_BALANCE'
);

emit BuyAssetThroughRedemption(originator, receiver, issuedAssetAmount, ghoSold);
return (issuedAssetAmount, ghoSold);
emit BuyAssetThroughRedemption(originator, receiver, boughtAssetAmount, ghoSold);
return (boughtAssetAmount, ghoSold);
}

/**
Expand All @@ -227,27 +227,37 @@ contract GsmConverter is Ownable, EIP712, IGsmConverter {
address receiver
) internal returns (uint256, uint256) {
uint256 initialGhoBalance = IGhoToken(GHO_TOKEN).balanceOf(address(this));
uint256 initialissuedAssetBalance = IERC20(ISSUED_ASSET).balanceOf(address(this));
uint256 initialIssuedAssetBalance = IERC20(ISSUED_ASSET).balanceOf(address(this));
uint256 initialRedeemedAssetBalance = IERC20(REDEEMED_ASSET).balanceOf(address(this));

(uint256 redeemedAssetAmount, , , ) = IGsm(GSM).getGhoAmountForSellAsset(maxAmount);
IERC20(REDEEMED_ASSET).transferFrom(originator, address(this), redeemedAssetAmount);
IERC20(REDEEMED_ASSET).approve(SUBSCRIPTION_CONTRACT, redeemedAssetAmount);
(uint256 assetAmount, , , ) = IGsm(GSM).getGhoAmountForSellAsset(maxAmount); // asset is BUIDL
IERC20(REDEEMED_ASSET).transferFrom(originator, address(this), assetAmount);
IERC20(REDEEMED_ASSET).approve(SUBSCRIPTION_CONTRACT, assetAmount);
//TODO: replace with proper issuance implementation later
MockBUIDLSubscription(SUBSCRIPTION_CONTRACT).issuance(redeemedAssetAmount);
MockBUIDLSubscription(SUBSCRIPTION_CONTRACT).issuance(assetAmount);
uint256 subscribedAssetAmount = IERC20(ISSUED_ASSET).balanceOf(address(this)) -
initialIssuedAssetBalance;
// TODO: probably will be fees from issuance, so need to adjust the logic
// only use this require only if preview of issuance is possible, otherwise it is redundant
require(
IERC20(ISSUED_ASSET).balanceOf(address(this)) ==
initialissuedAssetBalance + redeemedAssetAmount,
initialIssuedAssetBalance + subscribedAssetAmount,
'INVALID_ISSUANCE'
);
// reset approval after issuance
IERC20(REDEEMED_ASSET).approve(SUBSCRIPTION_CONTRACT, 0);

IERC20(ISSUED_ASSET).approve(GSM, redeemedAssetAmount);
(uint256 assetAmount, uint256 ghoBought) = IGsm(GSM).sellAsset(maxAmount, receiver);
// TODO: account for fees for sellAsset amount param
(assetAmount, , , ) = IGsm(GSM).getGhoAmountForSellAsset(subscribedAssetAmount); // recalculate based on actual issuance amount, < maxAmount
IERC20(ISSUED_ASSET).approve(GSM, assetAmount);
(uint256 soldAssetAmount, uint256 ghoBought) = IGsm(GSM).sellAsset(
subscribedAssetAmount,
receiver
);
// reset approval after sellAsset
IERC20(ISSUED_ASSET).approve(GSM, 0);

// by the end of the transaction, this contract should not retain any of the assets
require(
IGhoToken(GHO_TOKEN).balanceOf(address(this)) == initialGhoBalance,
'INVALID_REMAINING_GHO_BALANCE'
Expand All @@ -256,8 +266,12 @@ contract GsmConverter is Ownable, EIP712, IGsmConverter {
IERC20(REDEEMED_ASSET).balanceOf(address(this)) == initialRedeemedAssetBalance,
'INVALID_REMAINING_REDEEMED_ASSET_BALANCE'
);
require(
IERC20(ISSUED_ASSET).balanceOf(address(this)) == initialIssuedAssetBalance,
'INVALID_REMAINING_ISSUED_ASSET_BALANCE'
);

emit SellAssetThroughSubscription(originator, receiver, redeemedAssetAmount, ghoBought);
return (assetAmount, ghoBought);
emit SellAssetThroughSubscription(originator, receiver, soldAssetAmount, ghoBought);
return (soldAssetAmount, ghoBought);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,27 @@ interface IGsmConverter {
* @dev Emitted when a user buys an asset (selling GHO) in the GSM after a redemption
* @param originator The address of the buyer originating the request
* @param receiver The address of the receiver of the underlying asset
* @param issuedAssetAmount The amount of the issued asset converted
* @param boughtAssetAmount The amount of the asset bought
* @param ghoAmount The amount of total GHO sold, inclusive of fee
*/
event BuyAssetThroughRedemption(
address indexed originator,
address indexed receiver,
uint256 issuedAssetAmount,
uint256 boughtAssetAmount,
uint256 ghoAmount
);

/**
* @dev Emitted when a user sells an asset (buying GHO) in the GSM after an asset subscription
* @param originator The address of the seller originating the request
* @param receiver The address of the receiver of GHO
* @param redeemedAssetAmount The amount of the redeemed asset converted
* @param soldAssetAmount The amount of the asset sold
* @param ghoAmount The amount of GHO bought, inclusive of fee
*/
event SellAssetThroughSubscription(
address indexed originator,
address indexed receiver,
uint256 redeemedAssetAmount,
uint256 soldAssetAmount,
uint256 ghoAmount
);

Expand Down
5 changes: 3 additions & 2 deletions src/test/TestGsmConverter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -544,13 +544,14 @@ contract TestGsmConverter is TestGhoBase {
address(USDC_TOKEN)
);

vm.prank(FAUCET);
vm.startPrank(FAUCET);
USDC_TOKEN.mint(ALICE, DEFAULT_GSM_BUIDL_AMOUNT);

vm.startPrank(ALICE);
USDC_TOKEN.approve(address(gsmConverter), DEFAULT_GSM_BUIDL_AMOUNT);
vm.expectRevert('INVALID_ISSUANCE');
vm.expectRevert('INVALID_AMOUNT');
gsmConverter.sellAsset(DEFAULT_GSM_BUIDL_AMOUNT, ALICE);
vm.stopPrank();
}

function testRevertSellAssetInvalidRemainingGhoBalance() public {
Expand Down

0 comments on commit a32f7ee

Please sign in to comment.