Skip to content

Commit 9209e59

Browse files
committed
feat: integrate USTB contracts
1 parent d85915b commit 9209e59

File tree

6 files changed

+127
-108
lines changed

6 files changed

+127
-108
lines changed

src/contracts/facilitators/gsm/converter/USTBGsmConverter.sol

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {IGhoToken} from '../../../gho/interfaces/IGhoToken.sol';
1010
import {IGsm} from '../interfaces/IGsm.sol';
1111
import {IGsmConverter} from './interfaces/IGsmConverter.sol';
1212
// TODO: replace with proper issuance implementation/interface later from USTB
13-
import {ISubscriptionRedemption} from '../dependencies/USTB/ISubscriptionRedemption.sol';
13+
import {ISubscription} from '../dependencies/USTB/ISubscription.sol';
1414
import {MockBUIDLSubscription} from '../../../../test/mocks/MockBUIDLSubscription.sol';
1515

1616
import 'forge-std/console2.sol';
@@ -230,15 +230,15 @@ contract USTBGsmConverter is Ownable, EIP712, IGsmConverter {
230230
uint256 initialIssuedAssetBalance = IERC20(ISSUED_ASSET).balanceOf(address(this));
231231
uint256 initialRedeemedAssetBalance = IERC20(REDEEMED_ASSET).balanceOf(address(this));
232232

233-
(uint256 assetAmount, , , ) = IGsm(GSM).getGhoAmountForSellAsset(maxAmount); // asset is BUIDL
234-
IERC20(REDEEMED_ASSET).transferFrom(originator, address(this), assetAmount);
235-
IERC20(REDEEMED_ASSET).approve(SUBSCRIPTION_CONTRACT, assetAmount);
236-
//TODO: replace with proper issuance implementation later
237-
MockBUIDLSubscription(SUBSCRIPTION_CONTRACT).issuance(assetAmount);
238-
uint256 subscribedAssetAmount = IERC20(ISSUED_ASSET).balanceOf(address(this)) -
239-
initialIssuedAssetBalance;
240-
// TODO: probably will be fees from issuance, so need to adjust the logic
241-
// only use this require only if preview of issuance is possible, otherwise it is redundant
233+
(uint256 redeemedAssetAmount, , , ) = IGsm(GSM).getGhoAmountForSellAsset(maxAmount); // asset is BUIDL
234+
IERC20(REDEEMED_ASSET).transferFrom(originator, address(this), redeemedAssetAmount);
235+
IERC20(REDEEMED_ASSET).approve(SUBSCRIPTION_CONTRACT, redeemedAssetAmount);
236+
237+
(uint256 subscribedAssetAmount, , ) = IERC20(SUBSCRIPTION_CONTRACT).calculateSuperstateTokenOut(
238+
redeemedAssetAmount,
239+
REDEEMED_ASSET
240+
);
241+
ISubscription(SUBSCRIPTION_CONTRACT).subscribe(redeemedAssetAmount, REDEEMED_ASSET);
242242
require(
243243
IERC20(ISSUED_ASSET).balanceOf(address(this)) ==
244244
initialIssuedAssetBalance + subscribedAssetAmount,
@@ -247,9 +247,8 @@ contract USTBGsmConverter is Ownable, EIP712, IGsmConverter {
247247
// reset approval after issuance
248248
IERC20(REDEEMED_ASSET).approve(SUBSCRIPTION_CONTRACT, 0);
249249

250-
// TODO: account for fees for sellAsset amount param
251-
(assetAmount, , , ) = IGsm(GSM).getGhoAmountForSellAsset(subscribedAssetAmount); // recalculate based on actual issuance amount, < maxAmount
252-
IERC20(ISSUED_ASSET).approve(GSM, assetAmount);
250+
(redeemedAssetAmount, , , ) = IGsm(GSM).getGhoAmountForSellAsset(subscribedAssetAmount); // recalculate based on actual issuance amount, < maxAmount
251+
IERC20(ISSUED_ASSET).approve(GSM, redeemedAssetAmount);
253252
(uint256 soldAssetAmount, uint256 ghoBought) = IGsm(GSM).sellAsset(
254253
subscribedAssetAmount,
255254
receiver
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Simple minimal interface for subscriptions of USTB
2+
pragma solidity ^0.8.10;
3+
4+
interface ISubscription {
5+
/**
6+
* @notice Subscribes an amount of USTB in exchange for USDC
7+
* @param amount The amount of USDC to subscribe
8+
* @param stablecoin The address of the stablecoin to calculate with
9+
*/
10+
function subscribe(uint256 inAmount, address stablecoin) external;
11+
}

src/test/TestGhoBase.t.sol

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import {MockRedemptionFailed} from './mocks/MockRedemptionFailed.sol';
3737
import {MockBUIDLSubscription} from './mocks/MockBUIDLSubscription.sol';
3838
import {MockBUIDLSubscriptionFailed} from './mocks/MockBUIDLSubscriptionFailed.sol';
3939
import {MockBUIDLSubscriptionFailedInvalidUSDCAccepted} from './mocks/MockBUIDLSubscriptionFailedInvalidUSDCAccepted.sol';
40-
import {MockUSTBSubscriptionRedemption} from './mocks/MockUSTBSubscriptionRedemption.sol';
40+
import {MockUSTBSubscription} from './mocks/MockUSTBSubscription.sol';
4141
import {MockPoolDataProvider} from './mocks/MockPoolDataProvider.sol';
4242

4343
// interfaces
@@ -121,6 +121,7 @@ contract TestGhoBase is Test, Constants, Events {
121121
IStakedAaveV3 STK_TOKEN;
122122
TestnetERC20 USDC_TOKEN;
123123
TestnetERC20 BUIDL_TOKEN;
124+
TestnetERC20 USTB_TOKEN;
124125
MockERC4626 USDC_4626_TOKEN;
125126
MockPool POOL;
126127
MockAclManager ACL_MANAGER;
@@ -132,7 +133,7 @@ contract TestGhoBase is Test, Constants, Events {
132133
MockBUIDLSubscription BUIDL_USDC_ISSUANCE;
133134
MockBUIDLSubscriptionFailed BUIDL_USDC_ISSUANCE_FAILED;
134135
MockBUIDLSubscriptionFailedInvalidUSDCAccepted BUIDL_USDC_ISSUANCE_FAILED_INVALID_USDC;
135-
MockUSTBSubscriptionRedemption USTB_SUBCRIPTION_REDEMPTION;
136+
MockUSTBSubscription USTB_SUBCRIPTION;
136137
PriceOracle PRICE_ORACLE;
137138
WETH9Mock WETH;
138139
GhoVariableDebtToken GHO_DEBT_TOKEN;
@@ -216,6 +217,7 @@ contract TestGhoBase is Test, Constants, Events {
216217
6,
217218
FAUCET
218219
);
220+
USTB_TOKEN = new TestnetERC20('Superstate Token', 'USTB', 6, FAUCET);
219221
USDC_4626_TOKEN = new MockERC4626('USD Coin 4626', '4626', address(USDC_TOKEN));
220222
IPool iPool = IPool(address(POOL));
221223
WETH = new WETH9Mock('Wrapped Ether', 'WETH', FAUCET);
@@ -291,6 +293,11 @@ contract TestGhoBase is Test, Constants, Events {
291293
address(BUIDL_TOKEN),
292294
6
293295
);
296+
GHO_USTB_GSM_FIXED_PRICE_STRATEGY = new FixedPriceStrategy(
297+
DEFAULT_FIXED_PRICE,
298+
address(BUIDL_TOKEN),
299+
6
300+
);
294301
GHO_GSM_FIXED_FEE_STRATEGY = new FixedFeeStrategy(DEFAULT_GSM_BUY_FEE, DEFAULT_GSM_SELL_FEE);
295302
GHO_GSM_LAST_RESORT_LIQUIDATOR = new SampleLiquidator();
296303
GHO_GSM_SWAP_FREEZER = new SampleSwapFreezer();
@@ -428,10 +435,7 @@ contract TestGhoBase is Test, Constants, Events {
428435
address(USDC_TOKEN)
429436
);
430437

431-
USTB_SUBCRIPTION_REDEMPTION = new MockUSTBSubscriptionRedemption(
432-
address(USDC_TOKEN),
433-
address(USDC_TOKEN)
434-
);
438+
USTB_SUBCRIPTION = new MockUSTBSubscription(address(USDC_TOKEN), address(USDC_TOKEN));
435439
}
436440

437441
function ghoFaucet(address to, uint256 amount) public {
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
pragma solidity ^0.8.10;
2+
3+
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
4+
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
5+
6+
/**
7+
* @title MockUSTBSubscription
8+
*/
9+
contract MockUSTBSubscription {
10+
using SafeERC20 for IERC20;
11+
12+
uint256 constant USDC_PRECISION = 1e6;
13+
uint256 constant SUPERSTATE_TOKEN_PRECISION = 1e6;
14+
uint256 constant CHAINLINK_FEED_PRECISION = 1e8;
15+
16+
address public immutable asset;
17+
address public immutable liquidity;
18+
uint256 public USTBPrice; // 1_100_000_000 is 1 USTB = 11 USDC, including chainlink precision
19+
20+
/**
21+
* @param _asset Address of asset token, ie USTB
22+
* @param _liquidity Address of liquidity token, ie USDC
23+
*/
24+
constructor(address _asset, address _liquidity, uint256 _price) {
25+
asset = _asset;
26+
liquidity = _liquidity;
27+
setUSTBPrice(_price);
28+
}
29+
30+
function test_coverage_ignore() public virtual {
31+
// Intentionally left blank.
32+
// Excludes contract from coverage.
33+
}
34+
35+
/**
36+
* @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.
37+
* @param inAmount The amount of the stablecoin in
38+
* @param stablecoin The address of the stablecoin to calculate with
39+
*/
40+
function subscribe(uint256 inAmount, address stablecoin) external {
41+
(
42+
uint256 superstateTokenOutAmount,
43+
uint256 stablecoinInAmountAfterFee,
44+
45+
) = calculateSuperstateTokenOut({inAmount: inAmount, stablecoin: stablecoin});
46+
47+
IERC20(stablecoin).safeTransferFrom({from: msg.sender, to: address(this), value: inAmount});
48+
IERC20(asset).safeTransfer(msg.sender, superstateTokenOutAmount);
49+
}
50+
51+
/**
52+
* @notice The ```calculateSuperstateTokenOut``` function calculates the total amount of Superstate tokens you'll receive for the inAmount of stablecoin. Treats all stablecoins as if they are always worth a dollar.
53+
* @param inAmount The amount of the stablecoin in
54+
* @param stablecoin The address of the stablecoin to calculate with
55+
* @return superstateTokenOutAmount The amount of Superstate tokens received for inAmount of stablecoin
56+
* @return stablecoinInAmountAfterFee The amount of the stablecoin in after any fees
57+
* @return feeOnStablecoinInAmount The amount of the stablecoin taken in fees
58+
*/
59+
function calculateSuperstateTokenOut(
60+
uint256 inAmount,
61+
address stablecoin
62+
)
63+
public
64+
view
65+
returns (
66+
uint256 superstateTokenOutAmount,
67+
uint256 stablecoinInAmountAfterFee,
68+
uint256 feeOnStablecoinInAmount
69+
)
70+
{
71+
StablecoinConfig memory config = supportedStablecoins[stablecoin];
72+
73+
feeOnStablecoinInAmount = 0;
74+
stablecoinInAmountAfterFee = inAmount - feeOnStablecoinInAmount;
75+
76+
usdPerSuperstateTokenChainlinkRaw = USTBPrice; // 9.5 USDC/SUPERSTATE_TOKEN
77+
78+
uint256 stablecoinPrecision = 10 ** 6;
79+
uint256 chainlinkFeedPrecision = 10 ** 8;
80+
81+
// converts from a USD amount to a SUPERSTATE_TOKEN amount
82+
superstateTokenOutAmount =
83+
(stablecoinInAmountAfterFee * chainlinkFeedPrecision * SUPERSTATE_TOKEN_PRECISION) /
84+
(usdPerSuperstateTokenChainlinkRaw * stablecoinPrecision);
85+
}
86+
87+
/**
88+
* @notice Set the price of USTB, amount of USDC for 1 USTB. USTB/USDC both have 6 decimals.
89+
* @param newPrice The new price of USTB
90+
*/
91+
function setUSTBPrice(uint256 newPrice) public {
92+
USTBPrice = newPrice;
93+
}
94+
}

src/test/mocks/MockUSTBSubscriptionRedemption.sol

Lines changed: 0 additions & 89 deletions
This file was deleted.

0 commit comments

Comments
 (0)