Skip to content

Commit c4ed53d

Browse files
committed
imp(transfer)!: use AddressCodec instead of Bech32 (backport #8573)
* imp: added address codec to transfer * imp: address codec used in transfer * fix: tests * imp: test passing * imp: added addressCodec to PFM * imp: added receiver test * test: transfer cov * test: added cases * test: more cov * test: more cov * style: single line func def * test: new test works * imp: add basic test * imp: added migration guide * doc: added changelog * refactor: address codec moved
1 parent 0b90af4 commit c4ed53d

File tree

10 files changed

+102
-26
lines changed

10 files changed

+102
-26
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ Ref: https://keepachangelog.com/en/1.0.0/
3434

3535
# Changelog
3636

37+
## [Unreleased]
38+
39+
* [\#8573](https://github.com/cosmos/ibc-go/pull/8573) Support custom address codecs in transfer.
40+
3741
## [v10.3.0](https://github.com/cosmos/ibc-go/releases/tag/v10.3.0) - 2025-06-06
3842

3943
### Features

modules/apps/transfer/keeper/keeper.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"strings"
77

8+
"cosmossdk.io/core/address"
89
corestore "cosmossdk.io/core/store"
910
"cosmossdk.io/log"
1011
sdkmath "cosmossdk.io/math"
@@ -28,6 +29,7 @@ type Keeper struct {
2829
storeService corestore.KVStoreService
2930
cdc codec.BinaryCodec
3031
legacySubspace types.ParamSubspace
32+
addressCodec address.Codec
3133

3234
ics4Wrapper porttypes.ICS4Wrapper
3335
channelKeeper types.ChannelKeeper
@@ -91,6 +93,16 @@ func (k Keeper) GetAuthority() string {
9193
return k.authority
9294
}
9395

96+
// SetAddressCodec sets the address codec used by the keeper.
97+
func (k *Keeper) SetAddressCodec(addressCodec address.Codec) {
98+
k.addressCodec = addressCodec
99+
}
100+
101+
// GetAddressCodec returns the address codec used by the keeper.
102+
func (k *Keeper) GetAddressCodec() address.Codec {
103+
return k.addressCodec
104+
}
105+
94106
// Logger returns a module-specific logger.
95107
func (Keeper) Logger(ctx sdk.Context) log.Logger {
96108
return ctx.Logger().With("module", "x/"+exported.ModuleName+"-"+types.ModuleName)

modules/apps/transfer/keeper/msg_server.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.
2727
return nil, types.ErrSendDisabled
2828
}
2929

30-
sender, err := sdk.AccAddressFromBech32(msg.Sender)
30+
var sender sdk.AccAddress
31+
var err error
32+
if k.addressCodec != nil {
33+
sender, err = k.addressCodec.StringToBytes(msg.Sender)
34+
} else {
35+
sender, err = sdk.AccAddressFromBech32(msg.Sender)
36+
}
3137
if err != nil {
3238
return nil, err
3339
}
@@ -47,7 +53,7 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.
4753
return nil, err
4854
}
4955

50-
packetData := types.NewFungibleTokenPacketData(token.Denom.Path(), token.Amount, sender.String(), msg.Receiver, msg.Memo)
56+
packetData := types.NewFungibleTokenPacketData(token.Denom.Path(), token.Amount, msg.Sender, msg.Receiver, msg.Memo)
5157

5258
if err := packetData.ValidateBasic(); err != nil {
5359
return nil, errorsmod.Wrapf(err, "failed to validate %s packet data", types.V1)
@@ -60,7 +66,7 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.
6066
var sequence uint64
6167
if isIBCV1 {
6268
// if a V1 channel exists for the source channel, then use IBC V1 protocol
63-
sequence, err = k.transferV1Packet(ctx, msg.SourceChannel, token, msg.TimeoutHeight, msg.TimeoutTimestamp, packetData)
69+
sequence, err = k.transferV1Packet(ctx, msg.SourceChannel, token, msg.TimeoutHeight, msg.TimeoutTimestamp, sender, packetData)
6470
// telemetry for transfer occurs here, in IBC V2 this is done in the onSendPacket callback
6571
telemetry.ReportTransfer(msg.SourcePort, msg.SourceChannel, channel.Counterparty.PortId, channel.Counterparty.ChannelId, token)
6672
} else {
@@ -77,8 +83,8 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.
7783
return &types.MsgTransferResponse{Sequence: sequence}, nil
7884
}
7985

80-
func (k Keeper) transferV1Packet(ctx sdk.Context, sourceChannel string, token types.Token, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, packetData types.FungibleTokenPacketData) (uint64, error) {
81-
if err := k.SendTransfer(ctx, types.PortID, sourceChannel, token, sdk.MustAccAddressFromBech32(packetData.Sender)); err != nil {
86+
func (k *Keeper) transferV1Packet(ctx sdk.Context, sourceChannel string, token types.Token, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, sender sdk.AccAddress, packetData types.FungibleTokenPacketData) (uint64, error) {
87+
if err := k.SendTransfer(ctx, types.PortID, sourceChannel, token, sender); err != nil {
8288
return 0, err
8389
}
8490

modules/apps/transfer/keeper/msg_server_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ func (suite *KeeperTestSuite) TestMsgTransfer() {
6161
},
6262
types.ErrSendDisabled,
6363
},
64+
{
65+
"failure: zero amount",
66+
func() {
67+
msg.Token = sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)
68+
},
69+
types.ErrInvalidAmount,
70+
},
6471
{
6572
"failure: invalid sender",
6673
func() {

modules/apps/transfer/keeper/relay.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,13 @@ func (k Keeper) OnRecvPacket(
127127
return types.ErrReceiveDisabled
128128
}
129129

130-
receiver, err := sdk.AccAddressFromBech32(data.Receiver)
130+
var receiver sdk.AccAddress
131+
var err error
132+
if k.addressCodec != nil {
133+
receiver, err = k.addressCodec.StringToBytes(data.Receiver)
134+
} else {
135+
receiver, err = sdk.AccAddressFromBech32(data.Receiver)
136+
}
131137
if err != nil {
132138
return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "failed to decode receiver address: %s", data.Receiver)
133139
}
@@ -195,7 +201,7 @@ func (k Keeper) OnRecvPacket(
195201
if err := k.BankKeeper.SendCoins(
196202
ctx, moduleAddr, receiver, sdk.NewCoins(voucher),
197203
); err != nil {
198-
return errorsmod.Wrapf(err, "failed to send coins to receiver %s", receiver.String())
204+
return errorsmod.Wrapf(err, "failed to send coins to receiver %s", data.Receiver)
199205
}
200206

201207
}
@@ -253,7 +259,13 @@ func (k Keeper) refundPacketTokens(
253259
) error {
254260
// NOTE: packet data type already checked in handler.go
255261

256-
sender, err := sdk.AccAddressFromBech32(data.Sender)
262+
var sender sdk.AccAddress
263+
var err error
264+
if k.addressCodec != nil {
265+
sender, err = k.addressCodec.StringToBytes(data.Sender)
266+
} else {
267+
sender, err = sdk.AccAddressFromBech32(data.Sender)
268+
}
257269
if err != nil {
258270
return err
259271
}

modules/apps/transfer/keeper/relay_test.go

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package keeper_test
22

33
import (
4+
"encoding/hex"
45
"errors"
56
"fmt"
67
"strings"
@@ -47,19 +48,19 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
4748
expError error
4849
}{
4950
{
50-
"successful transfer of native token",
51+
"success: transfer of native token",
5152
func() {},
5253
nil,
5354
},
5455
{
55-
"successful transfer of native token with memo",
56+
"success: transfer of native token with memo",
5657
func() {
5758
memo = "memo" //nolint:goconst
5859
},
5960
nil,
6061
},
6162
{
62-
"successful transfer of IBC token",
63+
"success: transfer of IBC token",
6364
func() {
6465
// send IBC token back to chainB
6566
denom := types.NewDenom(ibctesting.TestCoin.Denom, types.NewHop(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID))
@@ -70,7 +71,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
7071
nil,
7172
},
7273
{
73-
"successful transfer of IBC token with memo",
74+
"success: transfer of IBC token with memo",
7475
func() {
7576
// send IBC token back to chainB
7677
denom := types.NewDenom(ibctesting.TestCoin.Denom, types.NewHop(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID))
@@ -82,7 +83,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
8283
nil,
8384
},
8485
{
85-
"successful transfer of entire balance",
86+
"success: transfer of entire balance",
8687
func() {
8788
coin = sdk.NewCoin(coin.Denom, types.UnboundedSpendLimit())
8889
var ok bool
@@ -92,7 +93,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
9293
nil,
9394
},
9495
{
95-
"successful transfer of entire spendable balance with vesting account",
96+
"success: transfer of entire spendable balance with vesting account",
9697
func() {
9798
// create vesting account
9899
vestingAccPrivKey := secp256k1.GenPrivKey()
@@ -325,17 +326,27 @@ func (suite *KeeperTestSuite) TestOnRecvPacket_ReceiverIsNotSource() {
325326
expError error
326327
}{
327328
{
328-
"successful receive",
329+
"success: receive",
329330
func() {},
330331
nil,
331332
},
332333
{
333-
"successful receive with memo",
334+
"success: receive with memo",
334335
func() {
335336
packetData.Memo = "memo"
336337
},
337338
nil,
338339
},
340+
{
341+
"success: receive with hex receiver address",
342+
func() {
343+
s.chainB.GetSimApp().TransferKeeper.SetAddressCodec(ibcmock.TestAddressCodec{})
344+
345+
receiver := sdk.MustAccAddressFromBech32(packetData.Receiver)
346+
packetData.Receiver = hex.EncodeToString(receiver.Bytes())
347+
},
348+
nil,
349+
},
339350
{
340351
"failure: mint zero coin",
341352
func() {

modules/apps/transfer/types/msgs.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,8 @@ func NewMsgUpdateParams(signer string, params Params) *MsgUpdateParams {
3434

3535
// ValidateBasic implements sdk.Msg
3636
func (msg MsgUpdateParams) ValidateBasic() error {
37-
_, err := sdk.AccAddressFromBech32(msg.Signer)
38-
if err != nil {
39-
return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err)
37+
if strings.TrimSpace(msg.Signer) == "" {
38+
return errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "missing sender address")
4039
}
4140

4241
return nil
@@ -96,9 +95,8 @@ func (msg MsgTransfer) ValidateBasic() error {
9695
return errorsmod.Wrap(ibcerrors.ErrInvalidCoins, msg.Token.String())
9796
}
9897

99-
_, err := sdk.AccAddressFromBech32(msg.Sender)
100-
if err != nil {
101-
return errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err)
98+
if strings.TrimSpace(msg.Sender) == "" {
99+
return errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "missing sender address")
102100
}
103101
if strings.TrimSpace(msg.Receiver) == "" {
104102
return errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "missing recipient address")

modules/apps/transfer/types/msgs_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ func TestMsgUpdateParamsValidateBasic(t *testing.T) {
111111
expError error
112112
}{
113113
{"success: valid signer and valid params", types.NewMsgUpdateParams(ibctesting.TestAccAddress, types.DefaultParams()), nil},
114-
{"failure: invalid signer with valid params", types.NewMsgUpdateParams(invalidAddress, types.DefaultParams()), ibcerrors.ErrInvalidAddress},
115114
{"failure: empty signer with valid params", types.NewMsgUpdateParams(emptyAddr, types.DefaultParams()), ibcerrors.ErrInvalidAddress},
116115
}
117116

modules/apps/transfer/v2/ibc_module.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ func (im *IBCModule) OnSendPacket(ctx sdk.Context, sourceChannel string, destina
5252
return err
5353
}
5454

55-
sender, err := sdk.AccAddressFromBech32(data.Sender)
55+
sender, err := im.keeper.GetAddressCodec().StringToBytes(data.Sender)
5656
if err != nil {
5757
return err
5858
}
5959

60-
if !signer.Equals(sender) {
60+
if !bytes.Equal(sender, signer) {
6161
return errorsmod.Wrapf(ibcerrors.ErrUnauthorized, "sender %s is different from signer %s", sender, signer)
6262
}
6363

@@ -76,7 +76,7 @@ func (im *IBCModule) OnSendPacket(ctx sdk.Context, sourceChannel string, destina
7676
return err
7777
}
7878

79-
events.EmitTransferEvent(ctx, sender.String(), data.Receiver, data.Token, data.Memo)
79+
events.EmitTransferEvent(ctx, data.Sender, data.Receiver, data.Token, data.Memo)
8080

8181
telemetry.ReportTransfer(payload.SourcePort, sourceChannel, payload.DestinationPort, destinationChannel, data.Token)
8282

testing/mock/address_codec.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package mock
2+
3+
import (
4+
"errors"
5+
6+
sdk "github.com/cosmos/cosmos-sdk/types"
7+
)
8+
9+
type TestAddressCodec struct{}
10+
11+
func (t TestAddressCodec) StringToBytes(text string) ([]byte, error) {
12+
hexBytes, err := sdk.AccAddressFromHexUnsafe(text)
13+
if err == nil {
14+
return hexBytes, nil
15+
}
16+
17+
bech32Bytes, err := sdk.AccAddressFromBech32(text)
18+
if err == nil {
19+
return bech32Bytes, nil
20+
}
21+
22+
return nil, errors.New("invalid address format")
23+
}
24+
25+
func (t TestAddressCodec) BytesToString(bz []byte) (string, error) {
26+
return sdk.AccAddress(bz).String(), nil
27+
}

0 commit comments

Comments
 (0)