Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IRS new Query support for FE #725

Merged
merged 16 commits into from
Feb 2, 2024
1 change: 1 addition & 0 deletions proto/ununifi/irs/amm.proto
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ message TranchePool {
cosmos.base.v1beta1.Coin total_shares = 7 [(gogoproto.nullable) = false];
// Pool assets are sorted by denomination
repeated cosmos.base.v1beta1.Coin pool_assets = 8 [(gogoproto.nullable) = false];
string denom = 9; // denom of the underlying asset
}
1 change: 1 addition & 0 deletions proto/ununifi/irs/irs.proto
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ message InterestRateSwapVault {
uint64 max_maturity = 4; // e.g. 180 days - 180 * 86400
uint64 cycle = 5; // e.g. 90 days - 90 * 86400
uint64 last_tranche_time = 6; // last tranche creation timestamp
string denom = 7; // denom of the underlying asset
}
29 changes: 23 additions & 6 deletions proto/ununifi/irs/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ service Query {
rpc EstimateRedeemLiquidityPoolToken(QueryEstimateRedeemLiquidityPoolTokenRequest) returns (QueryEstimateRedeemLiquidityPoolTokenResponse) {
option (google.api.http).get = "/ununifi/irs/estimate-redeem-liquidity-pool-token";
}
// Estimate required UT amount to swao to YT
// Estimate swap UT to YT
rpc EstimateSwapUtToYt(QueryEstimateSwapUtToYtRequest) returns (QueryEstimateSwapUtToYtResponse) {
option (google.api.http).get = "/ununifi/irs/estimate-swap-ut-to-yt";
}
// Estimate required UT amount to swap to YT
rpc EstimateRequiredUtSwapToYt(QueryEstimateRequiredUtSwapToYtRequest) returns (QueryEstimateRequiredUtSwapToYtResponse) {
option (google.api.http).get = "/ununifi/irs/estimate-required-ut-swap-to-yt";
}
Expand Down Expand Up @@ -172,21 +176,24 @@ message QueryEstimateMintPtYtPairResponse {

message QueryEstimateRedeemPtYtPairRequest {
uint64 id = 1;
string desired_ut_amount = 2;
string denom = 2;
string amount = 3;
}

message QueryEstimateRedeemPtYtPairResponse {
cosmos.base.v1beta1.Coin pt_amount = 1 [(gogoproto.nullable) = false];
cosmos.base.v1beta1.Coin yt_amount = 2 [(gogoproto.nullable) = false];
cosmos.base.v1beta1.Coin redeem_amount = 1 [(gogoproto.nullable) = false];
cosmos.base.v1beta1.Coin additional_required_amount = 2 [(gogoproto.nullable) = false];
}

message QueryEstimateMintLiquidityPoolTokenRequest {
uint64 id = 1;
string desired_amount = 3;
string denom = 2;
string amount = 3;
}

message QueryEstimateMintLiquidityPoolTokenResponse {
repeated cosmos.base.v1beta1.Coin required_amount = 1 [(gogoproto.nullable) = false];
cosmos.base.v1beta1.Coin mint_amount = 1 [(gogoproto.nullable) = false];
cosmos.base.v1beta1.Coin additional_required_amount = 2 [(gogoproto.nullable) = false];
}

message QueryEstimateRedeemLiquidityPoolTokenRequest {
Expand All @@ -198,6 +205,16 @@ message QueryEstimateRedeemLiquidityPoolTokenResponse {
repeated cosmos.base.v1beta1.Coin redeem_amount = 1 [(gogoproto.nullable) = false];
}

message QueryEstimateSwapUtToYtRequest {
uint64 id = 1;
string denom = 2;
string amount = 3;
}

message QueryEstimateSwapUtToYtResponse {
cosmos.base.v1beta1.Coin yt_amount = 1 [(gogoproto.nullable) = false];
}

message QueryEstimateRequiredUtSwapToYtRequest {
uint64 id = 1;
string desired_yt_amount = 3;
Expand Down
1 change: 1 addition & 0 deletions x/irs/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func GetQueryCmd(queryRoute string) *cobra.Command {
CmdEstimateSwapInPool(),
CmdEstimateMintPtYtPair(),
CmdEstimateRedeemPtYtPair(),
CmdEstimateSwapUtToYt(),
CmdEstimateRequiredUtSwapToYt(),
CmdEstimateSwapMaturedYtToUt(),
CmdEstimateMintLiquidityPoolToken(),
Expand Down
74 changes: 59 additions & 15 deletions x/irs/client/cli/query_estimation.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ func CmdEstimateMintPtYtPair() *cobra.Command {

func CmdEstimateRedeemPtYtPair() *cobra.Command {
cmd := &cobra.Command{
Use: "estimate-redeem-pt-yt-pair [id] [desired-amount]",
Short: "estimate require PT & YT to redeem result by desired redeem amount",
Use: "estimate-redeem-pt-yt-pair [id] [amount]",
Short: "estimate redeem pt-yt-pair result by each input amount",
Long: `Example:
ununifid query irs estimate-redeem-pt-yt-pair 1 1000000
ununifid query irs estimate-redeem-pt-yt-pair 1 1000000irs/tranche/1/pt
`,
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -111,14 +111,15 @@ func CmdEstimateRedeemPtYtPair() *cobra.Command {
return err
}

desiredUt, ok := sdk.NewIntFromString(args[2])
if !ok {
return fmt.Errorf("error parsing amount")
token, err := sdk.ParseCoinNormalized(args[1])
if err != nil {
return err
}

params := &types.QueryEstimateRedeemPtYtPairRequest{
Id: uint64(id),
DesiredUtAmount: desiredUt.String(),
Id: uint64(id),
Denom: token.Denom,
Amount: token.Amount.String(),
}

res, err := queryClient.EstimateRedeemPtYtPair(context.Background(), params)
Expand All @@ -135,6 +136,48 @@ func CmdEstimateRedeemPtYtPair() *cobra.Command {
return cmd
}

func CmdEstimateSwapUtToYt() *cobra.Command {
cmd := &cobra.Command{
Use: "estimate-swap-ut-to-yt [id] [amount]",
Short: "estimate swap to YT result by underlying token amount",
Long: `Example:
ununifid query irs estimate-swap-ut-to-yt 1 1000000uguu
`,
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
queryClient := types.NewQueryClient(clientCtx)

id, err := strconv.Atoi(args[0])
if err != nil {
return err
}

token, err := sdk.ParseCoinNormalized(args[1])
if err != nil {
return err
}

params := &types.QueryEstimateSwapUtToYtRequest{
Id: uint64(id),
Denom: token.Denom,
Amount: token.Amount.String(),
}

res, err := queryClient.EstimateSwapUtToYt(context.Background(), params)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}

func CmdEstimateRequiredUtSwapToYt() *cobra.Command {
cmd := &cobra.Command{
Use: "estimate-required-ut-swap-to-yt [id] [desired-yt-amount]",
Expand Down Expand Up @@ -216,8 +259,8 @@ func CmdEstimateSwapMaturedYtToUt() *cobra.Command {

func CmdEstimateMintLiquidityPoolToken() *cobra.Command {
cmd := &cobra.Command{
Use: "estimate-mint-liquidity-pool-token [id] [desired-amount]",
Short: "estimate mint liquidity pool token by desired amount",
Use: "estimate-mint-liquidity-pool-token [id] [lp-amount]",
Short: "estimate mint liquidity pool token result by each input amount",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
Expand All @@ -228,14 +271,15 @@ func CmdEstimateMintLiquidityPoolToken() *cobra.Command {
return err
}

desired, ok := sdk.NewIntFromString(args[2])
if !ok {
return fmt.Errorf("error parsing amount")
token, err := sdk.ParseCoinNormalized(args[1])
if err != nil {
return err
}

params := &types.QueryEstimateMintLiquidityPoolTokenRequest{
Id: uint64(id),
DesiredAmount: desired.String(),
Id: uint64(id),
Denom: token.Denom,
Amount: token.Amount.String(),
}

res, err := queryClient.EstimateMintLiquidityPoolToken(context.Background(), params)
Expand Down
2 changes: 2 additions & 0 deletions x/irs/keeper/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ func (k Keeper) BeginBlocker(ctx sdk.Context) {
for _, vault := range vaults {
// register new tranches per cycle
if int64(vault.Cycle+vault.LastTrancheTime) < ctx.BlockTime().Unix() {
info := k.GetStrategyDepositInfo(ctx, vault.StrategyContract)
k.SetTranchePool(ctx, types.TranchePool{
Id: k.GetLastTrancheId(ctx) + 1,
StrategyContract: vault.StrategyContract,
Denom: info.Denom,
StartTime: uint64(ctx.BlockTime().Unix()),
Maturity: vault.MaxMaturity,
SwapFee: params.TradeFeeRate,
Expand Down
33 changes: 31 additions & 2 deletions x/irs/keeper/amm.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ func (k Keeper) DepositToLiquidityPool(
}

// Ensure underlying token and pt token denoms are accurate when adding the liquidity for the first time
depositInfo := k.GetStrategyDepositInfo(ctx, pool.StrategyContract)
utDenom := depositInfo.Denom
utDenom := pool.Denom
ptDenom := types.PtDenom(pool)

if !tokenInMaxs.AmountOf(ptDenom).IsPositive() {
Expand Down Expand Up @@ -111,6 +110,36 @@ func GetMaximalNoSwapLPAmount(ctx sdk.Context, pool types.TranchePool, shareOutA
return getMaximalNoSwapLPAmount(ctx, pool, shareOutAmount)
}

func (k Keeper) CalculateMintLpAmount(ctx sdk.Context, pool types.TranchePool, tokenIn sdk.Coin) (sdk.Coin, sdk.Coin, error) {
totalSharesAmount := pool.TotalShares.Amount
if totalSharesAmount.IsZero() {
return sdk.Coin{}, sdk.Coin{}, types.ErrInvalidMathApprox
}
var requiredPoolLiquidity sdk.Coin
var tokenInPoolLiquidity sdk.Coin
if len(pool.PoolAssets) != 2 {
return sdk.Coin{}, sdk.Coin{}, types.ErrInvalidPoolAssets
}
if tokenIn.Denom == pool.PoolAssets[0].Denom {
tokenInPoolLiquidity = pool.PoolAssets[0]
requiredPoolLiquidity = pool.PoolAssets[1]
} else if tokenIn.Denom == pool.PoolAssets[1].Denom {
tokenInPoolLiquidity = pool.PoolAssets[1]
requiredPoolLiquidity = pool.PoolAssets[0]
} else {
return sdk.Coin{}, sdk.Coin{}, types.ErrInvalidDepositDenom
}
if tokenInPoolLiquidity.Amount.IsZero() {
return sdk.Coin{}, sdk.Coin{}, types.ErrSupplyNotFound
}
if requiredPoolLiquidity.Amount.IsZero() {
return sdk.Coin{}, sdk.Coin{}, types.ErrSupplyNotFound
}
mintAmount := tokenIn.Amount.Mul(totalSharesAmount).Quo(tokenInPoolLiquidity.Amount)
requiredAmount := tokenIn.Amount.Mul(requiredPoolLiquidity.Amount).Quo(tokenInPoolLiquidity.Amount)
return sdk.NewCoin(types.LsDenom(pool), mintAmount), sdk.NewCoin(requiredPoolLiquidity.Denom, requiredAmount), nil
}

func (k Keeper) WithdrawFromLiquidityPool(
ctx sdk.Context,
sender sdk.AccAddress,
Expand Down
9 changes: 7 additions & 2 deletions x/irs/keeper/amm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ func (s *KeeperTestSuite) TestDepositToLiquidityPool() {
tranchePool := types.TranchePool{
Id: 1,
StrategyContract: strategyContract.String(),
Denom: ut,
StartTime: uint64(ctx.BlockTime().Unix()),
Maturity: 86400 * 180,
SwapFee: sdk.NewDecWithPrec(3, 3), // 0.3%
Expand Down Expand Up @@ -247,6 +248,7 @@ func (suite *KeeperTestSuite) TestGetMaximalNoSwapLPAmount() {
tranchePool := types.TranchePool{
Id: 1,
StrategyContract: strategyContract.String(),
Denom: ut,
StartTime: uint64(ctx.BlockTime().Unix()),
Maturity: 86400 * 180,
SwapFee: sdk.NewDecWithPrec(3, 3), // 0.3%
Expand Down Expand Up @@ -345,6 +347,7 @@ func (s *KeeperTestSuite) TestWithdrawFromLiquidityPool() {
tranchePool := types.TranchePool{
Id: 1,
StrategyContract: strategyContract.String(),
Denom: ut,
StartTime: uint64(ctx.BlockTime().Unix()),
Maturity: 86400 * 180,
SwapFee: sdk.NewDecWithPrec(3, 3), // 0.3%
Expand Down Expand Up @@ -385,7 +388,8 @@ func (suite *KeeperTestSuite) TestMintPoolShareToAccount() {
shareAmount := sdk.NewInt(200000)
pool := types.TranchePool{
Id: poolId,
StrategyContract: "address",
StrategyContract: "uatom",
Denom: "denom",
StartTime: 1698796800,
Maturity: 1572800,
SwapFee: sdk.ZeroDec(),
Expand All @@ -410,7 +414,8 @@ func (suite *KeeperTestSuite) TestBurnPoolShareFromAccount() {
burnAmount := sdk.NewInt(100000)
pool := types.TranchePool{
Id: poolId,
StrategyContract: "address",
StrategyContract: "uatom",
Denom: "denom",
StartTime: 1698796800,
Maturity: 1572800,
SwapFee: sdk.ZeroDec(),
Expand Down
16 changes: 8 additions & 8 deletions x/irs/keeper/grpc_query_estimate_mint_liquidity_pool_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,27 @@ func (k Keeper) EstimateMintLiquidityPoolToken(c context.Context, req *types.Que
}

ctx := sdk.UnwrapSDKContext(c)
tranche, found := k.GetTranchePool(ctx, req.Id)
pool, found := k.GetTranchePool(ctx, req.Id)
if !found {
return nil, types.ErrTrancheNotFound
}
// initial deposit
if tranche.TotalShares.IsZero() {
if pool.TotalShares.IsZero() {
return &types.QueryEstimateMintLiquidityPoolTokenResponse{
RequiredAmount: sdk.Coins{},
MintAmount: sdk.NewCoin(types.LsDenom(pool), types.OneShare),
AdditionalRequiredAmount: sdk.Coin{},
}, nil
}
desiredAmount, ok := sdk.NewIntFromString(req.DesiredAmount)
tokenInAmount, ok := sdk.NewIntFromString(req.Amount)
if !ok {
return nil, types.ErrInvalidAmount
}
// we do an abstract calculation on the lp liquidity coins needed to have
// the designated amount of given shares of the pool without performing swap
neededLpLiquidity, err := GetMaximalNoSwapLPAmount(ctx, tranche, desiredAmount)
mint, require, err := k.CalculateMintLpAmount(ctx, pool, sdk.NewCoin(req.Denom, tokenInAmount))
if err != nil {
return nil, err
}
return &types.QueryEstimateMintLiquidityPoolTokenResponse{
RequiredAmount: neededLpLiquidity,
MintAmount: mint,
AdditionalRequiredAmount: require,
}, nil
}
3 changes: 1 addition & 2 deletions x/irs/keeper/grpc_query_estimate_mint_pt_yt_pair.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ func (k Keeper) EstimateMintPtYtPair(c context.Context, req *types.QueryEstimate
if !found {
return nil, types.ErrTrancheNotFound
}
depositInfo := k.GetStrategyDepositInfo(ctx, tranche.StrategyContract)
if req.Denom != depositInfo.Denom {
if req.Denom != tranche.Denom {
return nil, types.ErrInvalidDepositDenom
}
depositAmount, ok := sdk.NewIntFromString(req.Amount)
Expand Down
8 changes: 4 additions & 4 deletions x/irs/keeper/grpc_query_estimate_redeem_pt_yt_pair.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ func (k Keeper) EstimateRedeemPtYtPair(c context.Context, req *types.QueryEstima
if !found {
return nil, types.ErrTrancheNotFound
}
redeemAmount, ok := sdk.NewIntFromString(req.DesiredUtAmount)
tokenInAmount, ok := sdk.NewIntFromString(req.Amount)
if !ok {
return nil, types.ErrInvalidAmount
}
pt, yt, err := k.CalculateRedeemRequiredPtAndYtAmount(ctx, tranche, redeemAmount)
redeem, require, err := k.CalculateRedeemAmount(ctx, tranche, sdk.NewCoin(req.Denom, tokenInAmount))
if err != nil {
return nil, err
}

return &types.QueryEstimateRedeemPtYtPairResponse{
PtAmount: pt,
YtAmount: yt,
RedeemAmount: redeem,
AdditionalRequiredAmount: require,
}, nil
}
3 changes: 1 addition & 2 deletions x/irs/keeper/grpc_query_estimate_swap_matured_yt_to_ut.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ func (k Keeper) EstimateSwapMaturedYtToUt(c context.Context, req *types.QueryEst
if err != nil {
return nil, err
}
depositInfo := k.GetStrategyDepositInfo(ctx, tranche.StrategyContract)
return &types.QueryEstimateSwapMaturedYtToUtResponse{
UtAmount: sdk.NewCoin(depositInfo.Denom, redeemAmount),
UtAmount: sdk.NewCoin(tranche.Denom, redeemAmount),
}, nil
}
Loading
Loading