Skip to content

Commit

Permalink
Merge pull request #725 from UnUniFi/irs_new_query
Browse files Browse the repository at this point in the history
IRS new Query support for FE
  • Loading branch information
Senna46 authored Feb 2, 2024
2 parents 7ffacf5 + 9536bb3 commit b66dd6c
Show file tree
Hide file tree
Showing 27 changed files with 1,327 additions and 306 deletions.
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

0 comments on commit b66dd6c

Please sign in to comment.