Skip to content

Commit e8721c2

Browse files
committed
Merge branch 'main' into fork/keruch/keruch/igp-zero-fee
# Conflicts: # x/core/02_post_dispatch/keeper/hook_igp_test.go
2 parents 9ca9872 + ccb6647 commit e8721c2

9 files changed

Lines changed: 534 additions & 82 deletions

File tree

proto/buf.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ lint:
1717
- SERVICE_SUFFIX
1818
- PACKAGE_VERSION_SUFFIX
1919
- RPC_REQUEST_STANDARD_NAME
20+
- RPC_REQUEST_RESPONSE_UNIQUE
21+
- RPC_RESPONSE_STANDARD_NAME

proto/hyperlane/warp/v1/tx.proto

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ service Msg {
2121
// CreateSyntheticToken ...
2222
rpc CreateSyntheticToken(MsgCreateSyntheticToken)
2323
returns (MsgCreateSyntheticTokenResponse);
24+
// CreateNativeSyntheticToken ...
25+
rpc CreateNativeSyntheticToken(MsgCreateNativeSyntheticToken)
26+
returns (MsgCreateSyntheticTokenResponse);
2427

2528
// SetToken ...
2629
rpc SetToken(MsgSetToken) returns (MsgSetTokenResponse);
@@ -77,6 +80,22 @@ message MsgCreateSyntheticToken {
7780
];
7881
}
7982

