mukan-ibc/modules/apps/transfer/transfer_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

201 lines
8.5 KiB
Go

package transfer_test
import (
"testing"
testifysuite "github.com/stretchr/testify/suite"
sdkmath "cosmossdk.io/math"
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
"git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/types"
clienttypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/02-client/types"
ibctesting "git.cw.tr/mukan-network/mukan-ibc/testing"
)
type TransferTestSuite struct {
testifysuite.Suite
coordinator *ibctesting.Coordinator
// testing chains used for convenience and readability
chainA *ibctesting.TestChain
chainB *ibctesting.TestChain
chainC *ibctesting.TestChain
}
func (suite *TransferTestSuite) SetupTest() {
suite.coordinator = ibctesting.NewCoordinator(suite.T(), 3)
suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1))
suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2))
suite.chainC = suite.coordinator.GetChain(ibctesting.GetChainID(3))
}
// Constructs the following sends based on the established channels/connections
// 1 - from chainA to chainB
// 2 - from chainB to chainC
// 3 - from chainC to chainB
func (suite *TransferTestSuite) TestHandleMsgTransfer() {
var (
sourceDenomToTransfer string
msgAmount sdkmath.Int
)
testCases := []struct {
name string
malleate func()
}{
{
"transfer single denom",
func() {},
},
{
"transfer amount larger than int64",
func() {
var ok bool
msgAmount, ok = sdkmath.NewIntFromString("9223372036854775808") // 2^63 (one above int64)
suite.Require().True(ok)
},
},
{
"transfer entire balance",
func() {
msgAmount = types.UnboundedSpendLimit()
},
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest() // reset
// setup between chainA and chainB
// NOTE:
// pathAToB.EndpointA = endpoint on chainA
// pathAToB.EndpointB = endpoint on chainB
pathAToB := ibctesting.NewTransferPath(suite.chainA, suite.chainB)
pathAToB.Setup()
traceAToB := types.NewHop(pathAToB.EndpointB.ChannelConfig.PortID, pathAToB.EndpointB.ChannelID)
sourceDenomToTransfer = sdk.DefaultBondDenom
msgAmount = ibctesting.DefaultCoinAmount
tc.malleate()
originalBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), sourceDenomToTransfer)
timeoutHeight := clienttypes.NewHeight(1, 110)
originalCoin := sdk.NewCoin(sourceDenomToTransfer, msgAmount)
// send from chainA to chainB
msg := types.NewMsgTransfer(pathAToB.EndpointA.ChannelConfig.PortID, pathAToB.EndpointA.ChannelID, originalCoin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, "")
res, err := suite.chainA.SendMsgs(msg)
suite.Require().NoError(err) // message committed
packet, err := ibctesting.ParseV1PacketFromEvents(res.Events)
suite.Require().NoError(err)
// Get the packet data to determine the amount of tokens being transferred (needed for sending entire balance)
packetData, err := types.UnmarshalPacketData(packet.GetData(), pathAToB.EndpointA.GetChannel().Version, "")
suite.Require().NoError(err)
transferAmount, ok := sdkmath.NewIntFromString(packetData.Token.Amount)
suite.Require().True(ok)
// relay send
err = pathAToB.RelayPacket(packet)
suite.Require().NoError(err) // relay committed
escrowAddress := types.GetEscrowAddress(packet.GetSourcePort(), packet.GetSourceChannel())
// check that the balance for chainA is updated
chainABalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), originalCoin.Denom)
suite.Require().True(originalBalance.Amount.Sub(transferAmount).Equal(chainABalance.Amount))
// check that module account escrow address has locked the tokens
chainAEscrowBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), escrowAddress, originalCoin.Denom)
suite.Require().True(transferAmount.Equal(chainAEscrowBalance.Amount))
// check that voucher exists on chain B
chainBDenom := types.NewDenom(originalCoin.Denom, traceAToB)
chainBBalance := suite.chainB.GetSimApp().BankKeeper.GetBalance(suite.chainB.GetContext(), suite.chainB.SenderAccount.GetAddress(), chainBDenom.IBCDenom())
coinSentFromAToB := sdk.NewCoin(chainBDenom.IBCDenom(), transferAmount)
suite.Require().Equal(coinSentFromAToB, chainBBalance)
// setup between chainB to chainC
// NOTE:
// pathBToC.EndpointA = endpoint on chainB
// pathBToC.EndpointB = endpoint on chainC
pathBToC := ibctesting.NewTransferPath(suite.chainB, suite.chainC)
pathBToC.Setup()
traceBToC := types.NewHop(pathBToC.EndpointB.ChannelConfig.PortID, pathBToC.EndpointB.ChannelID)
// send from chainB to chainC
msg = types.NewMsgTransfer(pathBToC.EndpointA.ChannelConfig.PortID, pathBToC.EndpointA.ChannelID, coinSentFromAToB, suite.chainB.SenderAccount.GetAddress().String(), suite.chainC.SenderAccount.GetAddress().String(), timeoutHeight, 0, "")
res, err = suite.chainB.SendMsgs(msg)
suite.Require().NoError(err) // message committed
packet, err = ibctesting.ParseV1PacketFromEvents(res.Events)
suite.Require().NoError(err)
err = pathBToC.RelayPacket(packet)
suite.Require().NoError(err) // relay committed
coinsSentFromBToC := sdk.NewCoins()
// check balances for chainB and chainC after transfer from chainB to chainC
// NOTE: fungible token is prefixed with the full trace in order to verify the packet commitment
chainCDenom := types.NewDenom(originalCoin.Denom, traceBToC, traceAToB)
// check that the balance is updated on chainC
coinSentFromBToC := sdk.NewCoin(chainCDenom.IBCDenom(), transferAmount)
chainCBalance := suite.chainC.GetSimApp().BankKeeper.GetBalance(suite.chainC.GetContext(), suite.chainC.SenderAccount.GetAddress(), coinSentFromBToC.Denom)
suite.Require().Equal(coinSentFromBToC, chainCBalance)
// check that balance on chain B is empty
chainBBalance = suite.chainB.GetSimApp().BankKeeper.GetBalance(suite.chainB.GetContext(), suite.chainB.SenderAccount.GetAddress(), coinSentFromBToC.Denom)
suite.Require().Zero(chainBBalance.Amount.Int64())
// send from chainC back to chainB
msg = types.NewMsgTransfer(pathBToC.EndpointB.ChannelConfig.PortID, pathBToC.EndpointB.ChannelID, coinSentFromBToC, suite.chainC.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, "")
res, err = suite.chainC.SendMsgs(msg)
suite.Require().NoError(err) // message committed
packet, err = ibctesting.ParseV1PacketFromEvents(res.Events)
suite.Require().NoError(err)
err = pathBToC.RelayPacket(packet)
suite.Require().NoError(err) // relay committed
// check balances for chainC are empty after transfer from chainC to chainB
for _, coin := range coinsSentFromBToC {
// check that balance on chain C is empty
chainCBalance := suite.chainC.GetSimApp().BankKeeper.GetBalance(suite.chainC.GetContext(), suite.chainC.SenderAccount.GetAddress(), coin.Denom)
suite.Require().Zero(chainCBalance.Amount.Int64())
}
// check balances for chainB after transfer from chainC to chainB
// check that balance on chain B has the transferred amount
chainBBalance = suite.chainB.GetSimApp().BankKeeper.GetBalance(suite.chainB.GetContext(), suite.chainB.SenderAccount.GetAddress(), coinSentFromAToB.Denom)
suite.Require().Equal(coinSentFromAToB, chainBBalance)
// check that module account escrow address is empty
escrowAddress = types.GetEscrowAddress(traceBToC.PortId, traceBToC.ChannelId)
chainBEscrowBalance := suite.chainB.GetSimApp().BankKeeper.GetBalance(suite.chainB.GetContext(), escrowAddress, coinSentFromAToB.Denom)
suite.Require().Zero(chainBEscrowBalance.Amount.Int64())
// check balances for chainA after transfer from chainC to chainB
// check that the balance is unchanged
chainABalance = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), originalCoin.Denom)
suite.Require().True(originalBalance.Amount.Sub(transferAmount).Equal(chainABalance.Amount))
// check that module account escrow address is unchanged
escrowAddress = types.GetEscrowAddress(pathAToB.EndpointA.ChannelConfig.PortID, pathAToB.EndpointA.ChannelID)
chainAEscrowBalance = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), escrowAddress, originalCoin.Denom)
suite.Require().True(transferAmount.Equal(chainAEscrowBalance.Amount))
})
}
}
func TestTransferTestSuite(t *testing.T) {
testifysuite.Run(t, new(TransferTestSuite))
}