mukan-ibc/e2e/tests/transfer/base_test.go
Mukan Erkin Törük 88dd97a9f8
Some checks failed
CodeQL / Analyze (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Has been cancelled
refactor: replace all github.com upstream refs with git.cw.tr/mukan-network
2026-05-11 03:36:22 +03:00

406 lines
16 KiB
Go

//go:build !test_e2e
package transfer
import (
"context"
"testing"
"time"
"github.com/cosmos/interchaintest/v10/ibc"
test "github.com/cosmos/interchaintest/v10/testutil"
testifysuite "github.com/stretchr/testify/suite"
sdkmath "cosmossdk.io/math"
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
"github.com/cosmos/ibc-go/e2e/testsuite"
"github.com/cosmos/ibc-go/e2e/testsuite/query"
"github.com/cosmos/ibc-go/e2e/testvalues"
transfertypes "git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/types"
)
// compatibility:from_version: v7.10.0
func TestTransferTestSuite(t *testing.T) {
testifysuite.Run(t, new(TransferTestSuite))
}
type TransferTestSuite struct {
transferTester
}
// SetupSuite sets up chains for the current test suite
func (s *TransferTestSuite) SetupSuite() {
s.SetupChains(context.TODO(), 2, nil)
}
// transferTester defines some helper functions that can be used in various test suites
// that test transfer functionality.
type transferTester struct {
testsuite.E2ETestSuite
}
// QueryTransferParams queries the on-chain send enabled param for the transfer module
func (s *transferTester) QueryTransferParams(ctx context.Context, chain ibc.Chain) transfertypes.Params {
res, err := query.GRPCQuery[transfertypes.QueryParamsResponse](ctx, chain, &transfertypes.QueryParamsRequest{})
s.Require().NoError(err)
return *res.Params
}
// CreateTransferPath sets up a path between chainA and chainB with a transfer channel and returns the relayer wired
// up to watch the channel and port IDs created.
func (s *transferTester) CreateTransferPath(testName string) (ibc.Relayer, ibc.ChannelOutput) {
relayer, channel := s.CreatePaths(ibc.DefaultClientOpts(), s.TransferChannelOptions(), testName), s.GetChainAChannelForTest(testName)
s.T().Logf("test %s running on portID %s channelID %s", testName, channel.PortID, channel.ChannelID)
return relayer, channel
}
// TestMsgTransfer_Succeeds_Nonincentivized will test sending successful IBC transfers from chainA to chainB.
// The transfer will occur over a basic transfer channel (non incentivized) and both native and non-native tokens
// will be sent forwards and backwards in the IBC transfer timeline (both chains will act as source and receiver chains).
func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized() {
t := s.T()
ctx := context.TODO()
testName := t.Name()
// NOTE: t.Parallel() should be called before SetupPath in all tests.
// t.Name() must be stored in a variable before t.Parallel() otherwise t.Name() is not
// deterministic.
t.Parallel()
relayer, channelA := s.CreateTransferPath(testName)
chainA, chainB := s.GetChains()
chainBVersion := chainB.Config().Images[0].Version
chainADenom := chainA.Config().Denom
chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
chainAAddress := chainAWallet.FormattedAddress()
chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
chainBAddress := chainBWallet.FormattedAddress()
s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks")
t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) {
transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "")
s.AssertTxSuccess(transferTxResp)
})
t.Run("tokens are escrowed", func(t *testing.T) {
actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet)
s.Require().NoError(err)
expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount
s.Require().Equal(expected, actualBalance)
// TODO: cannot query total escrow if tests in parallel are using the same denom.
// if testvalues.TotalEscrowFeatureReleases.IsSupported(chainAVersion) {
// actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom)
// s.Require().NoError(err)
//
// expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount))
// s.Require().Equal(expectedTotalEscrow, actualTotalEscrow)
// }
})
t.Run("start relayer", func(t *testing.T) {
s.StartRelayer(relayer, testName)
})
chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID)
t.Run("packets are relayed", func(t *testing.T) {
s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1)
actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom())
s.Require().NoError(err)
expected := testvalues.IBCTransferAmount
s.Require().Equal(expected, actualBalance.Int64())
})
if testvalues.TokenMetadataFeatureReleases.IsSupported(chainBVersion) {
t.Run("metadata for IBC denomination exists on chainB", func(t *testing.T) {
s.AssertHumanReadableDenom(ctx, chainB, chainADenom, channelA)
})
}
t.Run("non-native IBC token transfer from chainB to chainA, receiver is source of tokens", func(t *testing.T) {
transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferAmount(chainBIBCToken.IBCDenom()), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "")
s.AssertTxSuccess(transferTxResp)
})
t.Run("tokens are escrowed", func(t *testing.T) {
actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom())
s.Require().NoError(err)
s.Require().Equal(sdkmath.ZeroInt(), actualBalance)
// https://github.com/cosmos/ibc-go/issues/6742
// if testvalues.TotalEscrowFeatureReleases.IsSupported(chainBVersion) {
// actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainB, chainBIBCToken.IBCDenom())
// s.Require().NoError(err)
// s.Require().Equal(sdk.NewCoin(chainBIBCToken.IBCDenom(), sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because sending chain is not source for tokens
// }
})
s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks")
t.Run("packets are relayed", func(t *testing.T) {
s.AssertPacketRelayed(ctx, chainB, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1)
actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet)
s.Require().NoError(err)
expected := testvalues.StartingTokenAmount
s.Require().Equal(expected, actualBalance)
})
// https://github.com/cosmos/ibc-go/issues/6742
// if testvalues.TotalEscrowFeatureReleases.IsSupported(chainAVersion) {
// t.Run("tokens are un-escrowed", func(t *testing.T) {
// actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom)
// s.Require().NoError(err)
// s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back
// })
// }
}
// TestMsgTransfer_Fails_InvalidAddress attempts to send an IBC transfer to an invalid address and ensures
// that the tokens on the sending chain are unescrowed.
func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress() {
t := s.T()
ctx := context.TODO()
testName := t.Name()
t.Parallel()
relayer, channelA := s.CreateTransferPath(testName)
chainA, chainB := s.GetChains()
chainADenom := chainA.Config().Denom
chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
chainAAddress := chainAWallet.FormattedAddress()
s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks")
t.Run("native IBC token transfer from chainA to invalid address", func(t *testing.T) {
transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainB), 0, "")
s.AssertTxSuccess(transferTxResp)
})
t.Run("tokens are escrowed", func(t *testing.T) {
actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet)
s.Require().NoError(err)
expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount
s.Require().Equal(expected, actualBalance)
})
t.Run("start relayer", func(t *testing.T) {
s.StartRelayer(relayer, testName)
})
t.Run("packets are relayed", func(t *testing.T) {
s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1)
})
t.Run("token transfer amount unescrowed", func(t *testing.T) {
actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet)
s.Require().NoError(err)
expected := testvalues.StartingTokenAmount
s.Require().Equal(expected, actualBalance)
})
}
func (s *TransferTestSuite) TestMsgTransfer_Timeout_Nonincentivized() {
t := s.T()
ctx := context.TODO()
testName := t.Name()
t.Parallel()
relayer, channelA := s.CreateTransferPath(testName)
chainA, _ := s.GetChains()
chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
chainBWalletAmount := ibc.WalletAmount{
Address: chainBWallet.FormattedAddress(), // destination address
Denom: chainA.Config().Denom,
Amount: sdkmath.NewInt(testvalues.IBCTransferAmount),
}
t.Run("IBC transfer packet timesout", func(t *testing.T) {
tx, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainBWalletAmount, ibc.TransferOptions{Timeout: testvalues.ImmediatelyTimeout()})
s.Require().NoError(err)
s.Require().NoError(tx.Validate(), "source ibc transfer tx is invalid")
time.Sleep(time.Nanosecond * 1) // want it to timeout immediately
})
t.Run("tokens are escrowed", func(t *testing.T) {
actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet)
s.Require().NoError(err)
expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount
s.Require().Equal(expected, actualBalance)
})
t.Run("start relayer", func(t *testing.T) {
s.StartRelayer(relayer, testName)
})
t.Run("ensure escrowed tokens have been refunded to sender due to timeout", func(t *testing.T) {
// ensure destination address did not receive any tokens
bal, err := s.GetChainBNativeBalance(ctx, chainBWallet)
s.Require().NoError(err)
s.Require().Equal(testvalues.StartingTokenAmount, bal)
// ensure that the sender address has been successfully refunded the full amount
bal, err = s.GetChainANativeBalance(ctx, chainAWallet)
s.Require().NoError(err)
s.Require().Equal(testvalues.StartingTokenAmount, bal)
})
}
// This can be used to test sending with a transfer packet with a memo given different combinations of
// ibc-go versions.
//
// TestMsgTransfer_WithMemo will test sending IBC transfers from chainA to chainB
// If the chains contain a version of FungibleTokenPacketData with memo, both send and receive should succeed.
// If one of the chains contains a version of FungibleTokenPacketData without memo, then receiving a packet with
// memo should fail in that chain
func (s *TransferTestSuite) TestMsgTransfer_WithMemo() {
t := s.T()
ctx := context.TODO()
testName := t.Name()
t.Parallel()
relayer, channelA := s.CreateTransferPath(testName)
chainA, chainB := s.GetChains()
chainADenom := chainA.Config().Denom
chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
chainAAddress := chainAWallet.FormattedAddress()
chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
chainBAddress := chainBWallet.FormattedAddress()
s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks")
t.Run("IBC token transfer with memo from chainA to chainB", func(t *testing.T) {
transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "memo")
s.AssertTxSuccess(transferTxResp)
})
t.Run("tokens are escrowed", func(t *testing.T) {
actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet)
s.Require().NoError(err)
expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount
s.Require().Equal(expected, actualBalance)
})
t.Run("start relayer", func(t *testing.T) {
s.StartRelayer(relayer, testName)
})
chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID)
t.Run("packets relayed", func(t *testing.T) {
s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1)
actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom())
s.Require().NoError(err)
s.Require().Equal(testvalues.IBCTransferAmount, actualBalance.Int64())
})
}
// TestMsgTransfer_EntireBalance tests that it is possible to transfer the entire balance
// of a given denom by using types.UnboundedSpendLimit as the amount.
// compatibility:TestMsgTransfer_EntireBalance:from_versions: v7.10.0,v8.7.0,v10.0.0
func (s *TransferTestSuite) TestMsgTransfer_EntireBalance() {
t := s.T()
ctx := context.TODO()
testName := t.Name()
t.Parallel()
relayer, channelA := s.CreateTransferPath(testName)
chainA, chainB := s.GetChains()
chainADenom := chainA.Config().Denom
chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
chainAAddress := chainAWallet.FormattedAddress()
chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
chainBAddress := chainBWallet.FormattedAddress()
coinFromA := testvalues.DefaultTransferAmount(chainADenom)
s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks")
t.Run("IBC token transfer from chainA to chainB", func(t *testing.T) {
transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, coinFromA, chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainA), 0, "")
s.AssertTxSuccess(transferTxResp)
})
t.Run("tokens are escrowed", func(t *testing.T) {
actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet)
s.Require().NoError(err)
s.Require().Equal(testvalues.StartingTokenAmount-coinFromA.Amount.Int64(), actualBalance)
})
t.Run("start relayer", func(t *testing.T) {
s.StartRelayer(relayer, testName)
})
chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID)
t.Run("packets relayed", func(t *testing.T) {
s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1)
actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom())
s.Require().NoError(err)
s.Require().Equal(coinFromA.Amount.Int64(), actualBalance.Int64())
actualBalance, err = query.Balance(ctx, chainA, chainAAddress, chainADenom)
s.Require().NoError(err)
s.Require().Equal(testvalues.StartingTokenAmount-coinFromA.Amount.Int64(), actualBalance.Int64())
})
t.Run("send entire balance from B to A", func(t *testing.T) {
transferCoin := sdk.NewCoin(chainBIBCToken.IBCDenom(), transfertypes.UnboundedSpendLimit())
transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, transferCoin, chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainB), 0, "")
s.AssertTxSuccess(transferTxResp)
})
s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks")
t.Run("packets relayed", func(t *testing.T) {
// test that chainA has the entire balance back of its native token.
s.AssertPacketRelayed(ctx, chainB, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1)
actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainADenom)
s.Require().NoError(err)
s.Require().Equal(testvalues.StartingTokenAmount, actualBalance.Int64())
// test that chainB has a zero balance of chainA's IBC token denom.
actualBalance, err = query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom())
s.Require().NoError(err)
s.Require().Zero(actualBalance.Int64())
})
}