Skip to content

[Tunnel] implement route - Axelar #574

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

Merged
merged 25 commits into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
262 changes: 246 additions & 16 deletions api/band/tunnel/v1beta1/params.pulsar.go

Large diffs are not rendered by default.

1,448 changes: 1,278 additions & 170 deletions api/band/tunnel/v1beta1/route.pulsar.go

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
cosmossdk.io/x/tx v0.13.8
cosmossdk.io/x/upgrade v0.1.4
github.com/Masterminds/semver/v3 v3.3.1
github.com/axelarnetwork/utils v0.0.0-20230706045331-b7aacc1f4a2f
github.com/bandprotocol/bothan/bothan-api/client/go-client v0.0.1-alpha.6
github.com/bandprotocol/go-owasm v0.3.1
github.com/bytecodealliance/wasmtime-go/v20 v20.0.0
Expand Down Expand Up @@ -47,6 +48,7 @@ require (
github.com/stretchr/testify v1.10.0
go.uber.org/mock v0.5.1
golang.org/x/crypto v0.27.0
golang.org/x/text v0.18.0
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38
google.golang.org/grpc v1.67.1
google.golang.org/protobuf v1.36.6
Expand Down Expand Up @@ -205,7 +207,6 @@ require (
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/term v0.24.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/api v0.186.0 // indirect
google.golang.org/genproto v0.0.0-20240701130421-f6361c86f094 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX
github.com/aws/aws-sdk-go v1.44.224 h1:09CiaaF35nRmxrzWZ2uRq5v6Ghg/d2RiPjZnSgtt+RQ=
github.com/aws/aws-sdk-go v1.44.224/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/axelarnetwork/utils v0.0.0-20230706045331-b7aacc1f4a2f h1:Q0Qh79MuZ/PWKzytqU8/4dt+S2QemdYYPXkjwp/i4l0=
github.com/axelarnetwork/utils v0.0.0-20230706045331-b7aacc1f4a2f/go.mod h1:7N7/Qo5WOm8HnwCtWvQs7T5QQLwVoLU/v+KkMLrvxiA=
github.com/bandprotocol/bothan/bothan-api/client/go-client v0.0.1-alpha.6 h1:xq2Pf48GQwxxzNFt7URfzDKIJL2ITC5lbMluG0g0H3M=
github.com/bandprotocol/bothan/bothan-api/client/go-client v0.0.1-alpha.6/go.mod h1:Qt5LDLwrFhLDQbLVBRd/yKyf2CNzHJlT+F+DEhnyqVE=
github.com/bandprotocol/go-owasm v0.3.1 h1:L38qAEmb0KyTICHBHJaBoo6yy5+BlbOzQeQ+ioUV5Uw=
Expand Down
6 changes: 6 additions & 0 deletions proto/band/tunnel/v1beta1/params.proto
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,10 @@ message Params {
// router_integration_contract specifies the address of the Router integration contract on the Router chain
// that the tunnel module will interact with.
string router_integration_contract = 9;
// axelar_ibc_channel specifies the IBC channel used by the tunnel to communicate with the axelar chain.
string axelar_ibc_channel = 10 [(gogoproto.customname) = "AxelarIBCChannel"];
// axelar_gmp_account is the account address on axelar chain that processes and verifies Axelar GMP transactions.
string axelar_gmp_account = 11 [(gogoproto.customname) = "AxelarGMPAccount"];
// axelar_fee_recipient is the account address on axelar chain that receive fee from tunnel.
string axelar_fee_recipient = 12;
}
21 changes: 21 additions & 0 deletions proto/band/tunnel/v1beta1/route.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ syntax = "proto3";
package band.tunnel.v1beta1;

import "cosmos_proto/cosmos.proto";
import "cosmos/base/v1beta1/coin.proto";
import "gogoproto/gogo.proto";

import "band/feeds/v1beta1/encoder.proto";
Expand Down Expand Up @@ -150,3 +151,23 @@ message RouterMemo {
// wasm is the payload for calling detination contract
Payload wasm = 1 [(gogoproto.nullable) = false];
}

// AxelarRoute represents a route for Axelar packets and implements the RouteI interface.
message AxelarRoute {
option (cosmos_proto.implements_interface) = "RouteI";

// destination_chain_id is the destination chain ID
string destination_chain_id = 1 [(gogoproto.customname) = "DestinationChainID", (gogoproto.casttype) = "ChainName"];
// destination_contract_address is the destination contract address
string destination_contract_address = 2;
// fee is the fee for each packet in the Axelar network.
cosmos.base.v1beta1.Coin fee = 3 [(gogoproto.nullable) = false];
}

// AxelarPacketReceipt represents a receipt for a Axelar packet and implements the PacketReceiptI interface.
message AxelarPacketReceipt {
option (cosmos_proto.implements_interface) = "PacketReceiptI";

// sequence is representing the sequence of the Axelar packet.
uint64 sequence = 1;
}
1 change: 1 addition & 0 deletions scripts/tunnel/create_axelar_tunnel.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bandd tx tunnel create-tunnel axelar base-sepolia 0xa20810c9d56316EC3033D96D557433613A559d21 113747ibc/BF3B4F53F3694B66E13C23107C84B6485BD2B96296BB7EC680EA77BBA75B4801 1000000000uband 3000 ./scripts/tunnel/signal_deviations.json --from requester --keyring-backend test --gas-prices 0.0025uband -y --chain-id bandchain
5 changes: 4 additions & 1 deletion scripts/tunnel/proposal_update_params.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
}
],
"router_ibc_channel": "channel-0",
"router_integration_contract": "router1v8evlcrtu4peer2fk5tnkau3qd0r0vtn8ll86qep4sgp89t6l9fqkvsue6"
"router_integration_contract": "router1v8evlcrtu4peer2fk5tnkau3qd0r0vtn8ll86qep4sgp89t6l9fqkvsue6",
"axelar_ibc_channel": "channel-1",
"axelar_gmp_account": "axelar1dv4u5k73pzqrxlzujxg3qp8kvc3pje7jtdvu72npnt5zhq05ejcsn5qme5",
"axelar_fee_recipient": "axelar1aythygn6z5thymj6tmzfwekzh05ewg3l7d6y89"
},
"authority": "band10d07y265gmmuvt4z0w9aw880jnsr700jrdn8wm"
}
Expand Down
1 change: 1 addition & 0 deletions scripts/tunnel/update_axelar_route.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bandd tx tunnel update-route axelar 1 base-sepolia 0x75F01b3a2352bdc6e0D3983e40E09E9A8AAf4DF6 113747ibc/BF3B4F53F3694B66E13C23107C84B6485BD2B96296BB7EC680EA77BBA75B4801 --from requester --keyring-backend test --gas-prices 0.0025uband -y --chain-id bandchain
101 changes: 101 additions & 0 deletions x/tunnel/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func GetTxCmdCreateTunnel() *cobra.Command {
GetTxCmdCreateIBCTunnel(),
GetTxCmdCreateIBCHookTunnel(),
GetTxCmdCreateRouterTunnel(),
GetTxCmdCreateAxelarTunnel(),
)

