diff --git a/src/contracts/facilitators/gsm/converter/GsmConverter.sol b/src/contracts/facilitators/gsm/converter/GsmConverter.sol index b74a90a8..a170eec2 100644 --- a/src/contracts/facilitators/gsm/converter/GsmConverter.sol +++ b/src/contracts/facilitators/gsm/converter/GsmConverter.sol @@ -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, @@ -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); } /** @@ -227,29 +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); // TODO: account for fees for sellAsset amount param - IERC20(ISSUED_ASSET).approve(GSM, redeemedAssetAmount); - (uint256 assetAmount, uint256 ghoBought) = IGsm(GSM).sellAsset(maxAmount, receiver); + (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' @@ -258,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); } } diff --git a/src/contracts/facilitators/gsm/converter/interfaces/IGsmConverter.sol b/src/contracts/facilitators/gsm/converter/interfaces/IGsmConverter.sol index 7f2c7ee0..ebc7580d 100644 --- a/src/contracts/facilitators/gsm/converter/interfaces/IGsmConverter.sol +++ b/src/contracts/facilitators/gsm/converter/interfaces/IGsmConverter.sol @@ -12,13 +12,13 @@ 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 ); @@ -26,13 +26,13 @@ interface IGsmConverter { * @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 ); diff --git a/src/test/TestGsmConverter.t.sol b/src/test/TestGsmConverter.t.sol index b594e640..e2de64ff 100644 --- a/src/test/TestGsmConverter.t.sol +++ b/src/test/TestGsmConverter.t.sol @@ -549,7 +549,7 @@ contract TestGsmConverter is TestGhoBase { 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); }