83+
// MsgCreateNativeSyntheticToken ...
84+
message MsgCreateNativeSyntheticToken {
85+
option (cosmos.msg.v1.signer) = "owner";
86+
option (amino.name) = "hyperlane/warp/v1/MsgCreateNativeSyntheticToken";
87+
88+
string owner = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
89+
90+
string origin_mailbox = 2 [
91+
(gogoproto.customtype) =
92+
"github.com/bcp-innovations/hyperlane-cosmos/util.HexAddress",
93+
(gogoproto.nullable) = false
94+
];
95+
96+
string origin_denom = 3;
97+
}
98+
8099
// MsgCreateSyntheticTokenResponse ...
81100
message MsgCreateSyntheticTokenResponse {
82101
string id = 1 [

x/core/02_post_dispatch/keeper/hook_igp.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func (i InterchainGasPaymasterHookHandler) PayForGas(ctx context.Context, hookId
5252
return sdk.NewCoins(), err
5353
}
5454

55-
if requiredPayment.IsAllGT(maxFee) {
55+
if !maxFee.IsAllGTE(requiredPayment) {
5656
return sdk.NewCoins(), fmt.Errorf("required payment exceeds max hyperlane fee: %v", requiredPayment)
5757
}
5858

x/core/02_post_dispatch/keeper/hook_igp_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,37 @@ var _ = Describe("hook_igp_test.go", Ordered, func() {
100100
_, err = s.App().HyperlaneKeeper.PostDispatch(s.Ctx(), mailboxId, hookId, metadata, message, maxFee)
101101
Expect(err).To(BeNil())
102102
})
103+
104+
It("IGP PayForGas should fail when any required payment exceeds maxFee", func() {
105+
recipient, err := util.DecodeHexAddress("0x00000000000000000000000000000000000000000000000000000000deadbeef")
106+
Expect(err).To(BeNil())
107+
108+
sender, err := util.DecodeHexAddress("0x0000000000000000000000007fa9385be102ac3eac297483dd6233d62b3e1496")
109+
Expect(err).To(BeNil())
110+
111+
message := util.HyperlaneMessage{
112+
Version: 1,
113+
Nonce: 0,
114+
Origin: 1,
115+
Sender: sender,
116+
Destination: 2,
117+
Recipient: recipient,
118+
Body: []byte("test"),
119+
}
120+
121+
metadata := util.StandardHookMetadata{
122+
Address: creator.AccAddress,
123+
GasLimit: math.NewInt(100000),
124+
CustomHookMetadata: nil,
125+
}
126+
127+
// igpDenom != actualDenom
128+
maxFee := sdk.NewCoins(sdk.NewCoin(actualDenom, math.NewInt(50)))
129+
130+
_, err = s.App().HyperlaneKeeper.PostDispatch(s.Ctx(), mailboxId, hookId, metadata, message, maxFee)
131+
Expect(err).To(HaveOccurred())
132+
Expect(err.Error()).To(ContainSubstring("required payment exceeds max hyperlane fee"))
133+
})
103134
})
104135

105136
func createDummyIgp(s *i.KeeperTestSuite, creator string, denom string) (util.HexAddress, error) {

x/warp/keeper/logic_collateral.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func (k *Keeper) RemoteTransferCollateral(ctx sdk.Context, token types.HypToken,
8888
// RemoteReceiveCollateral handles the receipt of collateral from a remote chain.
8989
// It updates the token balance, verifies there is enough collateral, and transfers the funds to the recipient's account.
9090
func (k *Keeper) RemoteReceiveCollateral(ctx context.Context, token types.HypToken, payload types.WarpPayload) error {
91-
account := sdk.AccAddress(payload.Recipient()[12:32])
91+
account := payload.GetCosmosAccount()
9292

9393
amount := math.NewIntFromBigInt(payload.Amount())
9494

x/warp/keeper/logic_collateral_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
coreTypes "github.com/bcp-innovations/hyperlane-cosmos/x/core/types"
1212
"github.com/bcp-innovations/hyperlane-cosmos/x/warp/types"
1313
sdk "github.com/cosmos/cosmos-sdk/types"
14+
"github.com/cosmos/cosmos-sdk/types/address"
1415
. "github.com/onsi/ginkgo/v2"
1516
. "github.com/onsi/gomega"
1617
)
@@ -29,6 +30,7 @@ TEST CASES - logic_collateral.go
2930
* MsgRemoteTransfer (invalid) insufficient funds (Collateral)
3031
* MsgRemoteTransfer & MsgRemoteReceiveCollateral (invalid) not enough collateral (Collateral)
3132
* MsgRemoteTransfer && MsgRemoteReceiveCollateral (valid) (Collateral)
33+
* MsgRemoteTransfer && MsgRemoteReceiveCollateral (valid) 32-byte address (Collateral)
3234
3335
*/
3436

@@ -366,4 +368,67 @@ var _ = Describe("logic_collateral.go", Ordered, func() {
366368
Expect(err).To(BeNil())
367369
Expect(s.App().BankKeeper.GetBalance(s.Ctx(), sender.AccAddress, denom).Amount).To(Equal(senderBalance.Amount.Add(amount)))
368370
})
371+
372+
It("MsgRemoteTransfer && MsgRemoteReceiveCollateral (valid) 32-byte address (Collateral)", func() {
373+
// Arrange
374+
receiverAddress, _ := util.DecodeHexAddress("0xd7194459d45619d04a5a0f9e78dc9594a0f37fd6da8382fe12ddda6f2f46d647")
375+
remoteRouter := types.RemoteRouter{
376+
ReceiverDomain: 1,
377+
ReceiverContract: "0x934b867052ca9c65e33362112f35fb548f8732c2fe45f07b9c591958e865def0",
378+
Gas: math.NewInt(50000),
379+
}
380+
381+
amount := math.NewInt(100)
382+
maxFee := sdk.NewCoin(denom, math.NewInt(250000))
383+
384+
tokenId, mailboxId, igpId, _ := createToken(s, &remoteRouter, owner.Address, sender.Address, types.HYP_TOKEN_TYPE_COLLATERAL)
385+
err := s.MintBaseCoins(sender.Address, 1_000_000)
386+
Expect(err).To(BeNil())
387+
388+
// Act
389+
_, err = s.RunTx(&types.MsgRemoteTransfer{
390+
Sender: sender.Address,
391+
TokenId: tokenId,
392+
DestinationDomain: remoteRouter.ReceiverDomain,
393+
Recipient: receiverAddress,
394+
Amount: amount,
395+
CustomHookId: &igpId,
396+
GasLimit: math.ZeroInt(),
397+
MaxFee: maxFee,
398+
})
399+
Expect(err).To(BeNil())
400+
401+
receiverContract, err := util.DecodeHexAddress(remoteRouter.ReceiverContract)
402+
Expect(err).To(BeNil())
403+
404+
warpRecipient := address.Module(types.ModuleName, []byte("collateral-receiver"))
405+
Expect(len(warpRecipient)).To(Equal(address.Len))
406+
407+
warpPayload, err := types.NewWarpPayload(warpRecipient, *big.NewInt(amount.Int64()))
408+
Expect(err).To(BeNil())
409+
410+
message := util.HyperlaneMessage{
411+
Version: 3,
412+
Nonce: 1,
413+
Origin: remoteRouter.ReceiverDomain,
414+
Sender: receiverContract,
415+
Destination: 0,
416+
Recipient: tokenId,
417+
Body: warpPayload.Bytes(),
418+
}
419+
420+
receiverAcc := sdk.AccAddress(warpRecipient)
421+
receiverBalance := s.App().BankKeeper.GetBalance(s.Ctx(), receiverAcc, denom)
422+
423+
_, err = s.RunTx(&coreTypes.MsgProcessMessage{
424+
MailboxId: mailboxId,
425+
Relayer: sender.Address,
426+
Metadata: "",
427+
Message: message.String(),
428+
})
429+
430+
// Assert
431+
Expect(err).To(BeNil())
432+
Expect(s.App().BankKeeper.GetBalance(s.Ctx(), receiverAcc, denom).Amount).To(Equal(receiverBalance.Amount.Add(amount)))
433+
})
369434
})

x/warp/keeper/msg_server.go

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,54 @@ type msgServer struct {
1919

2020
// CreateSyntheticToken ...
2121
func (ms msgServer) CreateSyntheticToken(ctx context.Context, msg *types.MsgCreateSyntheticToken) (*types.MsgCreateSyntheticTokenResponse, error) {
22+
return ms.internalCreateSyntheticToken(ctx, msg.Owner, msg.OriginMailbox, "")
23+
}
24+
25+
// CreateNativeSyntheticToken is an authority gated version of
26+
// CreateSyntheticToken that allows the module authority to specify a native
27+
// (custom) origin denom for the synthetic token.
28+
func (ms msgServer) CreateNativeSyntheticToken(ctx context.Context, msg *types.MsgCreateNativeSyntheticToken) (*types.MsgCreateSyntheticTokenResponse, error) {
29+
if msg.Owner != ms.k.authority {
30+
return nil, fmt.Errorf("invalid authority: expected %s, got %s", ms.k.authority, msg.Owner)
31+
}
32+
33+
if ms.k.bankKeeper.HasSupply(ctx, msg.OriginDenom) {
34+
return nil, fmt.Errorf("denom %s already exists", msg.OriginDenom)
35+
}
36+
37+
return ms.internalCreateSyntheticToken(ctx, msg.Owner, msg.OriginMailbox, msg.OriginDenom)
38+
}
39+
40+
// internalCreateSyntheticToken handles the internal logic for creating a synthetic token.
41+
func (ms msgServer) internalCreateSyntheticToken(ctx context.Context, owner string, originMailbox util.HexAddress, originDenom string) (*types.MsgCreateSyntheticTokenResponse, error) {
2242
if !slices.Contains(ms.k.enabledTokens, int32(types.HYP_TOKEN_TYPE_SYNTHETIC)) {
2343
return nil, fmt.Errorf("module disabled synthetic tokens")
2444
}
2545

26-
has, err := ms.k.coreKeeper.MailboxIdExists(ctx, msg.OriginMailbox)
46+
has, err := ms.k.coreKeeper.MailboxIdExists(ctx, originMailbox)
2747
if err != nil {
2848
return nil, err
2949
}
3050
if !has {
31-
return nil, fmt.Errorf("failed to find mailbox with id: %s", msg.OriginMailbox.String())
51+
return nil, fmt.Errorf("failed to find mailbox with id: %s", originMailbox.String())
3252
}
3353

3454
tokenId, err := ms.k.coreKeeper.AppRouter().GetNextSequence(ctx, uint8(types.HYP_TOKEN_TYPE_SYNTHETIC))
3555
if err != nil {
3656
return nil, err
3757
}
3858

59+
// If the originDenom is left empty, we populate it as a non-native denom.
60+
if originDenom == "" {
61+
originDenom = fmt.Sprintf("hyperlane/%s", tokenId.String())
62+
}
63+
3964
newToken := types.HypToken{
4065
Id: tokenId,
41-
Owner: msg.Owner,
66+
Owner: owner,
4267
TokenType: types.HYP_TOKEN_TYPE_SYNTHETIC,
43-
OriginMailbox: msg.OriginMailbox,
44-
OriginDenom: fmt.Sprintf("hyperlane/%s", tokenId.String()),
68+
OriginMailbox: originMailbox,
69+
OriginDenom: originDenom,
4570
}
4671

4772
if err = ms.k.HypTokens.Set(ctx, tokenId.GetInternalId(), newToken); err != nil {

x/warp/types/expected_keepers.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type BankKeeper interface {
1414
BurnCoins(ctx context.Context, moduleName string, amt sdk.Coins) error
1515
MintCoins(ctx context.Context, moduleName string, amt sdk.Coins) error
1616
GetSupply(ctx context.Context, denom string) sdk.Coin
17+
HasSupply(ctx context.Context, denom string) bool
1718
}
1819

1920
type CoreKeeper interface {

0 commit comments

Comments
 (0)