diff --git a/app/app.go b/app/app.go index ce27454e1c..16a06df736 100644 --- a/app/app.go +++ b/app/app.go @@ -125,6 +125,7 @@ import ( "github.com/evmos/evmos/v18/app/post" v17 "github.com/evmos/evmos/v18/app/upgrades/v17" v18 "github.com/evmos/evmos/v18/app/upgrades/v18" + v19 "github.com/evmos/evmos/v18/app/upgrades/v19" "github.com/evmos/evmos/v18/encoding" "github.com/evmos/evmos/v18/ethereum/eip712" "github.com/evmos/evmos/v18/precompiles/common" @@ -1136,6 +1137,14 @@ func (app *Evmos) setupUpgradeHandlers() { ), ) + // v19 upgrade handler + app.UpgradeKeeper.SetUpgradeHandler( + v19.UpgradeName, + v19.CreateUpgradeHandler( + app.mm, app.configurator, + ), + ) + // When a planned update height is reached, the old binary will panic // writing on disk the height and name of the update that triggered it // This will read that value, and execute the preparations for the upgrade. diff --git a/app/upgrades/v19/constants.go b/app/upgrades/v19/constants.go new file mode 100644 index 0000000000..4cd3a8e0e2 --- /dev/null +++ b/app/upgrades/v19/constants.go @@ -0,0 +1,11 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package v19 + +const ( + // UpgradeName is the shared upgrade plan name for mainnet + UpgradeName = "v19.0.0" + // UpgradeInfo defines the binaries that will be used for the upgrade + UpgradeInfo = `'{"binaries":{"darwin/amd64":"https://github.com/evmos/evmos/releases/download/v19.0.0/evmos_19.0.0_Darwin_arm64.tar.gz","darwin/x86_64":"https://github.com/evmos/evmos/releases/download/v19.0.0/evmos_19.0.0_Darwin_x86_64.tar.gz","linux/arm64":"https://github.com/evmos/evmos/releases/download/v19.0.0/evmos_19.0.0_Linux_arm64.tar.gz","linux/amd64":"https://github.com/evmos/evmos/releases/download/v19.0.0/evmos_19.0.0_Linux_amd64.tar.gz","windows/x86_64":"https://github.com/evmos/evmos/releases/download/v19.0.0/evmos_19.0.0_Windows_x86_64.zip"}}'` +) diff --git a/app/upgrades/v19/upgrades.go b/app/upgrades/v19/upgrades.go new file mode 100644 index 0000000000..56441ad98b --- /dev/null +++ b/app/upgrades/v19/upgrades.go @@ -0,0 +1,21 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package v19 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +// CreateUpgradeHandler creates an SDK upgrade handler for v18 +func CreateUpgradeHandler( + mm *module.Manager, + configurator module.Configurator, +) upgradetypes.UpgradeHandler { + return func(ctx sdk.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { + // Leave modules as-is to avoid running InitGenesis. + return mm.RunMigrations(ctx, configurator, vm) + } +} diff --git a/app/upgrades/v20/convert.go b/app/upgrades/v20/convert.go index 0931eb6e17..8668e543a6 100644 --- a/app/upgrades/v20/convert.go +++ b/app/upgrades/v20/convert.go @@ -178,14 +178,9 @@ func ConvertERC20Coins( panic(err) } - userHomeDir, err := os.UserHomeDir() - if err != nil { - panic(err) - } - // Store in file // file, _ := json.MarshalIndent(jsonExport, "", " ") - file, err := os.ReadFile(fmt.Sprint(userHomeDir, "/results-full.json")) + file, err := os.ReadFile("./results-full.json") if err != nil { panic(err) } diff --git a/app/upgrades/v20/results-full.json b/app/upgrades/v20/results-full.json new file mode 100755 index 0000000000..59e806b1f6 --- /dev/null +++ b/app/upgrades/v20/results-full.json @@ -0,0 +1,27 @@ +[ + { + "Address": "evmos1q04jewhxw4xxu3vlg3rc85240h9q7ns6zh5687", + "Balance": "180", + "Erc20": "0x80b5a32E4F032B2a058b4F29EC95EEfEEB87aDcd" + }, + { + "Address": "evmos1x7x9pkfxf33l87ftspk5aetwnkr0lvlvyw7zsm", + "Balance": "10", + "Erc20": "0x80b5a32E4F032B2a058b4F29EC95EEfEEB87aDcd" + }, + { + "Address": "evmos12luku6uxehhak02py4rcz65zu0swh7wj9u9hfh", + "Balance": "200", + "Erc20": "0x80b5a32E4F032B2a058b4F29EC95EEfEEB87aDcd" + }, + { + "Address": "evmos10z9ephaxvz56jrz8gss9psha6sf96gdwqvuntw", + "Balance": "200", + "Erc20": "0x80b5a32E4F032B2a058b4F29EC95EEfEEB87aDcd" + }, + { + "Address": "evmos16z0herz998946wr659lr84c8c556da55c86dlp", + "Balance": "200", + "Erc20": "0x80b5a32E4F032B2a058b4F29EC95EEfEEB87aDcd" + } +] \ No newline at end of file diff --git a/contracts/compiled_contracts/WEVMOS.json b/contracts/compiled_contracts/WEVMOS.json index 0ccdbd80a5..7411b14029 100644 --- a/contracts/compiled_contracts/WEVMOS.json +++ b/contracts/compiled_contracts/WEVMOS.json @@ -1,4 +1,4 @@ { "abi": "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"deposit\",\"outputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", "bin": "60806040526040518060400160405280600d81526020017f577261707065642045766d6f73000000000000000000000000000000000000008152506000908051906020019061004f9291906100ca565b506040518060400160405280600681526020017f5745564d4f5300000000000000000000000000000000000000000000000000008152506001908051906020019061009b9291906100ca565b506012600260006101000a81548160ff021916908360ff1602179055503480156100c457600080fd5b5061016f565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061010b57805160ff1916838001178555610139565b82800160010185558215610139579182015b8281111561013857825182559160200191906001019061011d565b5b509050610146919061014a565b5090565b61016c91905b80821115610168576000816000905550600101610150565b5090565b90565b610cb18061017e6000396000f3fe60806040526004361061009c5760003560e01c8063313ce56711610064578063313ce567146102a257806370a08231146102d357806395d89b4114610338578063a9059cbb146103c8578063d0e30db01461043b578063dd62ed3e146104455761009c565b806306fdde03146100a6578063095ea7b31461013657806318160ddd146101a957806323b872dd146101d45780632e1a7d4d14610267575b6100a46104ca565b005b3480156100b257600080fd5b506100bb610567565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100fb5780820151818401526020810190506100e0565b50505050905090810190601f1680156101285780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561014257600080fd5b5061018f6004803603604081101561015957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610605565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106f7565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061024d600480360360608110156101f757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106ff565b604051808215151515815260200191505060405180910390f35b34801561027357600080fd5b506102a06004803603602081101561028a57600080fd5b8101908080359060200190929190505050610a48565b005b3480156102ae57600080fd5b506102b7610b79565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102df57600080fd5b50610322600480360360208110156102f657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b8c565b6040518082815260200191505060405180910390f35b34801561034457600080fd5b5061034d610ba4565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561038d578082015181840152602081019050610372565b50505050905090810190601f1680156103ba5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156103d457600080fd5b50610421600480360360408110156103eb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610c42565b604051808215151515815260200191505060405180910390f35b6104436104ca565b005b34801561045157600080fd5b506104b46004803603604081101561046857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610c57565b6040518082815260200191505060405180910390f35b34600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055503373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a2565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105fd5780601f106105d2576101008083540402835291602001916105fd565b820191906000526020600020905b8154815290600101906020018083116105e057829003601f168201915b505050505081565b600081600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600047905090565b600081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101561074d57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415801561082557507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b1561093e5781600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156108b357600080fd5b81600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b81600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610a9457600080fd5b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050158015610b27573d6000803e3d6000fd5b503373ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65826040518082815260200191505060405180910390a250565b600260009054906101000a900460ff1681565b60036020528060005260406000206000915090505481565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610c3a5780601f10610c0f57610100808354040283529160200191610c3a565b820191906000526020600020905b815481529060010190602001808311610c1d57829003601f168201915b505050505081565b6000610c4f3384846106ff565b905092915050565b600460205281600052604060002060205280600052604060002060009150915050548156fea265627a7a7231582097a4d4c7b547d39e9799336e80c1cddc2df92ceec381f9a5f1df8aed270234bd64736f6c63430005110032" -} \ No newline at end of file +} diff --git a/go.mod b/go.mod index 2d09cc40c9..1db91762ce 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/hashicorp/go-version v1.6.0 github.com/improbable-eng/grpc-web v0.15.0 github.com/linxGnu/grocksdb v1.8.14 - github.com/onsi/ginkgo/v2 v2.17.1 + github.com/onsi/ginkgo/v2 v2.17.2 github.com/onsi/gomega v1.33.0 github.com/ory/dockertest/v3 v3.10.0 github.com/pkg/errors v0.9.1 @@ -128,7 +128,7 @@ require ( github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -140,7 +140,7 @@ require ( github.com/google/flatbuffers v1.12.1 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/orderedcode v0.0.1 // indirect - github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 // indirect + github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect @@ -232,13 +232,13 @@ require ( go.opentelemetry.io/otel v1.22.0 // indirect go.opentelemetry.io/otel/metric v1.22.0 // indirect go.opentelemetry.io/otel/trace v1.22.0 // indirect - golang.org/x/mod v0.14.0 // indirect + golang.org/x/mod v0.17.0 // indirect golang.org/x/oauth2 v0.17.0 // indirect - golang.org/x/sync v0.6.0 // indirect + golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.19.0 // indirect golang.org/x/term v0.19.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.17.0 // indirect + golang.org/x/tools v0.20.0 // indirect google.golang.org/api v0.162.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect diff --git a/go.sum b/go.sum index 7b9c02338f..2346f96181 100644 --- a/go.sum +++ b/go.sum @@ -546,14 +546,17 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= +github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= @@ -666,8 +669,8 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 h1:CqYfpuYIjnlNxM3msdyPRKabhXZWbKjf3Q8BWROFBso= -github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= @@ -933,8 +936,8 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= -github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g= +github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= @@ -1266,8 +1269,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1377,8 +1380,8 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1579,8 +1582,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= -golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/gomod2nix.toml b/gomod2nix.toml index f74f1fbec9..196dd973e8 100644 --- a/gomod2nix.toml +++ b/gomod2nix.toml @@ -281,9 +281,9 @@ schema = 3 [mod."github.com/go-stack/stack"] version = "v1.8.1" hash = "sha256-ixcJ2RrK1ZH3YWGQZF9QFBo02NOuLeSp9wJ7gniipgY=" - [mod."github.com/go-task/slim-sprig"] - version = "v0.0.0-20230315185526-52ccab3ef572" - hash = "sha256-D6NjCQbcYC53NdwzyAm4i9M1OjTJIVu4EIt3AD/Vxfg=" + [mod."github.com/go-task/slim-sprig/v3"] + version = "v3.0.0" + hash = "sha256-vCCw4MXVBm33VNLXcOBccVDD1CSnzDvDdWB6w5FN1cA=" [mod."github.com/godbus/dbus"] version = "v0.0.0-20190726142602-4481cbc300e2" hash = "sha256-R7Gb9+Zjy80FbQSDGketoVEqfdOQKuOVTfWRjQ5kxZY=" @@ -321,8 +321,8 @@ schema = 3 version = "v0.0.1" hash = "sha256-KrExYovtUQrHGI1mPQf57jGw8soz7eWOC2xqEaV0uGk=" [mod."github.com/google/pprof"] - version = "v0.0.0-20230228050547-1710fef4ab10" - hash = "sha256-ts+I48BUzyra4LFY+qc7I7uPliuMMAh1d1UfANDBqPo=" + version = "v0.0.0-20240424215950-a892ee059fd6" + hash = "sha256-j3nKXUZtSbCVR+/UcMhB8VpgV11ZAf7lpVj1dgn0LSY=" [mod."github.com/google/s2a-go"] version = "v0.1.7" hash = "sha256-E+SX/3VmRI5qN7SbnRP4Tt+gQTq93pScpY9U2tTmIU0=" @@ -480,8 +480,8 @@ schema = 3 version = "v0.0.5" hash = "sha256-/5i70IkH/qSW5KjGzv8aQNKh9tHoz98tqtL0K2DMFn4=" [mod."github.com/onsi/ginkgo/v2"] - version = "v2.17.1" - hash = "sha256-kZgSreUHuYwl6Hh1SHmSasBwbV7qeYeg4BFYoxMchg8=" + version = "v2.17.2" + hash = "sha256-APrPglqYnX2a6EypqDXi05Quou30UQdcDxztT1yhM3s=" [mod."github.com/onsi/gomega"] version = "v1.33.0" hash = "sha256-0LMaPjLYtaFy95PdY0oyFo00pN0ZStCvaurUuoPSPIo=" @@ -667,8 +667,8 @@ schema = 3 version = "v0.0.0-20230711153332-06a737ee72cb" hash = "sha256-Cbw10ZJ+jATPV232G47xZrn6ExO1FDtiT6nlMRCH7EI=" [mod."golang.org/x/mod"] - version = "v0.14.0" - hash = "sha256-sx3hWp5l99DBfIrn821ohfoBwvaITSHMWbzPvX0btLM=" + version = "v0.17.0" + hash = "sha256-CLaPeF6uTFuRDv4oHwOQE6MCMvrzkUjWN3NuyywZjKU=" [mod."golang.org/x/net"] version = "v0.24.0" hash = "sha256-w1c21ljta5wNIyel9CSIn/crPzwOCRofNKhqmfs4aEQ=" @@ -676,8 +676,8 @@ schema = 3 version = "v0.17.0" hash = "sha256-M2ZZQZt449RJL18YpzGiAiqfGsDVMsr1IVWbYp/G/go=" [mod."golang.org/x/sync"] - version = "v0.6.0" - hash = "sha256-LLims/wjDZtIqlYCVHREewcUOX4hwRwplEuZKPOJ/HI=" + version = "v0.7.0" + hash = "sha256-2ETllEu2GDWoOd/yMkOkLC2hWBpKzbVZ8LhjLu0d2A8=" [mod."golang.org/x/sys"] version = "v0.19.0" hash = "sha256-cmuL31TYLJmDm/fDnI2Sn0wB88cpdOHV1+urorsJWx4=" @@ -691,8 +691,8 @@ schema = 3 version = "v0.5.0" hash = "sha256-W6RgwgdYTO3byIPOFxrP2IpAZdgaGowAaVfYby7AULU=" [mod."golang.org/x/tools"] - version = "v0.17.0" - hash = "sha256-CxuHfKKtUkn3VjA7D9WQjzvV1EUbyI/xMNhb5CxO6IQ=" + version = "v0.20.0" + hash = "sha256-g5T5FrNPO/cf2W1lc+/93FcFB3HftPjqI72FueD9Wt8=" [mod."google.golang.org/api"] version = "v0.162.0" hash = "sha256-+AsT4DPjefEmPPelZoSHuQ8nCHhmhhUWU4UGnJ/8+fg=" diff --git a/ibc/testing/endpoint.go b/ibc/testing/endpoint.go index 2ff97d07b0..0953ad1969 100644 --- a/ibc/testing/endpoint.go +++ b/ibc/testing/endpoint.go @@ -4,6 +4,7 @@ package ibctesting import ( "fmt" + "time" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" @@ -109,10 +110,22 @@ func (endpoint *Endpoint) CreateClient() (err error) { return err } + require.NotNil( + endpoint.Chain.T, endpoint.Chain.SenderAccount, + fmt.Sprintf("expected sender account on chain with ID %q not to be nil", endpoint.Chain.ChainID), + ) + + zeroTimestamp := uint64(time.Time{}.UnixNano()) + require.NotEqual( + endpoint.Chain.T, consensusState.GetTimestamp(), zeroTimestamp, + "current timestamp on the last header is the zero time; it might be necessary to commit blocks with the IBC coordinator", + ) + msg, err := clienttypes.NewMsgCreateClient( clientState, consensusState, endpoint.Chain.SenderAccount.GetAddress().String(), ) require.NoError(endpoint.Chain.T, err) + require.NoError(endpoint.Chain.T, msg.ValidateBasic(), "failed to validate create client msg") res, err := SendMsgs(endpoint.Chain, DefaultFeeAmt, msg) if err != nil { diff --git a/testutil/integration/ibc/coordinator/coordinator.go b/testutil/integration/ibc/coordinator/coordinator.go index 406ecfc920..1af299d2eb 100644 --- a/testutil/integration/ibc/coordinator/coordinator.go +++ b/testutil/integration/ibc/coordinator/coordinator.go @@ -70,11 +70,16 @@ func NewIntegrationCoordinator(t *testing.T, preConfiguredChains []network.Netwo } } -// GetChain returns the TestChain for a given chainID. +// GetChain returns the TestChain for a given chainID but abstracted to our internal chain interface. func (c *IntegrationCoordinator) GetChain(chainID string) ibcchain.Chain { return c.coord.Chains[chainID] } +// GetTestChain returns the TestChain for a given chainID. +func (c *IntegrationCoordinator) GetTestChain(chainID string) *ibctesting.TestChain { + return c.coord.GetChain(chainID) +} + // GetDummyChainsIDs returns the chainIDs for all dummy chains. func (c *IntegrationCoordinator) GetDummyChainsIDs() []string { return c.dummyChainsIDs diff --git a/utils/utils.go b/utils/utils.go index 66aa67072f..dfabc48aa5 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -25,6 +25,8 @@ const ( MainnetChainID = "evmos_9001" // TestnetChainID defines the Evmos EIP155 chain ID for testnet TestnetChainID = "evmos_9000" + // TestingChainID defines the Evmos EIP155 chain ID for integration test + TestingChainID = "test_9000" // BaseDenom defines the Evmos mainnet denomination BaseDenom = "aevmos" ) @@ -39,6 +41,12 @@ func IsTestnet(chainID string) bool { return strings.HasPrefix(chainID, TestnetChainID) } +// IsTesting returns true if the chain-id has the "test" prefix. +// NOTE: for tests only +func IsTesting(chainID string) bool { + return strings.HasPrefix(chainID, TestingChainID) +} + // IsSupportedKey returns true if the pubkey type is supported by the chain // (i.e eth_secp256k1, amino multisig, ed25519). // NOTE: Nested multisigs are not supported. diff --git a/x/erc20/keeper/ibc_callbacks_integration_test.go b/x/erc20/keeper/ibc_callbacks_integration_test.go index c686ea5c88..c4f198b80f 100644 --- a/x/erc20/keeper/ibc_callbacks_integration_test.go +++ b/x/erc20/keeper/ibc_callbacks_integration_test.go @@ -197,6 +197,67 @@ var _ = Describe("Convert native ERC20 receiving from IBC back to Erc20", Ordere s.Require().Equal(int64(0), erc20CoinsBalance.Amount.Int64()) }) }) + Describe("strv2 bookkeeping - registered uosmo", func() { + BeforeEach(func() { + erc20params := types.DefaultParams() + erc20params.EnableErc20 = true + err := s.app.Erc20Keeper.SetParams(s.EvmosChain.GetContext(), erc20params) + s.Require().NoError(err) + + sender = s.IBCOsmosisChain.SenderAccount.GetAddress().String() + receiver = s.EvmosChain.SenderAccount.GetAddress().String() + senderAcc = sdk.MustAccAddressFromBech32(sender) + receiverAcc = sdk.MustAccAddressFromBech32(receiver) + + // Register uosmo pair + pair, err = s.app.Erc20Keeper.RegisterCoin(s.EvmosChain.GetContext(), osmoMeta) + s.Require().NoError(err) + }) + It("should register receiver address", func() { + found := s.app.Erc20Keeper.HasSTRv2Address(s.EvmosChain.GetContext(), receiverAcc) + s.Require().False(found) + + s.EvmosChain.Coordinator.CommitBlock() + // Send coins + s.SendAndReceiveMessage(s.pathOsmosisEvmos, s.IBCOsmosisChain, "uosmo", amount, sender, receiver, 1, "") + s.EvmosChain.Coordinator.CommitBlock() + + found = s.app.Erc20Keeper.HasSTRv2Address(s.EvmosChain.GetContext(), receiverAcc) + s.Require().True(found) + + s.SendAndReceiveMessage(s.pathOsmosisEvmos, s.IBCOsmosisChain, "uosmo", amount, sender, receiver, 2, "") + s.EvmosChain.Coordinator.CommitBlock() + + found = s.app.Erc20Keeper.HasSTRv2Address(s.EvmosChain.GetContext(), receiverAcc) + s.Require().True(found) + }) + }) + + Describe("strv2 bookkeeping - unregistered uosmo", func() { + BeforeEach(func() { + erc20params := types.DefaultParams() + erc20params.EnableErc20 = true + err := s.app.Erc20Keeper.SetParams(s.EvmosChain.GetContext(), erc20params) + s.Require().NoError(err) + + sender = s.IBCOsmosisChain.SenderAccount.GetAddress().String() + receiver = s.EvmosChain.SenderAccount.GetAddress().String() + senderAcc = sdk.MustAccAddressFromBech32(sender) + receiverAcc = sdk.MustAccAddressFromBech32(receiver) + }) + It("should not register receiver address", func() { + found := s.app.Erc20Keeper.HasSTRv2Address(s.EvmosChain.GetContext(), receiverAcc) + s.Require().False(found) + + s.EvmosChain.Coordinator.CommitBlock() + // Send coins + s.SendAndReceiveMessage(s.pathOsmosisEvmos, s.IBCOsmosisChain, "uosmo", amount, sender, receiver, 1, "") + s.EvmosChain.Coordinator.CommitBlock() + + found = s.app.Erc20Keeper.HasSTRv2Address(s.EvmosChain.GetContext(), receiverAcc) + s.Require().False(found) + }) + }) }) var _ = Describe("Native coins from IBC", Ordered, func() { diff --git a/x/erc20/keeper/msg_server.go b/x/erc20/keeper/msg_server.go index c27a54b8a8..e122c92d53 100644 --- a/x/erc20/keeper/msg_server.go +++ b/x/erc20/keeper/msg_server.go @@ -62,7 +62,111 @@ func (k Keeper) ConvertERC20( return nil, types.ErrUndefinedOwner } -// convertERC20IntoCoinsForNativeToken handles the erc20 conversion for a native erc20 token +// convertERC20NativeCoin handles the erc20 conversion for a native Cosmos coin +// token pair: +// - burn escrowed tokens +// - unescrow coins that have been previously escrowed with ConvertCoin +// - check if coin balance increased by amount +// - check if token balance decreased by amount +func (k Keeper) convertERC20NativeCoin( + ctx sdk.Context, + pair types.TokenPair, + msg *types.MsgConvertERC20, + receiver sdk.AccAddress, + sender common.Address, +) (*types.MsgConvertERC20Response, error) { + // NOTE: coin fields already validated + coins := sdk.Coins{sdk.Coin{Denom: pair.Denom, Amount: msg.Amount}} + erc20 := contracts.ERC20MinterBurnerDecimalsContract.ABI + contract := pair.GetERC20Contract() + balanceCoin := k.bankKeeper.GetBalance(ctx, receiver, pair.Denom) + balanceToken := k.BalanceOf(ctx, erc20, contract, sender) + if balanceToken == nil { + return nil, errorsmod.Wrap(types.ErrEVMCall, "failed to retrieve balance") + } + + // Burn escrowed tokens + _, err := k.CallEVM(ctx, erc20, types.ModuleAddress, contract, true, "burnCoins", sender, msg.Amount.BigInt()) + if err != nil { + return nil, err + } + + // Unescrow coins and send to receiver + err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, receiver, coins) + if err != nil { + return nil, err + } + + // Check expected receiver balance after transfer + balanceCoinAfter := k.bankKeeper.GetBalance(ctx, receiver, pair.Denom) + expCoin := balanceCoin.Add(coins[0]) + if ok := balanceCoinAfter.IsEqual(expCoin); !ok { + return nil, errorsmod.Wrapf( + types.ErrBalanceInvariance, + "invalid coin balance - expected: %v, actual: %v", + expCoin, balanceCoinAfter, + ) + } + + // Check expected Sender balance after transfer + tokens := coins[0].Amount.BigInt() + balanceTokenAfter := k.BalanceOf(ctx, erc20, contract, sender) + if balanceTokenAfter == nil { + return nil, errorsmod.Wrap(types.ErrEVMCall, "failed to retrieve balance") + } + + expToken := big.NewInt(0).Sub(balanceToken, tokens) + if r := balanceTokenAfter.Cmp(expToken); r != 0 { + return nil, errorsmod.Wrapf( + types.ErrBalanceInvariance, + "invalid token balance - expected: %v, actual: %v", + expToken, balanceTokenAfter, + ) + } + + // Keep track of interactions for STR v2 migration + // TODO: to be removed in 2nd upgrade + if !k.HasSTRv2Address(ctx, receiver) { + k.SetSTRv2Address(ctx, receiver) + } + + defer func() { + telemetry.IncrCounterWithLabels( + []string{"tx", "msg", "convert", "erc20", "total"}, + 1, + []metrics.Label{ + telemetry.NewLabel("denom", pair.Denom), + }, + ) + + if msg.Amount.IsInt64() { + telemetry.IncrCounterWithLabels( + []string{"tx", "msg", "convert", "erc20", "amount", "total"}, + float32(msg.Amount.Int64()), + []metrics.Label{ + telemetry.NewLabel("denom", pair.Denom), + }, + ) + } + }() + + ctx.EventManager().EmitEvents( + sdk.Events{ + sdk.NewEvent( + types.EventTypeConvertERC20, + sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender), + sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()), + sdk.NewAttribute(types.AttributeKeyCosmosCoin, pair.Denom), + sdk.NewAttribute(types.AttributeKeyERC20Token, msg.ContractAddress), + ), + }, + ) + + return &types.MsgConvertERC20Response{}, nil +} + +// convertERC20NativeToken handles the erc20 conversion for a native erc20 token // pair: // - escrow tokens on module account // - mint coins on bank module diff --git a/x/erc20/keeper/str_v2.go b/x/erc20/keeper/str_v2.go new file mode 100644 index 0000000000..a539021b3b --- /dev/null +++ b/x/erc20/keeper/str_v2.go @@ -0,0 +1,58 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/evmos/evmos/v18/x/erc20/types" +) + +// SetSTRv2Address stores an address that will be affected by the +// Single Token Representation v2 migration. +func (k Keeper) SetSTRv2Address(ctx sdk.Context, address sdk.AccAddress) { + store := prefix.NewStore( + ctx.KVStore(k.storeKey), + types.KeyPrefixSTRv2Addresses, + ) + store.Set(address.Bytes(), []byte{}) +} + +// HasSTRv2Address checks if a given address has already been stored as +// affected by the STR v2 migration. +func (k Keeper) HasSTRv2Address(ctx sdk.Context, address sdk.AccAddress) bool { + store := prefix.NewStore( + ctx.KVStore(k.storeKey), + types.KeyPrefixSTRv2Addresses, + ) + return store.Has(address.Bytes()) +} + +// DeleteSTRv2Address removes the entry already stored +// NOTE: for testing purpose only +func (k Keeper) DeleteSTRv2Address(ctx sdk.Context, address sdk.AccAddress) { + store := prefix.NewStore( + ctx.KVStore(k.storeKey), + types.KeyPrefixSTRv2Addresses, + ) + store.Delete(address.Bytes()) +} + +// GetAllSTRV2Address iterates over all the stored account that interacted with registered coins. +// and returns them in an array +func (k Keeper) GetAllSTRV2Address(ctx sdk.Context) []sdk.AccAddress { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.KeyPrefixSTRv2Addresses) + defer iterator.Close() + + accAddresses := []sdk.AccAddress{} + + for ; iterator.Valid(); iterator.Next() { + // First byte is the prefix, final bytes is the address + // iterator.Value is empty + address := sdk.AccAddress(iterator.Key()[1:]) + accAddresses = append(accAddresses, address) + } + return accAddresses +} diff --git a/x/erc20/keeper/str_v2_test.go b/x/erc20/keeper/str_v2_test.go new file mode 100644 index 0000000000..0132515f2e --- /dev/null +++ b/x/erc20/keeper/str_v2_test.go @@ -0,0 +1,50 @@ +package keeper_test + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (suite *KeeperTestSuite) TestGetAllSTRV2Address() { + address1 := suite.address.Bytes() + address2 := suite.consAddress.Bytes() + + testCases := []struct { + name string + malleate func() + expected []sdk.AccAddress + }{ + { + "space is empty", + func() { + }, + []sdk.AccAddress{}, + }, + { + "one address", + func() { + suite.app.Erc20Keeper.SetSTRv2Address(suite.ctx, address1) + }, + []sdk.AccAddress{address1}, + }, + { + "two addresses", + func() { + suite.app.Erc20Keeper.SetSTRv2Address(suite.ctx, address1) + suite.app.Erc20Keeper.SetSTRv2Address(suite.ctx, address2) + }, + []sdk.AccAddress{address1, address2}, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset + + tc.malleate() + + addresses := suite.app.Erc20Keeper.GetAllSTRV2Address(suite.ctx) + suite.Require().ElementsMatch(tc.expected, addresses) + }) + } +} diff --git a/x/erc20/keeper/testdata/tokenTransfer.go b/x/erc20/keeper/testdata/tokenTransfer.go new file mode 100644 index 0000000000..4cdd6684a1 --- /dev/null +++ b/x/erc20/keeper/testdata/tokenTransfer.go @@ -0,0 +1,26 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package testdata + +import ( + _ "embed" // embed compiled smart contract + "encoding/json" + + evmtypes "github.com/evmos/evmos/v18/x/evm/types" +) + +var ( + //go:embed tokenTransfer.json + tokenTransferJSON []byte + + // TokenTransferContract is the compiled tokenTransfer contract + TokenTransferContract evmtypes.CompiledContract +) + +func init() { + err := json.Unmarshal(tokenTransferJSON, &TokenTransferContract) + if err != nil { + panic(err) + } +} diff --git a/x/erc20/keeper/testdata/tokenTransfer.json b/x/erc20/keeper/testdata/tokenTransfer.json new file mode 100644 index 0000000000..3c1d9c3dec --- /dev/null +++ b/x/erc20/keeper/testdata/tokenTransfer.json @@ -0,0 +1,4 @@ +{ + "abi": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + "bin": "608060405234801561000f575f80fd5b506040516103ae3803806103ae833981810160405281019061003191906100d4565b805f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506100ff565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6100a38261007a565b9050919050565b6100b381610099565b81146100bd575f80fd5b50565b5f815190506100ce816100aa565b92915050565b5f602082840312156100e9576100e8610076565b5b5f6100f6848285016100c0565b91505092915050565b6102a28061010c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c80631072cbea1461002d575b5f80fd5b6100476004803603810190610042919061017b565b610049565b005b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd3384846040518463ffffffff1660e01b81526004016100a5939291906101d7565b6020604051808303815f875af11580156100c1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100e59190610241565b505050565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610117826100ee565b9050919050565b6101278161010d565b8114610131575f80fd5b50565b5f813590506101428161011e565b92915050565b5f819050919050565b61015a81610148565b8114610164575f80fd5b50565b5f8135905061017581610151565b92915050565b5f8060408385031215610191576101906100ea565b5b5f61019e85828601610134565b92505060206101af85828601610167565b9150509250929050565b6101c28161010d565b82525050565b6101d181610148565b82525050565b5f6060820190506101ea5f8301866101b9565b6101f760208301856101b9565b61020460408301846101c8565b949350505050565b5f8115159050919050565b6102208161020c565b811461022a575f80fd5b50565b5f8151905061023b81610217565b92915050565b5f60208284031215610256576102556100ea565b5b5f6102638482850161022d565b9150509291505056fea2646970667358221220bbc26fe3b0578219ad6235976ab9666151b3049b14b3d87a76d448f47aad0e8964736f6c63430008180033" +} \ No newline at end of file diff --git a/x/erc20/keeper/testdata/tokenTransfer.sol b/x/erc20/keeper/testdata/tokenTransfer.sol new file mode 100644 index 0000000000..7d11c67368 --- /dev/null +++ b/x/erc20/keeper/testdata/tokenTransfer.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +/* + @title tokenTransfer + @dev This contract is used to test that any addresses transferring ERC-20 tokens + are tracked if they're ERC-20 representations of native Cosmos coins. +*/ +contract tokenTransfer { + ERC20 token; + + constructor(address tokenAddress){ + token = ERC20(tokenAddress); + } + + /* + @notice This function is used to transfer ERC-20 tokens to a given address. + @param to The address to transfer the tokens to + @param amount The amount of tokens to transfer + */ + function transferToken(address to, uint256 amount) public { + token.transfer(to, amount); + } +} diff --git a/x/erc20/strv2/strv2_hooks_test.go b/x/erc20/strv2/strv2_hooks_test.go new file mode 100644 index 0000000000..91e1417256 --- /dev/null +++ b/x/erc20/strv2/strv2_hooks_test.go @@ -0,0 +1,310 @@ +package strv2_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/evmos/evmos/v18/contracts" + testfactory "github.com/evmos/evmos/v18/testutil/integration/evmos/factory" + grpchandler "github.com/evmos/evmos/v18/testutil/integration/evmos/grpc" + testkeyring "github.com/evmos/evmos/v18/testutil/integration/evmos/keyring" + "github.com/evmos/evmos/v18/testutil/integration/evmos/network" + evmtypes "github.com/evmos/evmos/v18/x/evm/types" + "github.com/stretchr/testify/require" +) + +type STRV2WEVMOSHooksTestSuite struct { + keyring testkeyring.Keyring + network *network.UnitTestNetwork + factory testfactory.TxFactory + + // account is the address of the account to withdraw WEVMOS from. + account common.Address + // wevmosContract is the address of the WEVMOS contract. + wevmosContract common.Address +} + +const ( + + // erc20Deployer is the index for the account that deploys the ERC-20 contract. + erc20Deployer = 0 +) + +// sentWEVMOS is the amount of WEVMOS sent to the WEVMOS contract during testing. +var sentWEVMOS = sdk.NewInt(1e18) + +func TestDepositWEVMOS(t *testing.T) { + t.Parallel() + + senderIdx := 0 + kr := testkeyring.New(1) + + testcases := []struct { + name string + malleate func(suite *STRV2WEVMOSHooksTestSuite) error + expFound bool + errContains string + chainID string + }{ + { + name: "found - deposit WEVMOS", + malleate: func(ts *STRV2WEVMOSHooksTestSuite) error { + // Deploy WEVMOS contract + wevmosAddr, err := ts.factory.DeployContract( + ts.keyring.GetPrivKey(erc20Deployer), + evmtypes.EvmTxArgs{}, + testfactory.ContractDeploymentData{Contract: contracts.WEVMOSContract}, + ) + require.NoError(t, err, "failed to deploy WEVMOS contract") + + ts.account = kr.GetAddr(senderIdx) + ts.wevmosContract = wevmosAddr + + require.False(t, ts.network.App.Erc20Keeper.HasSTRv2Address(ts.network.GetContext(), ts.keyring.GetAccAddr(0))) + + return nil + }, + expFound: true, + chainID: "test_9000-1", + }, + { + name: "found - should not fail if address already there while deposit WEVMOS", + malleate: func(ts *STRV2WEVMOSHooksTestSuite) error { + // Deploy WEVMOS contract + wevmosAddr, err := ts.factory.DeployContract( + ts.keyring.GetPrivKey(erc20Deployer), + evmtypes.EvmTxArgs{}, + testfactory.ContractDeploymentData{Contract: contracts.WEVMOSContract}, + ) + require.NoError(t, err, "failed to deploy WEVMOS contract") + + ts.account = kr.GetAddr(senderIdx) + ts.wevmosContract = wevmosAddr + + ts.network.App.Erc20Keeper.SetSTRv2Address(ts.network.GetContext(), ts.keyring.GetAccAddr(0)) + + return nil + }, + expFound: true, + chainID: "test_9000-1", + }, + { + name: "not found - should not register since its not expected contract", + malleate: func(ts *STRV2WEVMOSHooksTestSuite) error { + // Deploy WEVMOS contract + wevmosAddr, err := ts.factory.DeployContract( + ts.keyring.GetPrivKey(erc20Deployer), + evmtypes.EvmTxArgs{}, + testfactory.ContractDeploymentData{Contract: contracts.WEVMOSContract}, + ) + require.NoError(t, err, "failed to deploy WEVMOS contract") + + ts.account = kr.GetAddr(senderIdx) + ts.wevmosContract = wevmosAddr + + return nil + }, + expFound: false, + chainID: "evmos_9000-1", + }, + } + + for _, tc := range testcases { + tc := tc // capture range variable (for parallel testing) + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // Set up a new network + nw := network.NewUnitTestNetwork( + network.WithChainID(tc.chainID), + network.WithPreFundedAccounts(kr.GetAllAccAddrs()...), + ) + handler := grpchandler.NewIntegrationHandler(nw) + txFactory := testfactory.New(nw, handler) + + ts := &STRV2WEVMOSHooksTestSuite{ + keyring: kr, + network: nw, + factory: txFactory, + } + + err := tc.malleate(ts) + require.NoError(t, err, "failed to malleate test suite") + + // Send WEVMOS to account + _, err = ts.factory.ExecuteEthTx( + ts.keyring.GetPrivKey(senderIdx), + evmtypes.EvmTxArgs{ + To: &ts.wevmosContract, + Amount: sentWEVMOS.BigInt(), + // FIXME: the gas simulation is not working correctly - otherwise results in out of gas + GasLimit: 100_000, + }, + ) + require.NoError(t, err, "failed to send WEVMOS to account") + + require.Equal(t, tc.expFound, ts.network.App.Erc20Keeper.HasSTRv2Address(ts.network.GetContext(), ts.keyring.GetAccAddr(0))) + }) + } +} + +func TestWithdrawWEVMOS(t *testing.T) { + t.Parallel() + + senderIdx := 0 + kr := testkeyring.New(1) + + testcases := []struct { + name string + malleate func(suite *STRV2WEVMOSHooksTestSuite) error + expFound bool + errContains string + chainID string + }{ + { + name: "found - withdraw WEVMOS", + malleate: func(ts *STRV2WEVMOSHooksTestSuite) error { + // Deploy WEVMOS contract + wevmosAddr, err := ts.factory.DeployContract( + ts.keyring.GetPrivKey(erc20Deployer), + evmtypes.EvmTxArgs{}, + testfactory.ContractDeploymentData{Contract: contracts.WEVMOSContract}, + ) + require.NoError(t, err, "failed to deploy WEVMOS contract") + + // Send WEVMOS to account + _, err = ts.factory.ExecuteEthTx( + ts.keyring.GetPrivKey(senderIdx), + evmtypes.EvmTxArgs{ + To: &wevmosAddr, + Amount: sentWEVMOS.BigInt(), + // FIXME: the gas simulation is not working correctly - otherwise results in out of gas + GasLimit: 100_000, + }, + ) + require.NoError(t, err, "failed to send WEVMOS to account") + + // Address was added after deposit, delete the entry to test withdraw + require.True(t, ts.network.App.Erc20Keeper.HasSTRv2Address(ts.network.GetContext(), ts.keyring.GetAccAddr(0))) + ts.network.App.Erc20Keeper.DeleteSTRv2Address(ts.network.GetContext(), ts.keyring.GetAccAddr(0)) + require.False(t, ts.network.App.Erc20Keeper.HasSTRv2Address(ts.network.GetContext(), ts.keyring.GetAccAddr(0))) + + ts.account = kr.GetAddr(senderIdx) + ts.wevmosContract = wevmosAddr + + return nil + }, + expFound: true, + chainID: "test_9000-1", + }, + { + name: "found - with address already registered - withdraw WEVMOS", + malleate: func(ts *STRV2WEVMOSHooksTestSuite) error { + // Deploy WEVMOS contract + wevmosAddr, err := ts.factory.DeployContract( + ts.keyring.GetPrivKey(erc20Deployer), + evmtypes.EvmTxArgs{}, + testfactory.ContractDeploymentData{Contract: contracts.WEVMOSContract}, + ) + require.NoError(t, err, "failed to deploy WEVMOS contract") + + // Send WEVMOS to account + _, err = ts.factory.ExecuteEthTx( + ts.keyring.GetPrivKey(senderIdx), + evmtypes.EvmTxArgs{ + To: &wevmosAddr, + Amount: sentWEVMOS.BigInt(), + // FIXME: the gas simulation is not working correctly - otherwise results in out of gas + GasLimit: 100_000, + }, + ) + require.NoError(t, err, "failed to send WEVMOS to account") + + require.True(t, ts.network.App.Erc20Keeper.HasSTRv2Address(ts.network.GetContext(), ts.keyring.GetAccAddr(0))) + + ts.account = kr.GetAddr(senderIdx) + ts.wevmosContract = wevmosAddr + + return nil + }, + expFound: true, + chainID: "test_9000-1", + }, + { + name: "not found - wrong contract - withdraw WEVMOS", + malleate: func(ts *STRV2WEVMOSHooksTestSuite) error { + // Deploy WEVMOS contract + wevmosAddr, err := ts.factory.DeployContract( + ts.keyring.GetPrivKey(erc20Deployer), + evmtypes.EvmTxArgs{}, + testfactory.ContractDeploymentData{Contract: contracts.WEVMOSContract}, + ) + require.NoError(t, err, "failed to deploy WEVMOS contract") + + // Send WEVMOS to account + _, err = ts.factory.ExecuteEthTx( + ts.keyring.GetPrivKey(senderIdx), + evmtypes.EvmTxArgs{ + To: &wevmosAddr, + Amount: sentWEVMOS.BigInt(), + // FIXME: the gas simulation is not working correctly - otherwise results in out of gas + GasLimit: 100_000, + }, + ) + require.NoError(t, err, "failed to send WEVMOS to account") + + require.True(t, ts.network.App.Erc20Keeper.HasSTRv2Address(ts.network.GetContext(), ts.keyring.GetAccAddr(0))) + + ts.account = kr.GetAddr(senderIdx) + ts.wevmosContract = wevmosAddr + + return nil + }, + expFound: false, + chainID: "evmos_9000-1", + }, + } + + for _, tc := range testcases { + tc := tc // capture range variable (for parallel testing) + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // Set up a new network + nw := network.NewUnitTestNetwork( + network.WithChainID("test_9000-1"), + network.WithPreFundedAccounts(kr.GetAllAccAddrs()...), + ) + handler := grpchandler.NewIntegrationHandler(nw) + txFactory := testfactory.New(nw, handler) + + ts := &STRV2WEVMOSHooksTestSuite{ + keyring: kr, + network: nw, + factory: txFactory, + } + + err := tc.malleate(ts) + require.NoError(t, err, "failed to malleate test suite") + + _, err = ts.factory.ExecuteContractCall( + ts.keyring.GetPrivKey(0), + evmtypes.EvmTxArgs{ + To: &ts.wevmosContract, + }, + testfactory.CallArgs{ + ContractABI: contracts.WEVMOSContract.ABI, + MethodName: "withdraw", + Args: []interface{}{ + transferAmount, + }, + }, + ) + require.NoError(t, err) + require.True(t, ts.network.App.Erc20Keeper.HasSTRv2Address(ts.network.GetContext(), ts.keyring.GetAccAddr(0))) + }) + } +} diff --git a/x/erc20/strv2/strv2_integration_test.go b/x/erc20/strv2/strv2_integration_test.go new file mode 100644 index 0000000000..72b04e5061 --- /dev/null +++ b/x/erc20/strv2/strv2_integration_test.go @@ -0,0 +1,662 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package strv2_test + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + testfactory "github.com/evmos/evmos/v18/testutil/integration/evmos/factory" + "github.com/evmos/evmos/v18/testutil/integration/evmos/grpc" + testkeyring "github.com/evmos/evmos/v18/testutil/integration/evmos/keyring" + testnetwork "github.com/evmos/evmos/v18/testutil/integration/evmos/network" + + // "github.com/evmos/evmos/v18/x/erc20/keeper/testdata" + // erc20types "github.com/evmos/evmos/v18/x/erc20/types" + + . "github.com/onsi/ginkgo/v2" //nolint:revive + . "github.com/onsi/gomega" //nolint:revive + "github.com/pkg/errors" +) + +func TestSTRv2Tracking(t *testing.T) { + // Run Ginkgo BDD tests + RegisterFailHandler(Fail) + RunSpecs(t, "STRv2 Tracking Tests") +} + +type STRv2TrackingSuite struct { + keyring testkeyring.Keyring + network *testnetwork.UnitTestNetwork + handler grpc.Handler + factory testfactory.TxFactory + + nativeCoinERC20Addr common.Address + registeredERC20Addr common.Address + unregisteredERC20Addr common.Address +} + +func CreateTestSuite(chainID string) (*STRv2TrackingSuite, error) { + keyring := testkeyring.New(3) + + genesisSetup, err := CreateGenesisSetup(keyring) + if err != nil { + return nil, errors.Wrap(err, "failed to create genesis setup") + } + + if chainID == "" { + chainID = "evmos_9000-1" + } + + network := testnetwork.NewUnitTestNetwork( + testnetwork.WithChainID(chainID), + testnetwork.WithPreFundedAccounts(keyring.GetAllAccAddrs()...), + testnetwork.WithCustomGenesis(*genesisSetup.genesisState), + ) + handler := grpc.NewIntegrationHandler(network) + factory := testfactory.New(network, handler) + + // NOTE: this is necessary to enable e.g. erc20Keeper.BalanceOf(...) to work + // correctly internally. + // Removing it will break a bunch of tests giving errors like: "failed to retrieve balance" + if err = network.NextBlock(); err != nil { + return nil, errors.Wrap(err, "failed to advance block") + } + + return &STRv2TrackingSuite{ + keyring: keyring, + network: network, + handler: handler, + factory: factory, + nativeCoinERC20Addr: genesisSetup.nativeCoinERC20Addr, + registeredERC20Addr: genesisSetup.registeredERC20Addr, + unregisteredERC20Addr: genesisSetup.unregisteredERC20Addr, + }, nil +} + +const ( + deployerIdx = 0 + nativeIBCCoinDenom = "coin" +) + +var ( + mintAmount = big.NewInt(1000000000000000000) + convertAmount = testnetwork.PrefundedAccountInitialBalance.QuoRaw(10) + transferAmount = convertAmount.QuoRaw(10).BigInt() +) + +// var _ = Describe("STRv2 Tracking -", func() { +// var s *STRv2TrackingSuite + +// BeforeEach(func() { +// var err error +// s, err = CreateTestSuite(utils.MainnetChainID + "-1") +// Expect(err).ToNot(HaveOccurred(), "failed to create test suite") + +// // NOTE: this is necessary to enable e.g. erc20Keeper.BalanceOf(...) to work +// // correctly internally. +// // Removing it will break a bunch of tests giving errors like: "failed to retrieve balance" +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") +// }) + +// When("sending an EVM transaction", func() { +// Context("which interacts with a registered native token pair ERC-20 contract", func() { +// Context("in a direct call to the token pair contract", func() { +// It("should add the from and to addresses to the store if it is not already stored", func() { +// sender := s.keyring.GetKey(0) +// receiver := s.keyring.GetKey(2) + +// senderAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeFalse(), "expected address not to be stored before conversion") +// receiverAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), receiver.AccAddr) +// Expect(receiverAddrTracked).To(BeFalse(), "expected address not to be stored before conversion") + +// _, err := s.factory.ExecuteContractCall( +// sender.Priv, +// evmtypes.EvmTxArgs{ +// To: &s.nativeCoinERC20Addr, +// }, +// testfactory.CallArgs{ +// ContractABI: contracts.ERC20MinterBurnerDecimalsContract.ABI, +// MethodName: "transfer", +// Args: []interface{}{ +// receiver.Addr, +// transferAmount, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to transfer tokens of Cosmos native ERC-20 token pair") + +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") + +// senderAddrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeTrue(), "expected address to be stored") +// receiverAddrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(receiverAddrTracked).To(BeTrue(), "expected address to be stored") +// }) + +// It("should not fail if the addresses are already stored", func() { +// sender := s.keyring.GetKey(0) +// receiver := s.keyring.GetKey(2) + +// senderAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeFalse(), "expected address not to be stored before conversion") +// receiverAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), receiver.AccAddr) +// Expect(receiverAddrTracked).To(BeFalse(), "expected address not to be stored before conversion") + +// _, err := s.factory.ExecuteContractCall( +// sender.Priv, +// evmtypes.EvmTxArgs{ +// To: &s.nativeCoinERC20Addr, +// }, +// testfactory.CallArgs{ +// ContractABI: contracts.ERC20MinterBurnerDecimalsContract.ABI, +// MethodName: "transfer", +// Args: []interface{}{ +// receiver.Addr, +// transferAmount, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to transfer tokens of Cosmos native ERC-20 token pair") + +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") + +// _, err = s.factory.ExecuteContractCall( +// sender.Priv, +// evmtypes.EvmTxArgs{ +// To: &s.nativeCoinERC20Addr, +// }, +// testfactory.CallArgs{ +// ContractABI: contracts.ERC20MinterBurnerDecimalsContract.ABI, +// MethodName: "transfer", +// Args: []interface{}{ +// receiver.Addr, +// transferAmount, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to transfer tokens of Cosmos native ERC-20 token pair (2nd call)") + +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") + +// senderAddrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeTrue(), "expected address to be still stored") +// receiverAddrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), receiver.AccAddr) +// Expect(receiverAddrTracked).To(BeTrue(), "expected address to be still stored") +// }) + +// It("should not store anything if calling a different method than transfer or transferFrom", func() { +// sender := s.keyring.GetKey(0) +// grantee := s.keyring.GetKey(2) + +// senderAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeFalse(), "expected address not to be stored before conversion") +// receiverAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), grantee.AccAddr) +// Expect(receiverAddrTracked).To(BeFalse(), "expected address not to be stored before conversion") + +// _, err := s.factory.ExecuteContractCall( +// sender.Priv, +// evmtypes.EvmTxArgs{ +// To: &s.nativeCoinERC20Addr, +// }, +// testfactory.CallArgs{ +// ContractABI: contracts.ERC20MinterBurnerDecimalsContract.ABI, +// MethodName: "approve", +// Args: []interface{}{ +// grantee.Addr, +// transferAmount, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to approve ERC-20 transfer") + +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") + +// senderAddrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeFalse(), "expected address not to be stored") +// receiverAddrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), grantee.AccAddr) +// Expect(receiverAddrTracked).To(BeFalse(), "expected address not to be stored") +// }) +// }) + +// Context("in a call to the token pair contract from another contract", func() { +// var ( +// senderIdx = deployerIdx +// tokenTransferAddr common.Address +// ) + +// BeforeEach(func() { +// deployer := s.keyring.GetKey(deployerIdx) +// sender := s.keyring.GetKey(senderIdx) + +// var err error +// tokenTransferAddr, err = s.factory.DeployContract( +// deployer.Priv, +// evmtypes.EvmTxArgs{}, +// testfactory.ContractDeploymentData{ +// Contract: testdata.TokenTransferContract, +// ConstructorArgs: []interface{}{s.nativeCoinERC20Addr}, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to deploy ERC-20 transfer contract") + +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") + +// // approve the contract to spend on behalf of the sender +// _, err = s.factory.ExecuteContractCall( +// sender.Priv, +// evmtypes.EvmTxArgs{ +// To: &s.nativeCoinERC20Addr, +// }, +// testfactory.CallArgs{ +// ContractABI: contracts.ERC20MinterBurnerDecimalsContract.ABI, +// MethodName: "approve", +// Args: []interface{}{ +// tokenTransferAddr, +// transferAmount, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to approve ERC-20 transfer contract to spend on behalf of the sender") + +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") +// }) + +// It("should add the from AND to address to the store if it is not already stored", func() { +// sender := s.keyring.GetKey(senderIdx) +// receiver := s.keyring.GetKey(senderIdx + 1) + +// senderAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeFalse(), "expected address not to be stored before conversion") +// receiverAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), receiver.AccAddr) +// Expect(receiverAddrTracked).To(BeFalse(), "expected address not to be stored before conversion") + +// _, err := s.factory.ExecuteContractCall( +// sender.Priv, +// evmtypes.EvmTxArgs{ +// To: &tokenTransferAddr, +// }, +// testfactory.CallArgs{ +// ContractABI: testdata.TokenTransferContract.ABI, +// MethodName: "transferToken", +// Args: []interface{}{ +// receiver.Addr, +// transferAmount, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to transfer tokens of Cosmos native ERC-20 token pair") + +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") + +// senderAddrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeTrue(), "expected address to be stored") +// receiverAddrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), receiver.AccAddr) +// Expect(receiverAddrTracked).To(BeTrue(), "expected address to be stored") +// }) + +// It("should add the from address if sending to the ERC-20 module address", func() { +// sender := s.keyring.GetKey(senderIdx) + +// senderAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeFalse(), "expected address not to be stored before conversion") + +// _, err := s.factory.ExecuteContractCall( +// sender.Priv, +// evmtypes.EvmTxArgs{ +// To: &tokenTransferAddr, +// }, +// testfactory.CallArgs{ +// ContractABI: testdata.TokenTransferContract.ABI, +// MethodName: "transferToken", +// Args: []interface{}{ +// erc20types.ModuleAddress, +// transferAmount, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to transfer tokens of Cosmos native ERC-20 token pair") + +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") + +// senderAddrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeTrue(), "expected address to be stored") +// }) +// }) + +// // NOTE: this is running the coin conversion too +// Context("sending tokens to the module address", func() { +// It("should add the sender address in a direct call", func() { +// sender := s.keyring.GetKey(0) + +// senderAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeFalse(), "expected address not to be stored before conversion") + +// _, err := s.factory.ExecuteContractCall( +// sender.Priv, +// evmtypes.EvmTxArgs{ +// To: &s.nativeCoinERC20Addr, +// }, +// testfactory.CallArgs{ +// ContractABI: contracts.ERC20MinterBurnerDecimalsContract.ABI, +// MethodName: "transfer", +// Args: []interface{}{ +// erc20types.ModuleAddress, +// transferAmount, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to transfer tokens of Cosmos native ERC-20 token pair") + +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") + +// senderAddrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeTrue(), "expected address to be stored") +// erc20AddrTrack := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), erc20types.ModuleAddress.Bytes()) +// Expect(erc20AddrTrack).To(BeFalse(), "expected module address not to be stored") +// }) +// }) +// }) + +// Context("which interacts with a registered non-native token pair ERC-20 contract", func() { +// It("should not add the address to the store", func() { +// deployer := s.keyring.GetKey(deployerIdx) + +// addrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), deployer.AccAddr) +// Expect(addrTracked).To(BeFalse(), "expected address not to be stored before conversion") + +// _, err := s.factory.ExecuteContractCall( +// deployer.Priv, +// evmtypes.EvmTxArgs{ +// To: &s.registeredERC20Addr, +// }, +// testfactory.CallArgs{ +// ContractABI: contracts.ERC20MinterBurnerDecimalsContract.ABI, +// MethodName: "mint", +// Args: []interface{}{ +// deployer.Addr, +// mintAmount, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to interact with registered ERC-20 contract") + +// addrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), deployer.AccAddr) +// Expect(addrTracked).To(BeFalse(), "expected address to not be stored") +// }) +// }) + +// Context("which interacts with an unregistered ERC-20 contract", func() { +// It("should not add the address to the store", func() { +// deployer := s.keyring.GetKey(deployerIdx) + +// _, err := s.factory.ExecuteContractCall( +// deployer.Priv, +// evmtypes.EvmTxArgs{ +// To: &s.unregisteredERC20Addr, +// }, +// testfactory.CallArgs{ +// ContractABI: contracts.ERC20MinterBurnerDecimalsContract.ABI, +// MethodName: "mint", +// Args: []interface{}{ +// deployer.Addr, +// mintAmount, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to interact with unregistered ERC-20 contract") + +// deployerAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), deployer.AccAddr) +// Expect(deployerAddrTracked).To(BeFalse(), "expected address to not be stored") +// }) +// }) +// }) + +// When("manually converting", func() { +// Context("a registered coin into its ERC-20 representation", func() { +// It("should add the address to the store if it is not already stored", func() { +// sender := s.keyring.GetKey(1) + +// addrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(addrTracked).To(BeFalse(), "expected address not to be stored before conversion") + +// _, err := s.factory.ExecuteCosmosTx( +// sender.Priv, +// commonfactory.CosmosTxArgs{ +// Msgs: []sdk.Msg{ +// &erc20types.MsgConvertCoin{ +// Sender: sender.AccAddr.String(), +// Receiver: sender.Addr.String(), +// Coin: sdk.NewCoin(nativeIBCCoinDenom, convertAmount), +// }, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to convert native IBC coin") + +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") + +// addrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(addrTracked).To(BeTrue(), "expected address to be stored") +// }) + +// // TODO: is this correct? Yes, because only the addresses with ERC-20 tokens are relevant? +// It("should store only the receiving address if the sender and receiver are not the same account", func() { +// sender := s.keyring.GetKey(1) +// receiver := s.keyring.GetKey(2) + +// senderAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeFalse(), "expected address not to be stored before conversion") +// receiverAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), receiver.AccAddr) +// Expect(receiverAddrTracked).To(BeFalse(), "expected address not to be stored before conversion") + +// _, err := s.factory.ExecuteCosmosTx( +// sender.Priv, +// commonfactory.CosmosTxArgs{ +// Msgs: []sdk.Msg{ +// &erc20types.MsgConvertCoin{ +// Sender: sender.AccAddr.String(), +// Receiver: receiver.Addr.String(), +// Coin: sdk.NewCoin(nativeIBCCoinDenom, convertAmount), +// }, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to convert native IBC coin") + +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") + +// senderAddrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeFalse(), "expected address to be stored") +// receiverAddrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), receiver.AccAddr) +// Expect(receiverAddrTracked).To(BeTrue(), "expected address to be stored") +// }) + +// It("should not fail if the address is already stored", func() { +// sender := s.keyring.GetKey(1) + +// addrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(addrTracked).To(BeFalse(), "expected address not to be stored before conversion") + +// _, err := s.factory.ExecuteCosmosTx( +// sender.Priv, +// commonfactory.CosmosTxArgs{ +// Msgs: []sdk.Msg{ +// &erc20types.MsgConvertCoin{ +// Sender: sender.AccAddr.String(), +// Receiver: sender.Addr.String(), +// Coin: sdk.NewCoin(nativeIBCCoinDenom, testnetwork.PrefundedAccountInitialBalance.QuoRaw(10)), +// }, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to convert native IBC coin") + +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") + +// _, err = s.factory.ExecuteCosmosTx( +// sender.Priv, +// commonfactory.CosmosTxArgs{ +// Msgs: []sdk.Msg{ +// &erc20types.MsgConvertCoin{ +// Sender: sender.AccAddr.String(), +// Receiver: sender.Addr.String(), +// Coin: sdk.NewCoin(nativeIBCCoinDenom, testnetwork.PrefundedAccountInitialBalance.QuoRaw(10)), +// }, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to convert native IBC coin") + +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") + +// addrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(addrTracked).To(BeTrue(), "expected address to be still stored") +// }) +// }) + +// Context("a registered ERC-20 representation into its native coin", func() { +// It("should add the address to the store if it is not already stored", func() { +// sender := s.keyring.GetKey(deployerIdx) + +// addrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(addrTracked).To(BeFalse(), "expected address not to be stored before conversion") + +// _, err := s.factory.ExecuteCosmosTx( +// sender.Priv, +// commonfactory.CosmosTxArgs{ +// Msgs: []sdk.Msg{ +// &erc20types.MsgConvertERC20{ +// ContractAddress: s.nativeCoinERC20Addr.String(), +// Sender: sender.Addr.String(), +// Receiver: sender.AccAddr.String(), +// Amount: convertAmount, +// }, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to convert native IBC coin") + +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") + +// addrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(addrTracked).To(BeTrue(), "expected address to be stored") +// }) +// }) +// }) +// }) + +// var _ = Describe("STRv2 Tracking Wevmos-", func() { +// var s *STRv2TrackingSuite + +// BeforeEach(func() { +// var err error +// s, err = CreateTestSuite(utils.TestingChainID + "-1") +// Expect(err).ToNot(HaveOccurred(), "failed to create test suite") + +// // NOTE: this is necessary to enable e.g. erc20Keeper.BalanceOf(...) to work +// // correctly internally. +// // Removing it will break a bunch of tests giving errors like: "failed to retrieve balance" +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") + +// // Deploy WEVMOS contract +// s.wevmosAddr, err = s.factory.DeployContract( +// s.keyring.GetPrivKey(erc20Deployer), +// evmtypes.EvmTxArgs{}, +// testfactory.ContractDeploymentData{Contract: contracts.WEVMOSContract}, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to deploy wevmos contract") +// // Send WEVMOS to account +// _, err = s.factory.ExecuteEthTx( +// s.keyring.GetPrivKey(0), +// evmtypes.EvmTxArgs{ +// To: &s.wevmosAddr, +// Amount: sentWEVMOS.BigInt(), +// // FIXME: the gas simulation is not working correctly - otherwise results in out of gas +// GasLimit: 100_000, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to deposit to wevmos") + +// s.network.App.Erc20Keeper.DeleteSTRv2Address(s.network.GetContext(), s.keyring.GetKey(0).AccAddr) +// s.network.App.Erc20Keeper.DeleteSTRv2Address(s.network.GetContext(), s.keyring.GetKey(2).AccAddr) +// }) + +// When("sending an EVM transaction", func() { +// Context("which interacts with a registered native token pair ERC-20 contract", func() { +// Context("in a direct call to the token pair contract", func() { +// It("should add the from and to addresses to the store if it is not already stored", func() { +// sender := s.keyring.GetKey(0) +// receiver := s.keyring.GetKey(2) + +// senderAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeFalse(), "expected address not to be stored before conversion") +// receiverAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), receiver.AccAddr) +// Expect(receiverAddrTracked).To(BeFalse(), "expected address not to be stored before conversion") + +// _, err := s.factory.ExecuteContractCall( +// sender.Priv, +// evmtypes.EvmTxArgs{ +// To: &s.wevmosAddr, +// GasLimit: 100_000, +// }, +// testfactory.CallArgs{ +// ContractABI: contracts.WEVMOSContract.ABI, +// MethodName: "transfer", +// Args: []interface{}{ +// receiver.Addr, +// transferAmount, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to transfer tokens of Cosmos native ERC-20 token pair") + +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") + +// senderAddrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeTrue(), "expected address to be stored") +// receiverAddrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(receiverAddrTracked).To(BeTrue(), "expected address to be stored") +// }) +// It("should not fail if the addresses are already stored", func() { +// sender := s.keyring.GetKey(0) +// receiver := s.keyring.GetKey(2) + +// senderAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeFalse(), "expected address not to be stored before conversion") +// receiverAddrTracked := s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), receiver.AccAddr) +// Expect(receiverAddrTracked).To(BeFalse(), "expected address not to be stored before conversion") + +// s.network.App.Erc20Keeper.SetSTRv2Address(s.network.GetContext(), s.keyring.GetKey(0).AccAddr) +// s.network.App.Erc20Keeper.SetSTRv2Address(s.network.GetContext(), s.keyring.GetKey(2).AccAddr) + +// _, err := s.factory.ExecuteContractCall( +// sender.Priv, +// evmtypes.EvmTxArgs{ +// To: &s.wevmosAddr, +// }, +// testfactory.CallArgs{ +// ContractABI: contracts.WEVMOSContract.ABI, +// MethodName: "transfer", +// Args: []interface{}{ +// receiver.Addr, +// transferAmount, +// }, +// }, +// ) +// Expect(err).ToNot(HaveOccurred(), "failed to transfer tokens of Cosmos native ERC-20 token pair") + +// Expect(s.network.NextBlock()).To(BeNil(), "failed to advance block") + +// senderAddrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(senderAddrTracked).To(BeTrue(), "expected address to be stored") +// receiverAddrTracked = s.network.App.Erc20Keeper.HasSTRv2Address(s.network.GetContext(), sender.AccAddr) +// Expect(receiverAddrTracked).To(BeTrue(), "expected address to be stored") +// }) +// }) +// }) +// }) +// }) diff --git a/x/erc20/strv2/strv2_integration_utils_test.go b/x/erc20/strv2/strv2_integration_utils_test.go new file mode 100644 index 0000000000..561371e2a8 --- /dev/null +++ b/x/erc20/strv2/strv2_integration_utils_test.go @@ -0,0 +1,210 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package strv2_test + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/ethereum/go-ethereum/common" + "github.com/evmos/evmos/v18/contracts" + commonfactory "github.com/evmos/evmos/v18/testutil/integration/common/factory" + testfactory "github.com/evmos/evmos/v18/testutil/integration/evmos/factory" + "github.com/evmos/evmos/v18/testutil/integration/evmos/grpc" + testkeyring "github.com/evmos/evmos/v18/testutil/integration/evmos/keyring" + testnetwork "github.com/evmos/evmos/v18/testutil/integration/evmos/network" + "github.com/evmos/evmos/v18/utils" + "github.com/evmos/evmos/v18/x/erc20" + erc20types "github.com/evmos/evmos/v18/x/erc20/types" + "github.com/evmos/evmos/v18/x/evm" + evmtypes "github.com/evmos/evmos/v18/x/evm/types" +) + +type GenesisSetup struct { + keyring testkeyring.Keyring + genesisState *testnetwork.CustomGenesisState + + nativeCoinERC20Addr common.Address + registeredERC20Addr common.Address + unregisteredERC20Addr common.Address +} + +// CreateGenesisSetup sets up a genesis state to base the tracking of interactions with +// ERC-20 token pairs on. +// +// NOTE: it sets up another test network and then exports the genesis state to be used in the actual tests, +// which is similar to how real upgrades work, where a previous network state is used to start with the +// new version. +func CreateGenesisSetup(keyring testkeyring.Keyring) (GenesisSetup, error) { + genesisBalances := []banktypes.Balance{ + { + Address: keyring.GetAccAddr(0).String(), + Coins: sdk.NewCoins( + sdk.Coin{Denom: utils.BaseDenom, Amount: testnetwork.PrefundedAccountInitialBalance}, + sdk.Coin{Denom: nativeIBCCoinDenom, Amount: testnetwork.PrefundedAccountInitialBalance}, + ), + }, + { + Address: keyring.GetAccAddr(1).String(), + Coins: sdk.NewCoins( + sdk.Coin{Denom: utils.BaseDenom, Amount: testnetwork.PrefundedAccountInitialBalance}, + sdk.Coin{Denom: nativeIBCCoinDenom, Amount: testnetwork.PrefundedAccountInitialBalance.QuoRaw(2)}, + ), + }, + } + + network := testnetwork.NewUnitTestNetwork( + testnetwork.WithBalances(genesisBalances...), + ) + handler := grpc.NewIntegrationHandler(network) + factory := testfactory.New(network, handler) + + deployer := keyring.GetKey(deployerIdx) + + // ------------------------------------------------------------------ + // Register the native IBC coin + ibcCoinMetaData := banktypes.Metadata{ + Description: "The native IBC coin", + Base: nativeIBCCoinDenom, + Display: nativeIBCCoinDenom, + DenomUnits: []*banktypes.DenomUnit{ + {Denom: nativeIBCCoinDenom, Exponent: 0}, + {Denom: "u" + nativeIBCCoinDenom, Exponent: 6}, + }, + } + + ibcNativeTokenPair, err := network.App.Erc20Keeper.RegisterCoin(network.GetContext(), ibcCoinMetaData) + if err != nil { + return GenesisSetup{}, errorsmod.Wrap(err, "failed to register native IBC coin") + } + nativeCoinERC20Addr := common.HexToAddress(ibcNativeTokenPair.Erc20Address) + + if err := network.NextBlock(); err != nil { + return GenesisSetup{}, errorsmod.Wrap(err, "failed to advance block") + } + + // Convert a balance for the deployer + msgConvertCoin := erc20types.MsgConvertCoin{ + Sender: deployer.AccAddr.String(), + Receiver: deployer.Addr.String(), + Coin: sdk.NewCoin(nativeIBCCoinDenom, convertAmount), + } + _, err = factory.ExecuteCosmosTx(deployer.Priv, commonfactory.CosmosTxArgs{ + Msgs: []sdk.Msg{&msgConvertCoin}, + }) + if err != nil { + return GenesisSetup{}, errorsmod.Wrap(err, "failed to convert a balance") + } + + if err := network.NextBlock(); err != nil { + return GenesisSetup{}, errorsmod.Wrap(err, "failed to advance block") + } + + // ------------------------------------------------------------------ + // Register an ERC-20 token pair + registeredERC20Addr, err := factory.DeployContract( + deployer.Priv, + evmtypes.EvmTxArgs{}, + testfactory.ContractDeploymentData{ + Contract: contracts.ERC20MinterBurnerDecimalsContract, + ConstructorArgs: []interface{}{"TestToken", "TTK", uint8(18)}, + }, + ) + if err != nil { + return GenesisSetup{}, errorsmod.Wrap(err, "failed to deploy ERC-20 contract") + } + + if err := network.NextBlock(); err != nil { + return GenesisSetup{}, errorsmod.Wrap(err, "failed to advance block") + } + + // Mint some tokens for the deployer + _, err = factory.ExecuteContractCall( + deployer.Priv, + evmtypes.EvmTxArgs{ + To: ®isteredERC20Addr, + }, + testfactory.CallArgs{ + ContractABI: contracts.ERC20MinterBurnerDecimalsContract.ABI, + MethodName: "mint", + Args: []interface{}{ + deployer.Addr, + mintAmount, + }, + }, + ) + if err != nil { + return GenesisSetup{}, errorsmod.Wrap(err, "failed to mint ERC-20 tokens") + } + + _, err = network.App.Erc20Keeper.RegisterERC20(network.GetContext(), registeredERC20Addr) + if err != nil { + return GenesisSetup{}, errorsmod.Wrap(err, "failed to register token pair") + } + + if err := network.NextBlock(); err != nil { + return GenesisSetup{}, errorsmod.Wrap(err, "failed to advance block") + } + + // ------------------------------------------------------------------ + // Deploy an unregistered ERC-20 contract + unregisteredERC20Addr, err := factory.DeployContract( + deployer.Priv, + evmtypes.EvmTxArgs{}, + testfactory.ContractDeploymentData{ + Contract: contracts.ERC20MinterBurnerDecimalsContract, + ConstructorArgs: []interface{}{"UnregisteredToken", "URT", uint8(18)}, + }, + ) + if err != nil { + return GenesisSetup{}, errorsmod.Wrap(err, "failed to deploy ERC-20 contract") + } + + if err := network.NextBlock(); err != nil { + return GenesisSetup{}, errorsmod.Wrap(err, "failed to advance block") + } + + // Mint some tokens for the deployer + _, err = factory.ExecuteContractCall( + deployer.Priv, + evmtypes.EvmTxArgs{ + To: &unregisteredERC20Addr, + }, + testfactory.CallArgs{ + ContractABI: contracts.ERC20MinterBurnerDecimalsContract.ABI, + MethodName: "mint", + Args: []interface{}{ + deployer.Addr, + mintAmount, + }, + }, + ) + if err != nil { + return GenesisSetup{}, errorsmod.Wrap(err, "failed to mint ERC-20 tokens") + } + + // ------------------------------------------------------------------ + // Export genesis states + ag := network.App.AccountKeeper.ExportGenesis(network.GetContext()) + bg := network.App.BankKeeper.ExportGenesis(network.GetContext()) + dg := network.App.DistrKeeper.ExportGenesis(network.GetContext()) + eg := evm.ExportGenesis(network.GetContext(), network.App.EvmKeeper, network.App.AccountKeeper) + ercg := erc20.ExportGenesis(network.GetContext(), network.App.Erc20Keeper) + + return GenesisSetup{ + keyring: keyring, + genesisState: &testnetwork.CustomGenesisState{ + authtypes.ModuleName: ag, + banktypes.ModuleName: bg, + distributiontypes.ModuleName: dg, + evmtypes.ModuleName: eg, + erc20types.ModuleName: ercg, + }, + nativeCoinERC20Addr: nativeCoinERC20Addr, + registeredERC20Addr: registeredERC20Addr, + unregisteredERC20Addr: unregisteredERC20Addr, + }, nil +} diff --git a/x/erc20/types/keys.go b/x/erc20/types/keys.go index 6c95614652..edf70d536d 100644 --- a/x/erc20/types/keys.go +++ b/x/erc20/types/keys.go @@ -20,18 +20,19 @@ const ( RouterKey = ModuleName ) -// ModuleAddress is the native module address for EVM +// ModuleAddress is the native module address for ERC-20 var ModuleAddress common.Address func init() { ModuleAddress = common.BytesToAddress(authtypes.NewModuleAddress(ModuleName).Bytes()) } -// prefix bytes for the EVM persistent store +// prefix bytes for the ERC-20 persistent store const ( prefixTokenPair = iota + 1 prefixTokenPairByERC20 prefixTokenPairByDenom + prefixSTRv2Addresses ) // KVStore key prefixes @@ -39,4 +40,5 @@ var ( KeyPrefixTokenPair = []byte{prefixTokenPair} KeyPrefixTokenPairByERC20 = []byte{prefixTokenPairByERC20} KeyPrefixTokenPairByDenom = []byte{prefixTokenPairByDenom} + KeyPrefixSTRv2Addresses = []byte{prefixSTRv2Addresses} ) diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 02a8591f9a..7516b1714b 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -54,6 +54,8 @@ type Keeper struct { // chain ID number obtained from the context's chain id eip155ChainID *big.Int + storage sdk.KVStore + // Tracer used to collect execution traces from the EVM transaction execution tracer string diff --git a/x/evm/keeper/statedb.go b/x/evm/keeper/statedb.go index 24a33bf1bf..e905206a51 100644 --- a/x/evm/keeper/statedb.go +++ b/x/evm/keeper/statedb.go @@ -47,6 +47,15 @@ func (k *Keeper) GetState(ctx sdk.Context, addr common.Address, key common.Hash) return common.BytesToHash(value) } +func (k *Keeper) SetStorageDummy(ctx sdk.Context) { + k.storage = ctx.KVStore(k.storeKey) + // return prefix.NewStore(ctx.KVStore(k.storeKey), types.AddressStoragePrefix(contractAddr)) +} + +func (k *Keeper) GetStoreDummy(ctx sdk.Context, contractAddr common.Address) sdk.KVStore { + return prefix.NewStore(ctx.KVStore(k.storeKey), types.AddressStoragePrefix(contractAddr)) +} + // GetCode loads contract code from database, implements `statedb.Keeper` interface. func (k *Keeper) GetCode(ctx sdk.Context, codeHash common.Hash) []byte { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixCode) diff --git a/x/ibc/transfer/keeper/msg_server_test.go b/x/ibc/transfer/keeper/msg_server_test.go index 56cab0efcb..7b521c8367 100644 --- a/x/ibc/transfer/keeper/msg_server_test.go +++ b/x/ibc/transfer/keeper/msg_server_test.go @@ -7,6 +7,7 @@ import ( "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v7/modules/core/24-host" @@ -234,6 +235,53 @@ func (suite *KeeperTestSuite) TestTransfer() { }, false, }, + + // STRV2 + { + "no-op - pair not registered", + func() *types.MsgTransfer { + senderAcc := sdk.AccAddress(suite.address.Bytes()) + + denom := "test" + coinMetadata := banktypes.Metadata{ + Name: "Generic IBC name", + Symbol: "IBC", + Description: "Generic IBC token description", + DenomUnits: []*banktypes.DenomUnit{ + { + Denom: denom, + Exponent: 0, + Aliases: []string{denom}, + }, + { + Denom: denom, + Exponent: 18, + }, + }, + Display: denom, + Base: denom, + } + + coin := sdk.NewCoin(denom, math.NewInt(10)) + coins := sdk.NewCoins(coin) + + err := suite.app.BankKeeper.MintCoins(suite.ctx, erc20types.ModuleName, coins) + suite.Require().NoError(err) + + err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, erc20types.ModuleName, senderAcc, coins) + suite.Require().NoError(err) + suite.Commit() + + pair, err := suite.app.Erc20Keeper.RegisterCoin(suite.ctx, coinMetadata) + suite.Require().Equal(pair.Denom, denom) + suite.Require().NoError(err) + + transferMsg := types.NewMsgTransfer("transfer", "channel-0", coin, senderAcc.String(), "", timeoutHeight, 0, "") + + return transferMsg + }, + true, + }, } for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() {