Skip to content

Commit d03df0e

Browse files
danwtclaude
andauthored
feat(hyperlane): support HL-to-HL forwarding in x/forward module (#1944)
* feat: add support for HL-to-HL forwarding in x/forward module - Added hook_forward_to_hl field to HLMetadata proto - Implemented UnpackForwardToHL and MakeHLForwardToHLMetadata functions - Updated OnHyperlaneMessage to handle HL-to-HL forwarding - Added comprehensive unit tests for HL-to-HL forwarding - Maintains backward compatibility with existing HL-to-IBC forwarding 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: enforce at most one forward type in HLMetadata Add ValidateBasic() to HLMetadata to ensure only one forward type (IBC or HL) can be populated at a time. The kaspa field is excluded from this check as it's orthogonal to forwarding logic. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: add CLI command for HL-to-HL forwarding memo creation - Add CmdMemoHLtoHLRaw command to create Hyperlane-to-Hyperlane forwarding memos - Update CmdDecodeHyperlaneMessage to handle both IBC and HL forwarding types - Follows existing pattern for other memo creation commands 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fmt * remove busted test --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 96889d2 commit d03df0e

File tree

7 files changed

+343
-33
lines changed

7 files changed

+343
-33
lines changed

proto/dymensionxyz/dymension/forward/dt.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,7 @@ message HLMetadata {
2525
// see
2626
// https://www.notion.so/dymension/ADR-Kaspa-Bridge-Implementation-206a4a51f86a803980aec7099c826fb4?source=copy_link#208a4a51f86a8093a843cf4b5e903588
2727
bytes kaspa = 2;
28+
29+
// optional, can be empty
30+
bytes hook_forward_to_hl = 3;
2831
}

x/forward/cli/util.go

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func GetQueryCmd() *cobra.Command {
3737
cmd.AddCommand(CmdMemoEIBCtoHL())
3838
cmd.AddCommand(CmdMemoEIBCtoIBC())
3939
cmd.AddCommand(CmdMemoHLtoIBCRaw())
40+
cmd.AddCommand(CmdMemoHLtoHLRaw())
4041
cmd.AddCommand(CmdHLEthTransferRecipientHubAccount())
4142
cmd.AddCommand(CmdTestHLtoIBCMessage())
4243
cmd.AddCommand(CmdTestHLMessageKaspa())
@@ -265,6 +266,53 @@ func CmdMemoHLtoIBCRaw() *cobra.Command {
265266
return cmd
266267
}
267268

269+
// Get a memo for the direction HL -> HL
270+
func CmdMemoHLtoHLRaw() *cobra.Command {
271+
cmd := &cobra.Command{
272+
Use: "memo-hl-to-hl [token-id] [destination-domain] [hl-recipient] [hl-amount] [max-hl-fee]",
273+
Args: cobra.ExactArgs(5),
274+
Short: "Get the memo for the direction HL -> HL",
275+
Example: `dymd q forward memo-hl-to-hl 0x934b867052ca9c65e33362112f35fb548f8732c2fe45f07b9c591958e865def0 1 0x934b867052ca9c65e33362112f35fb548f8732c2fe45f07b9c591958e865def0 10000 20foo`,
276+
DisableFlagParsing: true,
277+
SuggestionsMinimumDistance: 2,
278+
RunE: func(cmd *cobra.Command, args []string) error {
279+
hook, err := hookForwardToHL(args)
280+
if err != nil {
281+
return fmt.Errorf("hook forward to hl: %w", err)
282+
}
283+
284+
readable, err := cmd.Flags().GetBool(MessageReadableFlag)
285+
if err != nil {
286+
return fmt.Errorf("readable flag: %w", err)
287+
}
288+
289+
if readable {
290+
fmt.Printf("hyperlane message: %+v\n", hook)
291+
} else {
292+
bz, err := proto.Marshal(hook)
293+
if err != nil {
294+
return fmt.Errorf("marshal: %w", err)
295+
}
296+
hlMetadata := &types.HLMetadata{
297+
HookForwardToHl: bz,
298+
}
299+
bz, err = proto.Marshal(hlMetadata)
300+
if err != nil {
301+
return fmt.Errorf("marshal: %w", err)
302+
}
303+
fmt.Printf("%s\n", util.EncodeEthHex(bz))
304+
}
305+
306+
return nil
307+
},
308+
}
309+
310+
cmd.Flags().Bool(MessageReadableFlag, false, "Show the message in a human readable format (for debug)")
311+
flags.AddQueryFlagsToCmd(cmd)
312+
313+
return cmd
314+
}
315+
268316
func hookForwardToIBC(args []string) (*types.HookForwardToIBC, error) {
269317
ibcSourceChan := args[0]
270318

@@ -558,11 +606,23 @@ func CmdDecodeHyperlaneMessage() *cobra.Command {
558606
}
559607
fmt.Printf("hl metadata: %+v\n", hlMetadata)
560608

561-
m, err := types.UnpackForwardToIBC(hlMetadata.HookForwardToIbc)
562-
if err != nil {
563-
return fmt.Errorf("unpack memo from warp message: %w", err)
609+
// Check for IBC forward
610+
if len(hlMetadata.HookForwardToIbc) > 0 {
611+
m, err := types.UnpackForwardToIBC(hlMetadata.HookForwardToIbc)
612+
if err != nil {
613+
return fmt.Errorf("unpack ibc forward from warp message: %w", err)
614+
}
615+
fmt.Printf("ibc forward: %+v\n", m)
616+
}
617+
618+
// Check for HL forward
619+
if len(hlMetadata.HookForwardToHl) > 0 {
620+
m, err := types.UnpackForwardToHL(hlMetadata.HookForwardToHl)
621+
if err != nil {
622+
return fmt.Errorf("unpack hl forward from warp message: %w", err)
623+
}
624+
fmt.Printf("hl forward: %+v\n", m)
564625
}
565-
fmt.Printf("ibc memo: %+v\n", m)
566626
}
567627
fmt.Printf("warp payload message: %+v\n", warpPL)
568628
fmt.Printf("cosmos account: %s\n", warpPL.GetCosmosAccount().String())

x/forward/hyperlane.go

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,36 @@ func (k Forward) OnHyperlaneMessage(goCtx context.Context, args warpkeeper.OnHyp
2323
if err != nil {
2424
return false, errorsmod.Wrap(err, "unpack hl metadata")
2525
}
26-
if hlMetadata == nil || len(hlMetadata.HookForwardToIbc) == 0 {
26+
if hlMetadata == nil {
2727
// Equivalent to the vanilla token standard.
2828
return false, nil
2929
}
3030

31-
d, err := types.UnpackForwardToIBC(hlMetadata.HookForwardToIbc)
32-
if err != nil {
33-
return true, errorsmod.Wrap(err, "unpack memo from hyperlane")
31+
// Check for HL-to-HL forwarding first
32+
if len(hlMetadata.HookForwardToHl) > 0 {
33+
d, err := types.UnpackForwardToHL(hlMetadata.HookForwardToHl)
34+
if err != nil {
35+
return true, errorsmod.Wrap(err, "unpack hl to hl forward from hyperlane")
36+
}
37+
38+
// funds src is the hyperlane transfer recipient
39+
return true, k.forwardToHyperlane(ctx, args.Account, args.Coin(), *d)
40+
}
41+
42+
// Check for HL-to-IBC forwarding
43+
if len(hlMetadata.HookForwardToIbc) > 0 {
44+
d, err := types.UnpackForwardToIBC(hlMetadata.HookForwardToIbc)
45+
if err != nil {
46+
return true, errorsmod.Wrap(err, "unpack memo from hyperlane")
47+
}
48+
49+
// funds src is the hyperlane transfer recipient, which should have same priv key as rollapp recipient
50+
// so in case of async failure, the funds will get refunded back there.
51+
return true, k.forwardToIBC(ctx, d.Transfer, args.Account, args.Coin())
3452
}
3553

36-
// funds src is the hyperlane transfer recipient, which should have same priv key as rollapp recipient
37-
// so in case of async failure, the funds will get refunded back there.
38-
return true, k.forwardToIBC(ctx, d.Transfer, args.Account, args.Coin())
54+
// No forwarding configured
55+
return false, nil
3956
})
4057

4158
return nil

x/forward/types/dt.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,32 @@
11
package types
22

33
import (
4+
errorsmod "cosmossdk.io/errors"
45
proto "github.com/cosmos/gogoproto/proto"
6+
"github.com/dymensionxyz/gerr-cosmos/gerrc"
57
)
68

9+
// ValidateBasic validates that at most one forward type is populated (excluding kaspa which is orthogonal)
10+
func (m *HLMetadata) ValidateBasic() error {
11+
populatedCount := 0
12+
13+
if len(m.HookForwardToIbc) > 0 {
14+
populatedCount++
15+
}
16+
17+
if len(m.HookForwardToHl) > 0 {
18+
populatedCount++
19+
}
20+
21+
// Note: kaspa field is orthogonal and not counted
22+
23+
if populatedCount > 1 {
24+
return gerrc.ErrInvalidArgument.Wrap("at most one forward type can be populated in HLMetadata")
25+
}
26+
27+
return nil
28+
}
29+
730
func UnpackHLMetadata(metadata []byte) (*HLMetadata, error) {
831
if len(metadata) == 0 {
932
return nil, nil
@@ -14,5 +37,11 @@ func UnpackHLMetadata(metadata []byte) (*HLMetadata, error) {
1437
if err != nil {
1538
return nil, err
1639
}
40+
41+
// Validate the metadata
42+
if err := x.ValidateBasic(); err != nil {
43+
return nil, errorsmod.Wrap(err, "validate HLMetadata")
44+
}
45+
1746
return &x, nil
1847
}

x/forward/types/dt.pb.go

Lines changed: 77 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)