return txCmd
Expand Down Expand Up @@ -262,6 +263,61 @@ func GetTxCmdCreateRouterTunnel() *cobra.Command {
return cmd
}

func GetTxCmdCreateAxelarTunnel() *cobra.Command {
cmd := &cobra.Command{
Use: "axelar [destination-chain-id] [destination-contract-address] [axelar-fee] [initial-deposit] [interval] [signal-deviations-json-file]",
Short: "Create a new Axelar tunnel",
Args: cobra.ExactArgs(6),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

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

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

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

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

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

msg, err := types.NewMsgCreateAxelarTunnel(
signalInfos.ToSignalDeviations(),
interval,
destinationChainID,
destinationContractAddress,
axelarFee,
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",
Expand All @@ -275,6 +331,7 @@ func GetTxCmdUpdateRoute() *cobra.Command {
GetTxCmdUpdateIBCRoute(),
GetTxCmdUpdateIBCHookRoute(),
GetTxCmdUpdateRouterRoute(),
GetTxCmdUpdateAxelarRoute(),
)

return txCmd
Expand Down Expand Up @@ -396,6 +453,50 @@ func GetTxCmdUpdateRouterRoute() *cobra.Command {
return cmd
}

func GetTxCmdUpdateAxelarRoute() *cobra.Command {
cmd := &cobra.Command{
Use: "axelar [tunnel-id] [destination-chain-id] [destination-contract-address] [axelar-fee]",
Short: "Update Axelar route of a Axelar tunnel",
Args: cobra.ExactArgs(4),
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
}

destinationChainID := args[1]
destinationContractAddress := args[2]

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

msg, err := types.NewMsgUpdateAxelarRoute(
id,
destinationChainID,
destinationContractAddress,
axelarFee,
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] ",
Expand Down
2 changes: 2 additions & 0 deletions x/tunnel/keeper/keeper_packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ func (k Keeper) SendPacket(ctx sdk.Context, packet types.Packet) (err error) {
receipt, err = k.SendIBCHookPacket(ctx, r, packet, sdk.MustAccAddressFromBech32(tunnel.FeePayer), tunnel.Interval)
case *types.RouterRoute:
receipt, err = k.SendRouterPacket(ctx, r, packet, sdk.MustAccAddressFromBech32(tunnel.FeePayer), tunnel.Interval)
case *types.AxelarRoute:
receipt, err = k.SendAxelarPacket(ctx, r, packet, sdk.MustAccAddressFromBech32(tunnel.FeePayer), tunnel.Interval)
default:
return types.ErrInvalidRoute.Wrapf("no route found for tunnel ID: %d", tunnel.ID)
}
Expand Down
69 changes: 69 additions & 0 deletions x/tunnel/keeper/keeper_packet_axelar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
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"
)

// SendAxelarPacket sends a packet to the destination chain using Axelar
func (k Keeper) SendAxelarPacket(
ctx sdk.Context,
route *types.AxelarRoute,
packet types.Packet,
feePayer sdk.AccAddress,
interval uint64,
) (types.PacketReceiptI, error) {
// get axelar params
params := k.GetParams(ctx)
ibcChannel := params.AxelarIBCChannel
feeRecipient := params.AxelarFeeRecipient
gmpAccount := params.AxelarGMPAccount

// encode packet to abi format
payload, err := types.EncodingPacketABI(packet)
if err != nil {
return nil, err
}

// create axelar fee
feePayerStr := feePayer.String()
axelarFee := types.NewAxelarFee(route.Fee.Amount.String(), feeRecipient, &feePayerStr)

// create memo string for ibc transfer
memoStr, err := types.NewAxelarMemo(
route.DestinationChainID,
route.DestinationContractAddress,
payload,
types.AxelarMessageTypeGeneralMessage,
&axelarFee,
).String()
if err != nil {
return nil, err
}

// create ibc transfer message with the memo string
msg := ibctransfertypes.NewMsgTransfer(
ibctransfertypes.PortID,
ibcChannel,
route.Fee,
feePayer.String(),
gmpAccount,
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.NewAxelarPacketReceipt(res.Sequence), nil
}
56 changes: 56 additions & 0 deletions x/tunnel/keeper/keeper_packet_axelar_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package keeper_test

import (
"time"

"go.uber.org/mock/gomock"

ibctransfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types"

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

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

func (s *KeeperTestSuite) TestSendAxelarPacket() {
ctx, k := s.ctx, s.keeper

axelarFee := sdk.NewInt64Coin("uband", 100)

tunnelID := uint64(1)
route := &types.AxelarRoute{
DestinationChainID: "mock-chain",
DestinationContractAddress: "0x1234567890",
Fee: axelarFee,
}
packet := types.Packet{
TunnelID: tunnelID,
Sequence: 1,
Prices: []feedstypes.Price{},
CreatedAt: time.Now().Unix(),
}
interval := uint64(60)
feePayer := sdk.AccAddress([]byte("feePayer"))

expectedPacketReceipt := types.AxelarPacketReceipt{
Sequence: 1,
}

s.transferKeeper.EXPECT().Transfer(ctx, gomock.Any()).Return(&ibctransfertypes.MsgTransferResponse{
Sequence: 1,
}, nil)

content, err := k.SendAxelarPacket(
ctx,
route,
packet,
feePayer,
interval,
)
s.Require().NoError(err)

receipt, ok := content.(*types.AxelarPacketReceipt)
s.Require().True(ok)
s.Require().Equal(expectedPacketReceipt, *receipt)
}
3 changes: 2 additions & 1 deletion x/tunnel/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ func (k msgServer) UpdateRoute(
tunnel.Route = msg.Route
case *types.RouterRoute:
tunnel.Route = msg.Route

case *types.AxelarRoute:
tunnel.Route = msg.Route
default:
return nil, types.ErrInvalidRoute.Wrap("cannot update route on this route type")
}
Expand Down
Loading
Loading