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

[Tunnel] implement route - IBC Hook #485

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
3,102 changes: 3,075 additions & 27 deletions api/band/tunnel/v1beta1/route.pulsar.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions app/keepers/keepers.go
Original file line number Diff line number Diff line change
@@ -493,6 +493,7 @@ func NewAppKeeper(
appKeepers.IBCFeeKeeper,
appKeepers.IBCKeeper.PortKeeper,
appKeepers.ScopedTunnelKeeper,
appKeepers.TransferKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)

2 changes: 1 addition & 1 deletion app/modules.go
Original file line number Diff line number Diff line change
@@ -83,7 +83,7 @@ var maccPerms = map[string][]string{
ibcfeetypes.ModuleName: nil,
bandtsstypes.ModuleName: nil,
restaketypes.ModuleName: nil,
tunneltypes.ModuleName: nil,
tunneltypes.ModuleName: {authtypes.Minter},
}

func appModules(
44 changes: 44 additions & 0 deletions proto/band/tunnel/v1beta1/route.proto
Original file line number Diff line number Diff line change
@@ -60,3 +60,47 @@ message TunnelPricesPacketData {
// created_at is the timestamp when the packet is created
int64 created_at = 4;
}

// IBCHookRoute is the type for an IBC hook route
message IBCHookRoute {
option (cosmos_proto.implements_interface) = "RouteI";

// channel_id is the IBC channel ID
string channel_id = 1 [(gogoproto.customname) = "ChannelID"];
// destination_contract_address is the destination contract address
string destination_contract_address = 2;
}

// IBCHookPacketReceipt represents a receipt for a IBC hook packet and implements the PacketReceiptI interface.
message IBCHookPacketReceipt {
option (cosmos_proto.implements_interface) = "PacketContentI";

// sequence is representing the sequence of the IBC packet.
uint64 sequence = 1;
}

// IBCHookPacket represents the IBC packet payload for the IBC hook packet.
message IBCHookPacket {
// packet is the packet data
TunnelPricesPacketData packet = 1;
}

// IBCHookMsg represents the message structure of the IBC hook message.
message IBCHookMsg {
// receive_packet is the function name on the destination contract
IBCHookPacket receive_packet = 1;
}

// IBCHookWasm represents the WASM contract and its associated message.
message IBCHookWasm {
// contract is destination contract address
string contract = 1;
// msg is the IBC hook message
IBCHookMsg msg = 2;
}

// IBCHookMemo is the type for an IBC hook memo
message IBCHookMemo {
// wasm is the wasm memo struct for IBC hook
IBCHookWasm wasm = 1;
}
1 change: 1 addition & 0 deletions scripts/tunnel/create_ibc_hook_tunnel.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bandd tx tunnel create-tunnel ibc-hook channel-0 osmo1hsj6zc68xh4ydpt57mkynye7frhx5ndzdqhkcl88k4duyfgzqc9qf8gxdx 1000000000uband 60 ./scripts/tunnel/signal_deviations.json --from requester --keyring-backend test --gas-prices 0.0025uband -y --chain-id bandchain
89 changes: 89 additions & 0 deletions x/tunnel/client/cli/tx.go
Original file line number Diff line number Diff line change
@@ -51,6 +51,7 @@ func GetTxCmdCreateTunnel() *cobra.Command {
txCmd.AddCommand(
GetTxCmdCreateTSSTunnel(),
GetTxCmdCreateIBCTunnel(),
GetTxCmdCreateIBCHookTunnel(),
)

return txCmd
@@ -156,6 +157,55 @@ func GetTxCmdCreateIBCTunnel() *cobra.Command {
return cmd
}

func GetTxCmdCreateIBCHookTunnel() *cobra.Command {
cmd := &cobra.Command{
Use: "ibc-hook [channel-id] [destination-contract-address] [initial-deposit] [interval] [signalInfos-json-file]",
Short: "Create a new IBC hook tunnel",
Args: cobra.ExactArgs(5),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

channelID := args[0]
destinationContractAddress := args[1]

initialDeposit, err := sdk.ParseCoinsNormalized(args[2])
if err != nil {
return err
}

interval, err := strconv.ParseUint(args[3], 10, 64)
if err != nil {
return err
}

signalInfos, err := parseSignalDeviations(args[4])
if err != nil {
return err
}

msg, err := types.NewMsgCreateIBCHookTunnel(
signalInfos.ToSignalDeviations(),
interval,
channelID,
destinationContractAddress,
initialDeposit,
clientCtx.GetFromAddress().String(),
)
if err != nil {
return err
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)
return cmd
}

func GetTxCmdUpdateRoute() *cobra.Command {
txCmd := &cobra.Command{
Use: "update-route",
@@ -167,6 +217,7 @@ func GetTxCmdUpdateRoute() *cobra.Command {
// add create tunnel subcommands
txCmd.AddCommand(
GetTxCmdUpdateIBCRoute(),
GetTxCmdUpdateIBCHookRoute(),
)

return txCmd
@@ -206,6 +257,44 @@ func GetTxCmdUpdateIBCRoute() *cobra.Command {
return cmd
}

func GetTxCmdUpdateIBCHookRoute() *cobra.Command {
cmd := &cobra.Command{
Use: "ibc-hook [tunnel-id] [channel-id] [destination-contract-address]",
Short: "Update IBC route of a IBC tunnel",
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

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

channelID := args[1]
destContractAddr := args[2]

msg, err := types.NewMsgUpdateIBCHookRoute(
id,
channelID,
destContractAddr,
clientCtx.GetFromAddress().String(),
)
if err != nil {
return err
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}

func GetTxCmdUpdateSignalsAndInterval() *cobra.Command {
cmd := &cobra.Command{
Use: "update-signals-and-interval [tunnel-id] [interval] [signalDeviations-json-file] ",
41 changes: 22 additions & 19 deletions x/tunnel/keeper/keeper.go
Original file line number Diff line number Diff line change
@@ -16,14 +16,15 @@ type Keeper struct {
cdc codec.BinaryCodec
storeKey storetypes.StoreKey

authKeeper types.AccountKeeper
bankKeeper types.BankKeeper
feedsKeeper types.FeedsKeeper
bandtssKeeper types.BandtssKeeper
channelKeeper types.ChannelKeeper
ics4Wrapper types.ICS4Wrapper
portKeeper types.PortKeeper
scopedKeeper types.ScopedKeeper
authKeeper types.AccountKeeper
bankKeeper types.BankKeeper
feedsKeeper types.FeedsKeeper
bandtssKeeper types.BandtssKeeper
channelKeeper types.ChannelKeeper
ics4Wrapper types.ICS4Wrapper
portKeeper types.PortKeeper
scopedKeeper types.ScopedKeeper
transferKeeper types.TransferKeeper

authority string
}
@@ -40,6 +41,7 @@ func NewKeeper(
ics4Wrapper types.ICS4Wrapper,
portKeeper types.PortKeeper,
scopedKeeper types.ScopedKeeper,
transferKeeper types.TransferKeeper,
authority string,
) Keeper {
// ensure tunnel module account is set
@@ -53,17 +55,18 @@ func NewKeeper(
}

return Keeper{
cdc: cdc,
storeKey: key,
authKeeper: authKeeper,
bankKeeper: bankKeeper,
feedsKeeper: feedsKeeper,
bandtssKeeper: bandtssKeeper,
channelKeeper: channelKeeper,
ics4Wrapper: ics4Wrapper,
portKeeper: portKeeper,
scopedKeeper: scopedKeeper,
authority: authority,
cdc: cdc,
storeKey: key,
authKeeper: authKeeper,
bankKeeper: bankKeeper,
feedsKeeper: feedsKeeper,
bandtssKeeper: bandtssKeeper,
channelKeeper: channelKeeper,
ics4Wrapper: ics4Wrapper,
portKeeper: portKeeper,
scopedKeeper: scopedKeeper,
transferKeeper: transferKeeper,
authority: authority,
}
}

2 changes: 2 additions & 0 deletions x/tunnel/keeper/keeper_packet.go
Original file line number Diff line number Diff line change
@@ -208,6 +208,8 @@ func (k Keeper) SendPacket(ctx sdk.Context, packet types.Packet) (err error) {
)
case *types.IBCRoute:
receipt, err = k.SendIBCPacket(ctx, r, packet, tunnel.Interval)
case *types.IBCHookRoute:
receipt, err = k.SendIBCHookPacket(ctx, r, packet, sdk.MustAccAddressFromBech32(tunnel.FeePayer), tunnel.Interval)
default:
return types.ErrInvalidRoute.Wrapf("no route found for tunnel ID: %d", tunnel.ID)
}
73 changes: 73 additions & 0 deletions x/tunnel/keeper/keeper_packet_ibc_hook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package keeper

import (
"time"

ibctransfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types"
clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/bandprotocol/chain/v3/x/tunnel/types"
)

// SendIBCHookPacket sends a packet to the destination chain using IBC Hook
func (k Keeper) SendIBCHookPacket(
ctx sdk.Context,
route *types.IBCHookRoute,
packet types.Packet,
feePayer sdk.AccAddress,
interval uint64,
) (types.PacketReceiptI, error) {
// create memo string for ibc transfer
pricePacket := types.NewTunnelPricesPacketData(packet.TunnelID, packet.Sequence, packet.Prices, packet.CreatedAt)
memoStr := types.NewIBCHookMemo(route.DestinationContractAddress, pricePacket).JSONString()

// mint coin to the fee payer
err := k.MintIBCHookCoinToAccount(ctx, packet.TunnelID, feePayer)
if err != nil {
return nil, err
}

// create ibc transfer message with the memo string
msg := ibctransfertypes.NewMsgTransfer(
ibctransfertypes.PortID,
route.ChannelID,
sdk.NewInt64Coin(types.FormatHookDenomIdentifier(packet.TunnelID), types.HookTransferAmount),
feePayer.String(),
route.DestinationContractAddress,
clienttypes.ZeroHeight(),
uint64(ctx.BlockTime().UnixNano())+interval*uint64(time.Second)*2,
memoStr,
)

// send packet
res, err := k.transferKeeper.Transfer(ctx, msg)
if err != nil {
return nil, err
}

return types.NewIBCHookPacketReceipt(res.Sequence), nil
}

// MintIBCHookCoinToAccount mints hook coin to the account
func (k Keeper) MintIBCHookCoinToAccount(ctx sdk.Context, tunnelID uint64, account sdk.AccAddress) error {
// create hook coins
hookCoins := sdk.NewCoins(
sdk.NewInt64Coin(types.FormatHookDenomIdentifier(tunnelID), types.HookTransferAmount),
)

// mint coins to the module account
err := k.bankKeeper.MintCoins(ctx, types.ModuleName, hookCoins)
if err != nil {
return err
}

// send coins to the account
return k.bankKeeper.SendCoinsFromModuleToAccount(
ctx,
types.ModuleName,
account,
hookCoins,
)
}
Loading