Skip to content

Commit

Permalink
phase 2 - added trade config (#995)
Browse files Browse the repository at this point in the history
  • Loading branch information
sampocs authored Nov 29, 2023
1 parent b32af28 commit 3e0917a
Show file tree
Hide file tree
Showing 19 changed files with 932 additions and 683 deletions.
51 changes: 30 additions & 21 deletions proto/stride/stakeibc/trade_route.proto
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,8 @@ message TradeHop {
ICAAccount to_account = 3 [ (gogoproto.nullable) = false ];
}

// TradeRoute represents a round trip including info on transfer and how to do
// the swap. It makes the assumption that the reward token is always foreign to
// the host so therefore the first two hops are to unwind the ibc denom enroute
// to the trade chain and the last hop is the return so funds start/end in the
// withdrawl ICA on hostZone
message TradeRoute {
// ibc denom for the reward on the host zone
string reward_denom_on_host_zone = 1;
// should be the native denom for the reward chain
string reward_denom_on_reward_zone = 2;
// ibc denom of the reward on the trade chain, input to the swap
string reward_denom_on_trade_zone = 3;
// ibc of the host denom on the trade chain, output from the swap
string host_denom_on_trade_zone = 4;
// should be the same as the native host denom on the host chain
string host_denom_on_host_zone = 5;

TradeHop host_to_reward_hop = 6 [ (gogoproto.nullable) = false ];
TradeHop reward_to_trade_hop = 7 [ (gogoproto.nullable) = false ];
TradeHop trade_to_host_hop = 8 [ (gogoproto.nullable) = false ];

// Stores pool information needed to execute the swap along a trade route
message TradeConfig {
// Currently Osmosis is the only trade chain so this is an osmosis pool id
uint64 pool_id = 9;

Expand Down Expand Up @@ -72,3 +53,31 @@ message TradeRoute {
(gogoproto.nullable) = false
];
}

// TradeRoute represents a round trip including info on transfer and how to do
// the swap. It makes the assumption that the reward token is always foreign to
// the host so therefore the first two hops are to unwind the ibc denom enroute
// to the trade chain and the last hop is the return so funds start/end in the
// withdrawl ICA on hostZone
// The structure is key'd on reward denom and host denom in their native forms
// (i.e. reward_denom_on_reward_zone and host_denom_on_host_zone)
message TradeRoute {
// ibc denom for the reward on the host zone
string reward_denom_on_host_zone = 1;
// should be the native denom for the reward chain
string reward_denom_on_reward_zone = 2;
// ibc denom of the reward on the trade chain, input to the swap
string reward_denom_on_trade_zone = 3;
// ibc of the host denom on the trade chain, output from the swap
string host_denom_on_trade_zone = 4;
// should be the same as the native host denom on the host chain
string host_denom_on_host_zone = 5;

TradeHop host_to_reward_hop = 6 [ (gogoproto.nullable) = false ];
TradeHop reward_to_trade_hop = 7 [ (gogoproto.nullable) = false ];
TradeHop trade_to_host_hop = 8 [ (gogoproto.nullable) = false ];

// specifies the configuration needed to execute the swap
// such as pool_id, slippage, min trade amount, etc.
TradeConfig trade_config = 9 [ (gogoproto.nullable) = false ];
}
65 changes: 52 additions & 13 deletions proto/stride/stakeibc/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ message MsgResumeHostZone {
}
message MsgResumeHostZoneResponse {}

// Creates a new trade route
message MsgCreateTradeRoute {
option (cosmos.msg.v1.signer) = "authority";
option (amino.name) = "stride/x/stakeibc/MsgCreateTradeRoute";
Expand Down Expand Up @@ -251,31 +252,69 @@ message MsgCreateTradeRoute {
// from the current value
string max_allowed_swap_loss_rate = 14;

// min and max set boundaries of reward denom on trade chain we will swap
uint64 min_swap_amount = 15;
uint64 max_swap_amount = 16;
// minimum amount of reward tokens to initate a swap
// if not provided, defaults to 0
string min_swap_amount = 15 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
// maximum amount of reward tokens in a single swap
// if not provided, defaults to 10e24
string max_swap_amount = 16 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
}
message MsgCreateTradeRouteResponse {}

// Deletes a trade route
message MsgDeleteTradeRoute {
string creator = 1;
string host_denom = 2;
string reward_denom = 3;
option (cosmos.msg.v1.signer) = "authority";
option (amino.name) = "stride/x/stakeibc/MsgDeleteTradeRoute";

// authority is the address that controls the module (defaults to x/gov unless
// overwritten).
string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];

// The reward denom of the route in it's native form (e.g. usdc)
string reward_denom = 2;
// The host zone's denom in it's native form (e.g. dydx)
string host_denom = 3;
}
message MsgDeleteTradeRouteResponse {}

// Updates the config of a trade route
message MsgUpdateTradeRoute {
string creator = 1;
string host_denom = 2;
string reward_denom = 3;
option (cosmos.msg.v1.signer) = "authority";
option (amino.name) = "stride/x/stakeibc/MsgUpdateTradeRoute";

// authority is the address that controls the module (defaults to x/gov unless
// overwritten).
string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];

// The reward denom of the route in it's native form (e.g. usdc)
string reward_denom = 2;
// The host zone's denom in it's native form (e.g. dydx)
string host_denom = 3;

// The osmosis pool ID
uint64 pool_id = 4;
string min_swap_amount = 5 [
(cosmos_proto.scalar) = "cosmos.Int",

// Threshold defining the percentage of tokens that could be lost in the trade
// This captures both the loss from slippage and from a stale price on stride
// "0.05" means the output from the trade can be no less than a 5% deviation
// from the current value
string max_allowed_swap_loss_rate = 5;

// minimum amount of reward tokens to initate a swap
// if not provided, defaults to 0
string min_swap_amount = 6 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
string max_swap_amount = 6 [
(cosmos_proto.scalar) = "cosmos.Int",
// maximum amount of reward tokens in a single swap
// if not provided, defaults to 10e24
string max_swap_amount = 7 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
Expand Down
2 changes: 0 additions & 2 deletions x/stakeibc/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ func GetTxCmd() *cobra.Command {
cmd.AddCommand(CmdUndelegateHost())
cmd.AddCommand(CmdUpdateInnerRedemptionRateBounds())
cmd.AddCommand(CmdResumeHostZone())
cmd.AddCommand(CmdDeleteTradeRoute())
cmd.AddCommand(CmdUpdateTradeRoute())

return cmd
}
44 changes: 0 additions & 44 deletions x/stakeibc/client/cli/tx_delete_trade_route.go

This file was deleted.

62 changes: 0 additions & 62 deletions x/stakeibc/client/cli/tx_update_trade_route.go

This file was deleted.

8 changes: 4 additions & 4 deletions x/stakeibc/keeper/icqcallbacks_pool_price.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import (
// P0LastSpotPrice gives the ratio of Asset0Denom / Asset1Denom
// P1LastSpotPrice gives the ratio of Asset1Denom / Asset0Denom
//
// When storing down the price, we want to store down the ratio of HostDenom.
// Meaning, if Asset0Denom is the host denom, we want to store P0LastSpotPrice
// When storing down the price, we want to store down the ratio of HostDenom to RewardDenom
// Meaning, if Asset0Denom is the HostDenom, we want to store P0LastSpotPrice
func PoolPriceCallback(k Keeper, ctx sdk.Context, args []byte, query icqtypes.Query) error {
k.Logger(ctx).Info(utils.LogICQCallbackWithHostZone(query.ChainId, ICQCallbackID_PoolPrice,
"Starting pool spot price callback, QueryId: %vs, QueryType: %s, Connection: %s", query.Id, query.QueryType, query.ConnectionId))
Expand Down Expand Up @@ -65,8 +65,8 @@ func PoolPriceCallback(k Keeper, ctx sdk.Context, args []byte, query icqtypes.Qu
tradeRoute.RewardDenomOnTradeZone, tradeRoute.HostDenomOnTradeZone, price))

// Update the price and time on the trade route data
tradeRoute.SwapPrice = price
tradeRoute.PriceUpdateTimestamp = uint64(ctx.BlockTime().UnixNano())
tradeRoute.TradeConfig.SwapPrice = price
tradeRoute.TradeConfig.PriceUpdateTimestamp = uint64(ctx.BlockTime().UnixNano())
k.SetTradeRoute(ctx, tradeRoute)

return nil
Expand Down
33 changes: 23 additions & 10 deletions x/stakeibc/keeper/msg_server_create_trade_route.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import (
connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types"
)

var DefaultMaxAllowedSwapLossRate = "0.05"
var (
DefaultMaxAllowedSwapLossRate = "0.05"
DefaultMaxSwapAmount = sdkmath.NewIntWithDecimal(10, 24) // 10e24
)

// Gov tx to to register a trade route that swaps reward tokens for a different denom
//
Expand Down Expand Up @@ -59,10 +62,10 @@ func (ms msgServer) CreateTradeRoute(goCtx context.Context, msg *types.MsgCreate
}

// Validate trade route does not already exist for this denom
_, found := ms.Keeper.GetTradeRoute(ctx, msg.RewardDenomOnHost, msg.HostDenomOnHost)
_, found := ms.Keeper.GetTradeRoute(ctx, msg.RewardDenomOnReward, msg.HostDenomOnHost)
if found {
return nil, errorsmod.Wrapf(types.ErrTradeRouteAlreadyExists,
"startDenom: %s, endDenom: %s", msg.RewardDenomOnHost, msg.HostDenomOnHost)
"startDenom: %s, endDenom: %s", msg.RewardDenomOnReward, msg.HostDenomOnHost)
}

// Confirm the host chain exists and the withdrawal address has been initialized
Expand Down Expand Up @@ -109,10 +112,26 @@ func (ms msgServer) CreateTradeRoute(goCtx context.Context, msg *types.MsgCreate
ToAccount: hostICA,
}

// If a max allowed swap loss is not provided, use the default
maxAllowedSwapLossRate := msg.MaxAllowedSwapLossRate
if maxAllowedSwapLossRate == "" {
maxAllowedSwapLossRate = DefaultMaxAllowedSwapLossRate
}
maxSwapAmount := msg.MaxSwapAmount
if maxSwapAmount.IsZero() {
maxSwapAmount = DefaultMaxSwapAmount
}

// Create the trade config to specify parameters needed for the swap
tradeConfig := types.TradeConfig{
PoolId: msg.PoolId,
SwapPrice: sdk.ZeroDec(), // this should only ever be set by ICQ so initialize to blank
PriceUpdateTimestamp: 0,

MaxAllowedSwapLossRate: sdk.MustNewDecFromStr(maxAllowedSwapLossRate),
MinSwapAmount: msg.MinSwapAmount,
MaxSwapAmount: maxSwapAmount,
}

// Finally build and store the main trade route
tradeRoute := types.TradeRoute{
Expand All @@ -126,13 +145,7 @@ func (ms msgServer) CreateTradeRoute(goCtx context.Context, msg *types.MsgCreate
RewardToTradeHop: rewardToTradeHop,
TradeToHostHop: tradeToHostHop,

PoolId: msg.PoolId,
SwapPrice: sdk.ZeroDec(), // this should only ever be set by ICQ so initialize to blank
PriceUpdateTimestamp: 0,

MaxAllowedSwapLossRate: sdk.MustNewDecFromStr(maxAllowedSwapLossRate),
MinSwapAmount: sdkmath.NewIntFromUint64(msg.MinSwapAmount),
MaxSwapAmount: sdkmath.NewIntFromUint64(msg.MaxSwapAmount),
TradeConfig: tradeConfig,
}

ms.Keeper.SetTradeRoute(ctx, tradeRoute)
Expand Down
Loading

0 comments on commit 3e0917a

Please sign in to comment.