diff --git a/.github/workflows/interchaintest.yaml b/.github/workflows/interchaintest.yaml index 7ba7ea6cc..a69c8767b 100644 --- a/.github/workflows/interchaintest.yaml +++ b/.github/workflows/interchaintest.yaml @@ -127,5 +127,20 @@ jobs: uses: actions/checkout@v4 - run: make ictest-wasm-gasless + env: + BRANCH_CI: "latest" + ictest-tf-force-transfer-ibc: + runs-on: ubuntu-latest + needs: build-and-push-image # This job must run after build and push image + steps: + - name: Set up Go 1.22 + uses: actions/setup-go@v4 + with: + go-version: '1.22.7' + + - name: checkout code + uses: actions/checkout@v4 + + - run: make ictest-tf-force-transfer-ibc env: BRANCH_CI: "latest" \ No newline at end of file diff --git a/Makefile b/Makefile index 4777dbc61..3a28b64d9 100644 --- a/Makefile +++ b/Makefile @@ -175,6 +175,10 @@ ictest-tf-set-metadata: ictest-ibchooks: cd tests/interchaintest && go test -race -v -run TestIbcHooks . +# Executes ibc orai osmosis with token factory force transfer tests via interchaintest +ictest-tf-force-transfer-ibc: + cd tests/interchaintest && go test -race -v -run TestTokenFactoryForceTransferWithIbc . + # Executes wasm gas less tests via interchaintest ictest-wasm-gasless: cd tests/interchaintest && go test -race -v -run TestWasmGasLessContract . diff --git a/app/app.go b/app/app.go index b014dc09a..f11ad1715 100644 --- a/app/app.go +++ b/app/app.go @@ -195,7 +195,7 @@ var ( EnabledCapabilities = []string{ tokenfactorytypes.EnableBurnFrom, - tokenfactorytypes.EnableForceTransfer, + // tokenfactorytypes.EnableForceTransfer, tokenfactorytypes.EnableSetMetadata, } ) diff --git a/scripts/tests-0.50.4/test-tokenfactory-force-transfer.sh b/scripts/tests-0.50.4/test-tokenfactory-force-transfer.sh new file mode 100755 index 000000000..e19e4ac18 --- /dev/null +++ b/scripts/tests-0.50.4/test-tokenfactory-force-transfer.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +set -ux + +CHAIN_ID=${CHAIN_ID:-testing} +USER=${USER:-"validator1"} +NODE_HOME=${NODE_HOME:-"$PWD/.oraid"} +ARGS="--from $USER --chain-id $CHAIN_ID -y --keyring-backend test --gas auto --gas-adjustment 1.5 -b sync --home $NODE_HOME" +HIDE_LOGS="/dev/null" + +# prepare a new contract for gasless +fee_params=$(oraid query tokenfactory params --output json | jq '.params.denom_creation_fee[0].denom') +if ! [[ $fee_params =~ "orai" ]]; then + echo "Tokenfactory force transfer tests failed. The tokenfactory fee params is not orai" + exit 1 +fi + +# try creating a new denom +denom_name="usd" +oraid tx tokenfactory create-denom $denom_name $ARGS >$HIDE_LOGS + +# try querying list denoms afterwards +# need to sleep 1s +sleep 1 +user_address=$(oraid keys show $USER --home $NODE_HOME --keyring-backend test -a) +first_denom=$(oraid query tokenfactory denoms-from-creator $user_address --output json | jq '.denoms[0]' | tr -d '"') +echo "first denom: $first_denom" + +if ! [[ $first_denom =~ "factory/$user_address/$denom_name" ]]; then + echo "Tokenfactory force transfer tests failed. The tokenfactory denom does not match the created denom" + exit 1 +fi + +admin=$(oraid query tokenfactory denom-authority-metadata $first_denom --output json | jq '.authority_metadata.admin') +echo "admin: $admin" + +if ! [[ $admin =~ $user_address ]]; then + echo "Tokenfactory force transfer tests failed. The tokenfactory admin does not match the creator" + exit 1 +fi + +sleep 2 +# try to mint token +oraid tx tokenfactory mint 10000$first_denom $ARGS >$HIDE_LOGS + +# query balance after mint +# need sleep 1s +sleep 2 +tokenfactory_balance=$(oraid query bank balance $user_address $first_denom --output json | jq '.balance.amount | tonumber') +if [[ $tokenfactory_balance -ne 10000 ]]; then + echo "Tokenfactory force transfer failed. The tokenfactory balance does not increase after mint" + exit 1 +fi + +# try to force transfer token to another address +oraid tx tokenfactory force-transfer 10$first_denom $user_address orai1cknd27x0244595pp7a5c9sdekl3ywl52x62ssn $ARGS &>$HIDE_LOGS + +# query balance after force trasnfer +# need sleep 2s +sleep 2 +tokenfactory_balance=$(oraid query bank balance $user_address $first_denom --output json | jq '.balance.amount | tonumber') +if ! [[ $tokenfactory_balance =~ 10000 ]]; then + echo "Tokenfactory force transfer failed. The tokenfactory balance decreases after force transfer" + exit 1 +fi + +echo "Tokenfactory force transfer tests passed!" diff --git a/tests/interchaintest/go.mod b/tests/interchaintest/go.mod index 2e461533a..81a645d7a 100644 --- a/tests/interchaintest/go.mod +++ b/tests/interchaintest/go.mod @@ -3,10 +3,12 @@ module github.com/oraichain/wasmd/tests/interchaintest go 1.23.3 require ( - cosmossdk.io/math v1.3.0 + cosmossdk.io/math v1.4.0 + github.com/CosmWasm/wasmd v0.50.0 github.com/cometbft/cometbft v0.38.12 github.com/cosmos/cosmos-sdk v0.50.10 github.com/cosmos/ibc-go/v8 v8.4.0 + github.com/docker/docker v24.0.9+incompatible github.com/icza/dyno v0.0.0-20220812133438-f0b6f8a18845 github.com/strangelove-ventures/interchaintest/v8 v8.3.0 github.com/stretchr/testify v1.9.0 @@ -36,7 +38,6 @@ require ( github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect github.com/ChainSafe/go-schnorrkel/1 v0.0.0-00010101000000-000000000000 // indirect github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420 // indirect - github.com/CosmWasm/wasmd v0.50.0 // indirect github.com/CosmWasm/wasmvm/v2 v2.1.3 // indirect github.com/DataDog/datadog-go v4.8.3+incompatible // indirect github.com/DataDog/zstd v1.5.5 // indirect @@ -84,7 +85,6 @@ require ( github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/distribution/reference v0.5.0 // indirect github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v24.0.9+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect diff --git a/tests/interchaintest/go.sum b/tests/interchaintest/go.sum index 21af4736f..6ccfba291 100644 --- a/tests/interchaintest/go.sum +++ b/tests/interchaintest/go.sum @@ -196,8 +196,8 @@ cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= cosmossdk.io/log v1.4.1 h1:wKdjfDRbDyZRuWa8M+9nuvpVYxrEOwbD/CA8hvhU8QM= cosmossdk.io/log v1.4.1/go.mod h1:k08v0Pyq+gCP6phvdI6RCGhLf/r425UT6Rk/m+o74rU= -cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= -cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= +cosmossdk.io/math v1.4.0 h1:XbgExXFnXmF/CccPPEto40gOO7FpWu9yWNAZPN3nkNQ= +cosmossdk.io/math v1.4.0/go.mod h1:O5PkD4apz2jZs4zqFdTr16e1dcaQCc5z6lkEnrrppuk= cosmossdk.io/store v1.1.1 h1:NA3PioJtWDVU7cHHeyvdva5J/ggyLDkyH0hGHl2804Y= cosmossdk.io/store v1.1.1/go.mod h1:8DwVTz83/2PSI366FERGbWSH7hL6sB7HbYp8bqksNwM= cosmossdk.io/x/circuit v0.1.1 h1:KPJCnLChWrxD4jLwUiuQaf5mFD/1m7Omyo7oooefBVQ= diff --git a/tests/interchaintest/helpers/tokenfactory.go b/tests/interchaintest/helpers/tokenfactory.go index 9bd678c37..df6fa77e4 100644 --- a/tests/interchaintest/helpers/tokenfactory.go +++ b/tests/interchaintest/helpers/tokenfactory.go @@ -33,6 +33,37 @@ func TxTokenFactoryCreateDenom( return denom, txHash } +func TxTokenFactoryMintToken( + t *testing.T, + ctx context.Context, + chain *cosmos.CosmosChain, + user ibc.Wallet, + denom string, + amount uint64, +) string { + tn := chain.GetNode() + txHash, err := tn.TokenFactoryMintDenom(ctx, user.KeyName(), denom, amount) + require.NoError(t, err) + + return txHash +} + +func TxTokenFactoryForceTransfer( + t *testing.T, + ctx context.Context, + chain *cosmos.CosmosChain, + user ibc.Wallet, + denom string, + amount uint64, + fromAddr string, + toAddr string, +) (string, error) { + tn := chain.GetNode() + txHash, err := tn.TokenFactoryForceTransferDenom(ctx, user.KeyName(), denom, amount, fromAddr, toAddr) + + return txHash, err +} + func TxTokenFactoryModifyMetadata( t *testing.T, ctx context.Context, @@ -161,3 +192,19 @@ func QueryDenomAuthorityMetadata(t *testing.T, return res.AuthorityMetadata.Admin, nil } + +func QueryBalance( + t *testing.T, + ctx context.Context, + chain *cosmos.CosmosChain, + denom string, + userAddress string, +) (uint64, error) { + tn := chain.GetNode() + balance, err := tn.Chain.GetBalance(ctx, userAddress, denom) + if err != nil { + return 0, err + } + + return balance.Uint64(), nil +} diff --git a/tests/interchaintest/ibc_hooks_test.go b/tests/interchaintest/ibc_hooks_test.go index e90ced8ad..0f0cb235c 100644 --- a/tests/interchaintest/ibc_hooks_test.go +++ b/tests/interchaintest/ibc_hooks_test.go @@ -21,9 +21,9 @@ func TestIbcHooks(t *testing.T) { } t.Parallel() - chains := CreateChains(t, 1, 1) + chains := CreateChains(t, 1, 1, []string{"orai", "gaia"}) orai, gaia := chains[0].(*cosmos.CosmosChain), chains[1].(*cosmos.CosmosChain) - ic, r, ctx, _, eRep, _ := BuildInitialChain(t, chains) + ic, r, ctx, _, eRep, _ := BuildInitialChain(t, chains, pathOraiGaia) t.Cleanup(func() { _ = ic.Close() }) diff --git a/tests/interchaintest/orai_osmo_test.go b/tests/interchaintest/orai_osmo_test.go new file mode 100644 index 000000000..9cf671b39 --- /dev/null +++ b/tests/interchaintest/orai_osmo_test.go @@ -0,0 +1,130 @@ +package interchaintest + +import ( + "context" + "fmt" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + "github.com/oraichain/wasmd/tests/interchaintest/helpers" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/strangelove-ventures/interchaintest/v8/testutil" + + "github.com/stretchr/testify/require" +) + +// TestStartOrai is a basic test to assert that spinning up a Orai network with 1 validator works properly. +func TestTokenFactoryForceTransferWithIbc(t *testing.T) { + if testing.Short() { + t.Skip() + } + + t.Parallel() + + ctx := context.Background() + + chains := CreateChains(t, 1, 1, []string{"orai", "osmosis"}) + orai, osmo := chains[0].(*cosmos.CosmosChain), chains[1].(*cosmos.CosmosChain) + + // Create relayer factory to utilize the go-relayer + ic, r, ctx, _, eRep, _ := BuildInitialChain(t, chains, pathOraiOsmo) + t.Cleanup(func() { + _ = ic.Close() + }) + + // Start the relayer + require.NoError(t, r.StartRelayer(ctx, eRep, pathOraiOsmo)) + t.Cleanup( + func() { + err := r.StopRelayer(ctx, eRep) + if err != nil { + panic(fmt.Errorf("an error occurred while stopping the relayer: %s", err)) + } + }, + ) + + channel, err := ibc.GetTransferChannel(ctx, r, eRep, orai.Config().ChainID, osmo.Config().ChainID) + require.NoError(t, err) + + users := CreateTestingUser(t, ctx, t.Name(), genesisWalletAmount, chains...) + // Get our Bech32 encoded user addresses + oraiUser, osmoUser := users[0], users[1] + + oraiUserAddress := sdk.MustBech32ifyAddressBytes(orai.Config().Bech32Prefix, oraiUser.Address()) + osmoUserAddr := sdk.MustBech32ifyAddressBytes(osmo.Config().Bech32Prefix, osmoUser.Address()) + + _ = oraiUserAddress + _ = osmoUserAddr + gas := uint64(100_000_000) + + // create new token factory denom + expectedDenom, _ := helpers.TxTokenFactoryCreateDenom(t, ctx, orai, oraiUser, "orai-usd", gas) + denomCreated, err := helpers.QueryDenomsFromCreator(t, ctx, orai, oraiUserAddress) + require.NoError(t, err) + require.Contains(t, denomCreated, expectedDenom) + + // mint token + tokenToMint := uint64(100_000_000_000) + _ = helpers.TxTokenFactoryMintToken(t, ctx, orai, oraiUser, expectedDenom, tokenToMint) + oraiUserBalance, err := helpers.QueryBalance(t, ctx, orai, expectedDenom, oraiUserAddress) + require.NoError(t, err) + require.Equal(t, tokenToMint, oraiUserBalance) + + // get escrowed address + addr := types.GetEscrowAddress(channel.PortID, channel.ChannelID) + escrowedAddress := sdk.MustBech32ifyAddressBytes(orai.Config().Bech32Prefix, addr.Bytes()) + + // balance before transfer ibc must be 0 + escrowedBalance, err := helpers.QueryBalance(t, ctx, orai, expectedDenom, escrowedAddress) + require.NoError(t, err) + require.Equal(t, escrowedBalance, uint64(0)) + + // ibc denom when transfer orai to osmosis + // transfer/channel-0/factory/orai14zqwen0pqj7s6drrkwaqwded7ajrq5czyw7fhq/orai-usd + oraiDenom := transfertypes.GetPrefixedDenom(channel.Counterparty.PortID, channel.Counterparty.ChannelID, expectedDenom) + // ibc/F859A4CC5A5EA6533657F6A83F7C11A479A13DBDC53F68135CDA95B0F12E5892 + oraiIBCDenom := transfertypes.ParseDenomTrace(oraiDenom).IBCDenom() + + // osmosis user balance before transfer ibc must be 0 + userOsmosisBalance, err := helpers.QueryBalance(t, ctx, osmo, oraiIBCDenom, osmoUserAddr) + require.NoError(t, err) + require.Equal(t, userOsmosisBalance, uint64(0)) + + // try to transfer token factory to osmosis + transfer := ibc.WalletAmount{ + Address: osmoUserAddr, + Denom: expectedDenom, + Amount: amountToSend, + } + transferTx, err := orai.SendIBCTransfer(ctx, channel.Counterparty.ChannelID, oraiUserAddress, transfer, ibc.TransferOptions{}) + require.NoError(t, err) + + // waiting for ACK -> transfer successfully + oraiHeight, err := orai.Height(ctx) + require.NoError(t, err) + _, err = testutil.PollForAck(ctx, orai, oraiHeight-5, oraiHeight+25, transferTx.Packet) + require.NoError(t, err) + + // balance after transfer ibc must be equalt amount to send + escrowedBalance, err = helpers.QueryBalance(t, ctx, orai, expectedDenom, escrowedAddress) + fmt.Println("escrowed balance: ", escrowedBalance) + require.NoError(t, err) + require.Equal(t, escrowedBalance, uint64(amountToSend.Int64())) + + // osmosis user balance after transfer ibc must be equal amount to send + userOsmosisBalance, err = helpers.QueryBalance(t, ctx, osmo, oraiIBCDenom, osmoUserAddr) + require.NoError(t, err) + require.Equal(t, userOsmosisBalance, uint64(amountToSend.Int64())) + + // try to force transfer tokenfactory from escrowed address + _, err = helpers.TxTokenFactoryForceTransfer(t, ctx, orai, oraiUser, expectedDenom, uint64(amountToSend.Int64()), escrowedAddress, oraiUserAddress) + require.Error(t, err) + + escrowedBalance, err = helpers.QueryBalance(t, ctx, orai, expectedDenom, escrowedAddress) + fmt.Println("escrowed balance: ", escrowedBalance) + require.NoError(t, err) + require.Equal(t, escrowedBalance, uint64(amountToSend.Int64())) +} diff --git a/tests/interchaintest/setup.go b/tests/interchaintest/setup.go index 7f71ea389..5d67bd03f 100644 --- a/tests/interchaintest/setup.go +++ b/tests/interchaintest/setup.go @@ -30,7 +30,7 @@ const ( OraidICTestRepo = "ghcr.io/oraichain/oraid-ictest" IBCRelayerVersion = "latest" GaiaImageVersion = "v21.0.0" - OsmosisImageVersion = "v22.0.1" + OsmosisImageVersion = "v28.0.0" ) var ( @@ -62,7 +62,31 @@ var ( genesisWalletAmount = math.NewInt(100_000_000_000) amountToSend = math.NewInt(1_000_000_000) - pathOraiGaia = "IbcPath" + pathOraiGaia = "IbcPathOraiGaia" + pathOraiOsmo = "IbcPathOraiOsmo" +) + +var ( + chainSpecs = map[string]*interchaintest.ChainSpec{ + "orai": { + Name: "orai", + ChainConfig: oraiConfig, + }, + "gaia": { + Name: "gaia", + Version: GaiaImageVersion, + ChainConfig: ibc.ChainConfig{ + GasPrices: "1uatom", + }, + }, + "osmosis": { + Name: "osmosis", + Version: OsmosisImageVersion, + ChainConfig: ibc.ChainConfig{ + GasPrices: "1uosmo", + }, + }, + } ) // oraiEncoding registers the Orai specific module codecs so that the associated types and msgs @@ -120,27 +144,25 @@ func modifyGenesisShortProposals( } // CreateChains create testing chain. Currently we instantiate 2 chain, first is Orai, seconds is gaia -func CreateChains(t *testing.T, numVals, numFullNodes int, opts ...func(*ibc.ChainConfig)) []ibc.Chain { +func CreateChains(t *testing.T, numVals, numFullNodes int, chainIds []string, opts ...func(*ibc.ChainConfig)) []ibc.Chain { for _, opt := range opts { opt(&oraiConfig) } - cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ - { - Name: "orai", - ChainConfig: oraiConfig, - NumValidators: &numVals, - NumFullNodes: &numFullNodes, - }, - { - Name: "gaia", - Version: GaiaImageVersion, - ChainConfig: ibc.ChainConfig{ - GasPrices: "1uatom", - }, - NumValidators: &numVals, - NumFullNodes: &numFullNodes, - }, - }) + + var specs []*interchaintest.ChainSpec + for _, chainId := range chainIds { + if chainSpecs[chainId] == nil { + fmt.Println("not supported this chain: ", chainId) + continue + } + + chainSpecs[chainId].NumValidators = &numVals + chainSpecs[chainId].NumFullNodes = &numFullNodes + + specs = append(specs, chainSpecs[chainId]) + } + + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), specs) // Get chains from the chain factory chains, err := cf.Chains(t.Name()) @@ -153,13 +175,12 @@ func CreateChain(t *testing.T, numVals, numFullNodes int, opts ...func(*ibc.Chai for _, opt := range opts { opt(&oraiConfig) } + + chainSpecs["orai"].NumFullNodes = &numFullNodes + chainSpecs["orai"].NumValidators = &numVals + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ - { - Name: "orai", - ChainConfig: oraiConfig, - NumValidators: &numVals, - NumFullNodes: &numFullNodes, - }, + chainSpecs["orai"], }) // Get chains from the chain factory @@ -189,7 +210,7 @@ func BuildInitialChainNoIbc(t *testing.T, chain ibc.Chain) (*interchaintest.Inte return ic, ctx } -func BuildInitialChain(t *testing.T, chains []ibc.Chain) (*interchaintest.Interchain, ibc.Relayer, context.Context, *client.Client, *testreporter.RelayerExecReporter, string) { +func BuildInitialChain(t *testing.T, chains []ibc.Chain, ibcPath string) (*interchaintest.Interchain, ibc.Relayer, context.Context, *client.Client, *testreporter.RelayerExecReporter, string) { // Create a new Interchain object which describes the chains, relayers, and IBC connections we want to use require.Equal(t, len(chains), 2) // we only initial 2 chain for now ic := interchaintest.NewInterchain() @@ -215,7 +236,7 @@ func BuildInitialChain(t *testing.T, chains []ibc.Chain) (*interchaintest.Interc Chain1: chains[0], Chain2: chains[1], Relayer: r, - Path: pathOraiGaia, + Path: ibcPath, }) err := ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ diff --git a/tests/interchaintest/tokenfactory_test.go b/tests/interchaintest/tokenfactory_test.go index 062170b9e..d11c5b1b6 100644 --- a/tests/interchaintest/tokenfactory_test.go +++ b/tests/interchaintest/tokenfactory_test.go @@ -22,9 +22,9 @@ func TestTokenfactoryParamChange(t *testing.T) { } t.Parallel() - chains := CreateChains(t, 1, 1) + chains := CreateChains(t, 1, 1, []string{"orai", "gaia"}) orai := chains[0].(*cosmos.CosmosChain) - ic, _, ctx, _, _, _ := BuildInitialChain(t, chains) + ic, _, ctx, _, _, _ := BuildInitialChain(t, chains, pathOraiGaia) t.Cleanup(func() { _ = ic.Close() }) diff --git a/tests/interchaintest/utils.go b/tests/interchaintest/utils.go index 7323632ce..654a7b0e7 100644 --- a/tests/interchaintest/utils.go +++ b/tests/interchaintest/utils.go @@ -22,7 +22,7 @@ func CreateTestingUser( chains ...ibc.Chain, ) []ibc.Wallet { // Create some user accounts on both chains - users := interchaintest.GetAndFundTestUsers(t, ctx, t.Name(), genesisWalletAmount, chains...) + users := interchaintest.GetAndFundTestUsers(t, ctx, t.Name(), amount, chains...) // Wait a few blocks for user accounts to be created err := testutil.WaitForBlocks(ctx, 5, chains[0]) require.NoError(t, err)