Skip to content

Commit

Permalink
fix(nexus): coin type should be ICS20 if the coin is from external co…
Browse files Browse the repository at this point in the history
…smos chain (#2194)
  • Loading branch information
fish-sammy authored Oct 14, 2024
1 parent 57ecd83 commit 5d08029
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 9 deletions.
32 changes: 23 additions & 9 deletions x/nexus/keeper/lockable_asset.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import (
"github.com/axelarnetwork/utils/funcs"
)

// NewLockableAsset creates a new lockable asset
// NewLockableAsset creates a new lockable asset.
// The coin denom can either be an actual bank Coin (e.g. uaxl, ICS20 coin) ,
// or the registered asset name (e.g. base denom for an ICS20 coin)
func (k Keeper) NewLockableAsset(ctx sdk.Context, ibc types.IBCKeeper, bank types.BankKeeper, coin sdk.Coin) (exported.LockableAsset, error) {
return newLockableAsset(ctx, k, ibc, bank, coin)
}
Expand All @@ -31,14 +33,14 @@ type lockableAsset struct {
func newLockableAsset(ctx sdk.Context, nexus types.Nexus, ibc types.IBCKeeper, bank types.BankKeeper, coin sdk.Coin) (lockableAsset, error) {
denom := coin.GetDenom()

coinType, err := getCoinType(ctx, nexus, denom)
coinType, err := getCoinType(ctx, nexus, ibc, denom)
if err != nil {
return lockableAsset{}, err
}

// If coin type is ICS20, we need to normalize it to convert from 'ibc/{hash}'
// to native asset denom so that nexus could recognize it
if coinType == types.ICS20 {
if coinType == types.ICS20 && isIBCDenom(denom) {
denomTrace, err := ibc.ParseIBCDenom(ctx, denom)
if err != nil {
return lockableAsset{}, err
Expand All @@ -55,13 +57,9 @@ func newLockableAsset(ctx sdk.Context, nexus types.Nexus, ibc types.IBCKeeper, b
bank: bank,
}

originalCoin, err := c.getCoin(ctx)
if err != nil {
if _, err := c.getCoin(ctx); err != nil {
return lockableAsset{}, err
}
if originalCoin.GetDenom() != denom {
return lockableAsset{}, fmt.Errorf("denom mismatch, expected %s, got %s", denom, originalCoin.GetDenom())
}

return c, nil
}
Expand Down Expand Up @@ -176,11 +174,14 @@ func mint(ctx sdk.Context, bank types.BankKeeper, toAddr sdk.AccAddress, coin sd
return nil
}

func getCoinType(ctx sdk.Context, nexus types.Nexus, denom string) (types.CoinType, error) {
func getCoinType(ctx sdk.Context, nexus types.Nexus, ibc types.IBCKeeper, denom string) (types.CoinType, error) {
switch {
// check if the format of token denomination is 'ibc/{hash}'
case isIBCDenom(denom):
return types.ICS20, nil
// check if the denom is the registered asset name for an ICS20 coin from a cosmos chain
case isFromExternalCosmosChain(ctx, nexus, ibc, denom):
return types.ICS20, nil
case isNativeAssetOnAxelarnet(ctx, nexus, denom):
return types.Native, nil
case nexus.IsAssetRegistered(ctx, axelarnet.Axelarnet, denom):
Expand All @@ -190,6 +191,19 @@ func getCoinType(ctx sdk.Context, nexus types.Nexus, denom string) (types.CoinTy
}
}

// isFromExternalCosmosChain returns true if the denom is a nexus-registered
// asset name for an ICS20 coin originating from a cosmos chain
func isFromExternalCosmosChain(ctx sdk.Context, nexus types.Nexus, ibc types.IBCKeeper, denom string) bool {
chain, ok := nexus.GetChainByNativeAsset(ctx, denom)
if !ok {
return false
}

_, ok = ibc.GetIBCPath(ctx, chain.Name)

return ok
}

// isIBCDenom validates that the given denomination is a valid ICS token representation (ibc/{hash})
func isIBCDenom(denom string) bool {
if err := sdk.ValidateDenom(denom); err != nil {
Expand Down
34 changes: 34 additions & 0 deletions x/nexus/keeper/lockable_asset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func TestLockableAsset(t *testing.T) {

return exported.Chain{}, false
}
ibc.GetIBCPathFunc = func(ctx sdk.Context, chain exported.ChainName) (string, bool) { return "", false }
})

whenCoinIsExternal := When("coin is external", func() {
Expand All @@ -61,6 +62,27 @@ func TestLockableAsset(t *testing.T) {
}
})

whenCoinIsICS20FromExternalCosmosChain := When("coin is ICS20 from external cosmos chain", func() {
path := testutils.RandomIBCPath()
trace = ibctypes.DenomTrace{
Path: path,
BaseDenom: rand.Denom(5, 10),
}

nexus.GetChainByNativeAssetFunc = func(ctx sdk.Context, asset string) (exported.Chain, bool) {
if asset == trace.GetBaseDenom() {
return axelarnet.Axelarnet, true
}

return exported.Chain{}, false
}
ibc.GetIBCPathFunc = func(ctx sdk.Context, chain exported.ChainName) (string, bool) {
return path, chain == axelarnet.Axelarnet.Name
}

coin = sdk.NewCoin(trace.GetBaseDenom(), sdk.NewInt(rand.PosI64()))
})

whenCoinIsICS20 := When("coin is ICS20", func() {
path := testutils.RandomIBCPath()
trace = ibctypes.DenomTrace{
Expand Down Expand Up @@ -106,6 +128,18 @@ func TestLockableAsset(t *testing.T) {
}).
Run(t)

givenKeeper.
When2(whenCoinIsICS20FromExternalCosmosChain).
Then("should create a new lockable asset of type ICS20", func(t *testing.T) {
lockableAsset, err := newLockableAsset(ctx, nexus, ibc, bank, coin)

assert.NoError(t, err)
assert.Equal(t, types.CoinType(types.ICS20), lockableAsset.coinType)
assert.Equal(t, coin, lockableAsset.GetAsset())
assert.Equal(t, sdk.NewCoin(trace.IBCDenom(), coin.Amount), lockableAsset.GetCoin(ctx))
}).
Run(t)

givenKeeper.
When2(whenCoinIsNative).
Then("should create a new lockable asset of type native", func(t *testing.T) {
Expand Down

0 comments on commit 5d08029

Please sign in to comment.