diff --git a/app/ante/handler_options_test.go b/app/ante/handler_options_test.go index 2b10f8c6..f0e0df0b 100644 --- a/app/ante/handler_options_test.go +++ b/app/ante/handler_options_test.go @@ -138,7 +138,7 @@ func (suite *AnteTestSuite) TestValidateHandlerOptions() { SignModeHandler: encoding.MakeConfig().TxConfig.SignModeHandler(), SigGasConsumer: evmosante.SigVerificationGasConsumer, MaxTxGasWanted: 40000000, - TxFeeChecker: evmosanteevm.NewDynamicFeeChecker(suite.app.EvmKeeper), + TxFeeChecker: evmosanteevm.NewDynamicFeeChecker(suite.app.FeeMarketKeeper), }, true, }, diff --git a/app/ante/utils_test.go b/app/ante/utils_test.go index d6bb4dcd..25484454 100644 --- a/app/ante/utils_test.go +++ b/app/ante/utils_test.go @@ -34,9 +34,9 @@ import ( "github.com/evmos/os/encoding" "github.com/evmos/os/ethereum/eip712" - tests "github.com/evmos/os/testutil/tx" "github.com/evmos/os/types" evmtypes "github.com/evmos/os/x/evm/types" + tests "github.com/realiotech/realio-network/testutil/tx" feemarkettypes "github.com/evmos/os/x/feemarket/types" @@ -185,7 +185,7 @@ func (suite *AnteTestSuite) BuildTestEthTx( gasTipCap *big.Int, accesses *ethtypes.AccessList, ) *evmtypes.MsgEthereumTx { - chainID := suite.app.EvmKeeper.ChainID() + chainID := evmtypes.GetEthChainConfig().ChainID nonce := suite.app.EvmKeeper.GetNonce( suite.ctx, common.BytesToAddress(from.Bytes()), diff --git a/app/app.go b/app/app.go index 2199b18f..5f29b03f 100644 --- a/app/app.go +++ b/app/app.go @@ -474,7 +474,7 @@ func New( app.EvmKeeper = evmkeeper.NewKeeper( appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], authtypes.NewModuleAddress(govtypes.ModuleName), - app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.FeeMarketKeeper, MockErc20Keeper{}, + app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.FeeMarketKeeper, NewEmptyMockErc20Keeper(), tracer, app.GetSubspace(evmtypes.ModuleName), ) @@ -490,7 +490,6 @@ func New( runtime.NewKVStoreService(keys[assetmoduletypes.StoreKey]), app.BankKeeper, app.AccountKeeper, - app.ModuleAccountAddrs(), authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) @@ -502,9 +501,6 @@ func New( authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) - // Add transfer restriction - app.BankKeeper.AppendSendRestriction(app.AssetKeeper.AssetSendRestriction) - // IBC Keeper app.IBCKeeper = ibckeeper.NewKeeper( appCodec, keys[ibcexported.StoreKey], app.GetSubspace(ibcexported.ModuleName), app.StakingKeeper, app.UpgradeKeeper, scopedIBCKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String(), @@ -801,7 +797,7 @@ func New( FeeMarketKeeper: app.FeeMarketKeeper, MaxTxGasWanted: maxGasWanted, ExtensionOptionChecker: ostypes.HasDynamicFeeExtensionOption, - TxFeeChecker: evmosanteevm.NewDynamicFeeChecker(app.EvmKeeper), + TxFeeChecker: evmosanteevm.NewDynamicFeeChecker(app.FeeMarketKeeper), } if err := options.Validate(); err != nil { diff --git a/app/mock_erc20.go b/app/mock_erc20.go index fdc903e9..c47695d5 100644 --- a/app/mock_erc20.go +++ b/app/mock_erc20.go @@ -2,12 +2,47 @@ package app import ( sdk "github.com/cosmos/cosmos-sdk/types" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" "github.com/ethereum/go-ethereum/common" "github.com/evmos/os/x/evm/core/vm" + transferkeeper "github.com/evmos/os/x/ibc/transfer/keeper" + "github.com/realiotech/realio-network/precompiles/erc20" + assetkeeper "github.com/realiotech/realio-network/x/asset/keeper" ) -type MockErc20Keeper struct{} +type MockErc20Keeper struct { + ContractAddrs []string -func (k MockErc20Keeper) GetERC20PrecompileInstance(_ sdk.Context, _ common.Address) (contract vm.PrecompiledContract, found bool, err error) { - return nil, false, nil + bankKeeper bankkeeper.Keeper + authzKeeper authzkeeper.Keeper + transferKeeper *transferkeeper.Keeper + assetKeeper assetkeeper.Keeper +} + +func NewMockErc20KeeperWithAddrs(addrs []string) MockErc20Keeper { + return MockErc20Keeper{ContractAddrs: addrs} +} + +func NewEmptyMockErc20Keeper() MockErc20Keeper { + return MockErc20Keeper{} +} + +func (k MockErc20Keeper) GetERC20PrecompileInstance(ctx sdk.Context, addr common.Address) (contract vm.PrecompiledContract, found bool, err error) { + // Check if contract address in list + exist, denom, err := k.assetKeeper.EVMContractExist(ctx, addr) + if err != nil || !exist { + return nil, false, nil + } + + precompile, err := erc20.NewPrecompile(denom, addr, k.bankKeeper, k.authzKeeper, *k.transferKeeper, k.assetKeeper) + if err != nil { + return nil, false, nil + } + + return precompile, true, nil +} + +func (k MockErc20Keeper) AddContractAddress(_ sdk.Context, addr common.Address) { + k.ContractAddrs = append(k.ContractAddrs, addr.String()) } diff --git a/example_chain/Makefile b/example_chain/Makefile new file mode 100644 index 00000000..3c279737 --- /dev/null +++ b/example_chain/Makefile @@ -0,0 +1,103 @@ +#!/usr/bin/make -f + +VERSION ?= $(shell echo $(shell git describe --tags --always) | sed 's/^v//') +TMVERSION := $(shell go list -m github.com/cometbft/cometbft | sed 's:.* ::') +COMMIT := $(shell git log -1 --format='%H') +BINDIR ?= $(GOPATH)/bin +EXAMPLE_BINARY = osd +BUILDDIR ?= $(CURDIR)/build + +export GO111MODULE = on + +# Default target executed when no arguments are given to make. +default_target: all + +.PHONY: default_target + +# process build tags +build_tags = netgo + +ifeq (cleveldb,$(findstring cleveldb,$(COSMOS_BUILD_OPTIONS))) + build_tags += gcc +endif +build_tags += $(BUILD_TAGS) +build_tags := $(strip $(build_tags)) + +# process linker flags + +ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=os \ + -X github.com/cosmos/cosmos-sdk/version.AppName=$(EXAMPLE_BINARY) \ + -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \ + -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) \ + -X github.com/cometbft/cometbft/version.TMCoreSemVer=$(TMVERSION) + +# DB backend selection +ifeq (cleveldb,$(findstring cleveldb,$(COSMOS_BUILD_OPTIONS))) + ldflags += -X github.com/cosmos/cosmos-sdk/types.DBBackend=cleveldb +endif + +# add build tags to linker flags +whitespace := $(subst ,, ) +comma := , +build_tags_comma_sep := $(subst $(whitespace),$(comma),$(build_tags)) +ldflags += -X "github.com/cosmos/cosmos-sdk/version.BuildTags=$(build_tags_comma_sep)" + +ifeq (,$(findstring nostrip,$(COSMOS_BUILD_OPTIONS))) + ldflags += -w -s +endif +ldflags += $(LDFLAGS) +ldflags := $(strip $(ldflags)) + +BUILD_FLAGS := -tags "$(build_tags)" -ldflags '$(ldflags)' +# check for nostrip option +ifeq (,$(findstring nostrip,$(COSMOS_BUILD_OPTIONS))) + BUILD_FLAGS += -trimpath +endif + +# check if no optimization option is passed +# used for remote debugging +ifneq (,$(findstring nooptimization,$(COSMOS_BUILD_OPTIONS))) + BUILD_FLAGS += -gcflags "all=-N -l" +endif + +# # The below include contains the tools and runsim targets. +# include contrib/devtools/Makefile + +############################################################################### +### Build ### +############################################################################### + +BUILD_TARGETS := build install + +build: BUILD_ARGS=-o $(BUILDDIR)/ +build-linux: + GOOS=linux GOARCH=amd64 $(MAKE) build + +$(BUILD_TARGETS): go.sum $(BUILDDIR)/ + CGO_ENABLED="1" go $@ $(BUILD_FLAGS) $(BUILD_ARGS) ./... + +$(BUILDDIR)/: + mkdir -p $(BUILDDIR)/ + +distclean: clean tools-clean + +clean: + rm -rf \ + $(BUILDDIR)/ \ + artifacts/ \ + tmp-swagger-gen/ + +all: build + +build-all: build lint test vulncheck + +.PHONY: distclean clean build-all + +############################################################################### +### Tools & Dependencies ### +############################################################################### + +go.sum: go.mod + echo "Ensure dependencies have not been modified ..." >&2 + go mod verify + go mod tidy diff --git a/example_chain/README.md b/example_chain/README.md new file mode 100644 index 00000000..42fa0cc1 --- /dev/null +++ b/example_chain/README.md @@ -0,0 +1,58 @@ +# Example evmOS Chain + +This directory contains an example chain that uses the evmOS modules. +It is based on the simapp implementation on the Cosmos SDK repository, +which itself is a simplified version of a Cosmos SDK-based blockchain. + +This chain implementation is used to demonstrate the integration of evmOS +as well as to provide a chain object for testing purposes within the repository. + +## Config + +By default, this chain has the following configuration: + +| Option | Value | +|---------------------|------------------------| +| Binary | `osd` | +| Chain ID | `os_9005-1` | +| Custom Opcodes | - | +| Default Token Pairs | 1 for the native token | +| Denomination | `aevmos` | +| EVM flavor | permissionless | +| Enabled Precompiles | all | + +## Running The Chain + +To run the example, execute the local node script found within this repository: + +```bash +./local_node.sh [FLAGS] +``` + +Available flags are: + +- `-y`: Overwrite previous database +- `-n`: Do **not** overwrite previous database +- `--no-install`: Skip installation of the binary +- `--remote-debugging`: Build a binary suitable for remote debugging + +## Available Cosmos SDK Modules + +As mentioned above, this exemplary chain implementation is a reduced version of `simapp`. +Specifically, instead of offering access to all Cosmos SDK modules, it just includes the following: + +- `auth` +- `authz` +- `bank` +- `capability` +- `consensus` +- `distribution` +- `evidence` +- `feegrant` +- `genutil` +- `gov` +- `mint` +- `params` +- `slashing` +- `staking` +- `upgrade` diff --git a/example_chain/app.go b/example_chain/app.go new file mode 100644 index 00000000..02828b4a --- /dev/null +++ b/example_chain/app.go @@ -0,0 +1,1135 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package example_chain + +import ( + "encoding/json" + "fmt" + "io" + "maps" + "os" + "sort" + + autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" + reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" + "cosmossdk.io/client/v2/autocli" + clienthelpers "cosmossdk.io/client/v2/helpers" + "cosmossdk.io/core/appmodule" + "cosmossdk.io/log" + storetypes "cosmossdk.io/store/types" + "cosmossdk.io/x/evidence" + evidencekeeper "cosmossdk.io/x/evidence/keeper" + evidencetypes "cosmossdk.io/x/evidence/types" + "cosmossdk.io/x/feegrant" + feegrantkeeper "cosmossdk.io/x/feegrant/keeper" + feegrantmodule "cosmossdk.io/x/feegrant/module" + "cosmossdk.io/x/upgrade" + upgradekeeper "cosmossdk.io/x/upgrade/keeper" + upgradetypes "cosmossdk.io/x/upgrade/types" + abci "github.com/cometbft/cometbft/abci/types" + dbm "github.com/cosmos/cosmos-db" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/grpc/cmtservice" + "github.com/cosmos/cosmos-sdk/client/grpc/node" + nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/runtime" + runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/api" + "github.com/cosmos/cosmos-sdk/server/config" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + testdata_pulsar "github.com/cosmos/cosmos-sdk/testutil/testdata/testpb" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/types/msgservice" + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/auth" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/posthandler" + authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + txmodule "github.com/cosmos/cosmos-sdk/x/auth/tx/config" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/authz" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module" + "github.com/cosmos/cosmos-sdk/x/bank" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/consensus" + consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" + consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/cosmos/cosmos-sdk/x/gov" + govclient "github.com/cosmos/cosmos-sdk/x/gov/client" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/mint" + mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/cosmos/cosmos-sdk/x/params" + paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" + paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/cosmos/cosmos-sdk/x/slashing" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + "github.com/cosmos/cosmos-sdk/x/staking" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/gogoproto/proto" + "github.com/cosmos/ibc-go/modules/capability" + capabilitykeeper "github.com/cosmos/ibc-go/modules/capability/keeper" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + ibctransfer "github.com/cosmos/ibc-go/v8/modules/apps/transfer" + ibctransfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + ibc "github.com/cosmos/ibc-go/v8/modules/core" + ibcclienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + ibcconnectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" + porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" + ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" + ibckeeper "github.com/cosmos/ibc-go/v8/modules/core/keeper" + ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" + ibctestingtypes "github.com/cosmos/ibc-go/v8/testing/types" + evmosante "github.com/evmos/os/ante" + evmosevmante "github.com/evmos/os/ante/evm" + evmosencoding "github.com/evmos/os/encoding" + srvflags "github.com/evmos/os/server/flags" + evmostypes "github.com/evmos/os/types" + evmosutils "github.com/evmos/os/utils" + "github.com/evmos/os/x/erc20" + erc20keeper "github.com/evmos/os/x/erc20/keeper" + erc20types "github.com/evmos/os/x/erc20/types" + "github.com/evmos/os/x/evm" + "github.com/evmos/os/x/evm/core/vm" + evmkeeper "github.com/evmos/os/x/evm/keeper" + evmtypes "github.com/evmos/os/x/evm/types" + "github.com/evmos/os/x/feemarket" + feemarketkeeper "github.com/evmos/os/x/feemarket/keeper" + feemarkettypes "github.com/evmos/os/x/feemarket/types" + chainante "github.com/evmos/os/example_chain/ante" + "github.com/spf13/cast" + + // NOTE: override ICS20 keeper to support IBC transfers of ERC20 tokens + "github.com/evmos/os/x/ibc/transfer" + transferkeeper "github.com/evmos/os/x/ibc/transfer/keeper" + + // Force-load the tracer engines to trigger registration due to Go-Ethereum v1.10.15 changes + _ "github.com/evmos/os/x/evm/core/tracers/js" + _ "github.com/evmos/os/x/evm/core/tracers/native" + assetkeeper "github.com/realiotech/realio-network/x/asset/keeper" + assettypes "github.com/realiotech/realio-network/x/asset/types" + "github.com/realiotech/realio-network/x/asset" +) + +func init() { + // manually update the power reduction by replacing micro (u) -> atto (a) evmos + sdk.DefaultPowerReduction = evmostypes.AttoPowerReduction + + // get the user's home directory + var err error + DefaultNodeHome, err = clienthelpers.GetNodeHomeDirectory(".osd") + if err != nil { + panic(err) + } +} + +const appName = "os" + +var ( + // DefaultNodeHome default home directories for the application daemon + DefaultNodeHome string + + // module account permissions + maccPerms = map[string][]string{ + authtypes.FeeCollectorName: nil, + distrtypes.ModuleName: nil, + ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + minttypes.ModuleName: {authtypes.Minter}, + stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, + stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, + govtypes.ModuleName: {authtypes.Burner}, + + // evmOS modules + evmtypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + feemarkettypes.ModuleName: nil, + erc20types.ModuleName: {authtypes.Minter, authtypes.Burner}, + assettypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + } +) + +var ( + _ runtime.AppI = (*ExampleChain)(nil) + _ servertypes.Application = (*ExampleChain)(nil) +) + +// ExampleChain extends an ABCI application, but with most of its parameters exported. +// They are exported for convenience in creating helper functions, as object +// capabilities aren't needed for testing. +type ExampleChain struct { + *baseapp.BaseApp + + legacyAmino *codec.LegacyAmino + appCodec codec.Codec + interfaceRegistry types.InterfaceRegistry + txConfig client.TxConfig + + // keys to access the substores + keys map[string]*storetypes.KVStoreKey + tkeys map[string]*storetypes.TransientStoreKey + memKeys map[string]*storetypes.MemoryStoreKey + + // keepers + AccountKeeper authkeeper.AccountKeeper + BankKeeper bankkeeper.Keeper + CapabilityKeeper *capabilitykeeper.Keeper + StakingKeeper *stakingkeeper.Keeper + SlashingKeeper slashingkeeper.Keeper + MintKeeper mintkeeper.Keeper + DistrKeeper distrkeeper.Keeper + GovKeeper govkeeper.Keeper + UpgradeKeeper *upgradekeeper.Keeper + ParamsKeeper paramskeeper.Keeper + AuthzKeeper authzkeeper.Keeper + EvidenceKeeper evidencekeeper.Keeper + FeeGrantKeeper feegrantkeeper.Keeper + ConsensusParamsKeeper consensusparamkeeper.Keeper + + // IBC keepers + IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly + TransferKeeper transferkeeper.Keeper + scopedIBCKeeper capabilitykeeper.ScopedKeeper + ScopedTransferKeeper capabilitykeeper.ScopedKeeper + + // evmOS keepers + FeeMarketKeeper feemarketkeeper.Keeper + EVMKeeper *evmkeeper.Keeper + Erc20Keeper erc20keeper.Keeper + AssetKeeper assetkeeper.Keeper + + // the module manager + ModuleManager *module.Manager + BasicModuleManager module.BasicManager + + // simulation manager + sm *module.SimulationManager + + // module configurator + configurator module.Configurator +} + +// NewExampleApp returns a reference to an initialized ExampleChain. +func NewExampleApp( + logger log.Logger, + db dbm.DB, + traceStore io.Writer, + loadLatest bool, + appOpts servertypes.AppOptions, + evmosAppOptions EvmosOptionsFn, + baseAppOptions ...func(*baseapp.BaseApp), +) *ExampleChain { + encodingConfig := evmosencoding.MakeConfig() + + appCodec := encodingConfig.Codec + legacyAmino := encodingConfig.Amino + interfaceRegistry := encodingConfig.InterfaceRegistry + txConfig := encodingConfig.TxConfig + + // Below we could construct and set an application specific mempool and + // ABCI 1.0 PrepareProposal and ProcessProposal handlers. These defaults are + // already set in the SDK's BaseApp, this shows an example of how to override + // them. + // + // Example: + // + // bApp := baseapp.NewBaseApp(...) + // nonceMempool := mempool.NewSenderNonceMempool() + // abciPropHandler := NewDefaultProposalHandler(nonceMempool, bApp) + // + // bApp.SetMempool(nonceMempool) + // bApp.SetPrepareProposal(abciPropHandler.PrepareProposalHandler()) + // bApp.SetProcessProposal(abciPropHandler.ProcessProposalHandler()) + // + // Alternatively, you can construct BaseApp options, append those to + // baseAppOptions and pass them to NewBaseApp. + // + // Example: + // + // prepareOpt = func(app *baseapp.BaseApp) { + // abciPropHandler := baseapp.NewDefaultProposalHandler(nonceMempool, app) + // app.SetPrepareProposal(abciPropHandler.PrepareProposalHandler()) + // } + // baseAppOptions = append(baseAppOptions, prepareOpt) + + bApp := baseapp.NewBaseApp( + appName, + logger, + db, + // use transaction decoder to support the sdk.Tx interface instead of sdk.StdTx + encodingConfig.TxConfig.TxDecoder(), + baseAppOptions..., + ) + bApp.SetCommitMultiStoreTracer(traceStore) + bApp.SetVersion(version.Version) + bApp.SetInterfaceRegistry(interfaceRegistry) + bApp.SetTxEncoder(txConfig.TxEncoder()) + + // initialize the evmOS application configuration + if err := evmosAppOptions(bApp.ChainID()); err != nil { + panic(err) + } + + keys := storetypes.NewKVStoreKeys( + authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, + minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, + govtypes.StoreKey, paramstypes.StoreKey, consensusparamtypes.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey, + evidencetypes.StoreKey, capabilitytypes.StoreKey, + authzkeeper.StoreKey, + // ibc keys + ibcexported.StoreKey, ibctransfertypes.StoreKey, + // evmOS store keys + evmtypes.StoreKey, feemarkettypes.StoreKey, erc20types.StoreKey, + // asset store + assettypes.StoreKey, + ) + + tkeys := storetypes.NewTransientStoreKeys(paramstypes.TStoreKey, evmtypes.TransientKey, feemarkettypes.TransientKey) + memKeys := storetypes.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) + + // load state streaming if enabled + if err := bApp.RegisterStreamingServices(appOpts, keys); err != nil { + fmt.Printf("failed to load state streaming: %s", err) + os.Exit(1) + } + + // wire up the versiondb's `StreamingService` and `MultiStore`. + if cast.ToBool(appOpts.Get("versiondb.enable")) { + panic("version db not supported in this example chain") + } + + app := &ExampleChain{ + BaseApp: bApp, + legacyAmino: legacyAmino, + appCodec: appCodec, + txConfig: txConfig, + interfaceRegistry: interfaceRegistry, + keys: keys, + tkeys: tkeys, + memKeys: memKeys, + } + + app.ParamsKeeper = initParamsKeeper(appCodec, legacyAmino, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey]) + + // get authority address + authAddr := authtypes.NewModuleAddress(govtypes.ModuleName).String() + + // set the BaseApp's parameter store + app.ConsensusParamsKeeper = consensusparamkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[consensusparamtypes.StoreKey]), + authAddr, + runtime.EventService{}, + ) + bApp.SetParamStore(app.ConsensusParamsKeeper.ParamsStore) + + app.CapabilityKeeper = capabilitykeeper.NewKeeper(appCodec, keys[capabilitytypes.StoreKey], memKeys[capabilitytypes.MemStoreKey]) + + app.scopedIBCKeeper = app.CapabilityKeeper.ScopeToModule(ibcexported.ModuleName) + app.ScopedTransferKeeper = app.CapabilityKeeper.ScopeToModule(ibctransfertypes.ModuleName) + + // Applications that wish to enforce statically created ScopedKeepers should call `Seal` after creating + // their scoped modules in `NewApp` with `ScopeToModule` + app.CapabilityKeeper.Seal() + + // add keepers + app.AccountKeeper = authkeeper.NewAccountKeeper( + appCodec, runtime.NewKVStoreService(keys[authtypes.StoreKey]), + authtypes.ProtoBaseAccount, maccPerms, + authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()), + sdk.GetConfig().GetBech32AccountAddrPrefix(), + authAddr, + ) + + app.BankKeeper = bankkeeper.NewBaseKeeper( + appCodec, + runtime.NewKVStoreService(keys[banktypes.StoreKey]), + app.AccountKeeper, + BlockedAddresses(), + authAddr, + logger, + ) + + // optional: enable sign mode textual by overwriting the default tx config (after setting the bank keeper) + enabledSignModes := append(authtx.DefaultSignModes, signingtypes.SignMode_SIGN_MODE_TEXTUAL) //nolint:gocritic + txConfigOpts := authtx.ConfigOptions{ + EnabledSignModes: enabledSignModes, + TextualCoinMetadataQueryFn: txmodule.NewBankKeeperCoinMetadataQueryFn(app.BankKeeper), + } + txConfig, err := authtx.NewTxConfigWithOptions( + appCodec, + txConfigOpts, + ) + if err != nil { + panic(err) + } + app.txConfig = txConfig + + app.StakingKeeper = stakingkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[stakingtypes.StoreKey]), + app.AccountKeeper, + app.BankKeeper, + authAddr, + authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()), + authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()), + ) + + app.MintKeeper = mintkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[minttypes.StoreKey]), + app.StakingKeeper, + app.AccountKeeper, + app.BankKeeper, + authtypes.FeeCollectorName, + authAddr, + ) + + app.DistrKeeper = distrkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[distrtypes.StoreKey]), + app.AccountKeeper, + app.BankKeeper, + app.StakingKeeper, + authtypes.FeeCollectorName, + authAddr, + ) + + app.SlashingKeeper = slashingkeeper.NewKeeper( + appCodec, + app.LegacyAmino(), + runtime.NewKVStoreService(keys[slashingtypes.StoreKey]), + app.StakingKeeper, + authAddr, + ) + + app.FeeGrantKeeper = feegrantkeeper.NewKeeper(appCodec, runtime.NewKVStoreService(keys[feegrant.StoreKey]), app.AccountKeeper) + + // register the staking hooks + // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks + app.StakingKeeper.SetHooks( + stakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks()), + ) + + app.AuthzKeeper = authzkeeper.NewKeeper( + runtime.NewKVStoreService(keys[authzkeeper.StoreKey]), + appCodec, + app.MsgServiceRouter(), + app.AccountKeeper, + ) + + // get skipUpgradeHeights from the app options + skipUpgradeHeights := map[int64]bool{} + for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) { + skipUpgradeHeights[int64(h)] = true + } + homePath := cast.ToString(appOpts.Get(flags.FlagHome)) + // set the governance module account as the authority for conducting upgrades + app.UpgradeKeeper = upgradekeeper.NewKeeper( + skipUpgradeHeights, + runtime.NewKVStoreService(keys[upgradetypes.StoreKey]), + appCodec, + homePath, + app.BaseApp, + authAddr, + ) + + // Create IBC Keeper + app.IBCKeeper = ibckeeper.NewKeeper( + appCodec, + keys[ibcexported.StoreKey], + app.GetSubspace(ibcexported.ModuleName), + app.StakingKeeper, + app.UpgradeKeeper, + app.scopedIBCKeeper, + authAddr, + ) + + govConfig := govtypes.DefaultConfig() + /* + Example of setting gov params: + govConfig.MaxMetadataLen = 10000 + */ + govKeeper := govkeeper.NewKeeper( + appCodec, runtime.NewKVStoreService(keys[govtypes.StoreKey]), app.AccountKeeper, app.BankKeeper, + app.StakingKeeper, app.DistrKeeper, app.MsgServiceRouter(), govConfig, authAddr, + ) + + app.GovKeeper = *govKeeper.SetHooks( + govtypes.NewMultiGovHooks( + // register the governance hooks + ), + ) + + // create evidence keeper with router + evidenceKeeper := evidencekeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[evidencetypes.StoreKey]), + app.StakingKeeper, + app.SlashingKeeper, + app.AccountKeeper.AddressCodec(), + runtime.ProvideCometInfoService(), + ) + // If evidence needs to be handled for the app, set routes in router here and seal + app.EvidenceKeeper = *evidenceKeeper + + // evmOS keepers + app.FeeMarketKeeper = feemarketkeeper.NewKeeper( + appCodec, authtypes.NewModuleAddress(govtypes.ModuleName), + keys[feemarkettypes.StoreKey], + tkeys[feemarkettypes.TransientKey], + app.GetSubspace(feemarkettypes.ModuleName), + ) + + // Set up EVM keeper + tracer := cast.ToString(appOpts.Get(srvflags.EVMTracer)) + + // NOTE: it's required to set up the EVM keeper before the ERC-20 keeper, because it is used in its instantiation. + app.EVMKeeper = evmkeeper.NewKeeper( + // TODO: check why this is not adjusted to use the runtime module methods like SDK native keepers + appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], + authtypes.NewModuleAddress(govtypes.ModuleName), + app.AccountKeeper, + app.BankKeeper, + app.StakingKeeper, + app.FeeMarketKeeper, + &app.Erc20Keeper, + tracer, app.GetSubspace(evmtypes.ModuleName), + ) + + app.Erc20Keeper = erc20keeper.NewKeeper( + keys[erc20types.StoreKey], + appCodec, + authtypes.NewModuleAddress(govtypes.ModuleName), + app.AccountKeeper, + app.BankKeeper, + app.EVMKeeper, + app.StakingKeeper, + app.AuthzKeeper, + &app.TransferKeeper, + ) + + // instantiate IBC transfer keeper AFTER the ERC-20 keeper to use it in the instantiation + app.TransferKeeper = transferkeeper.NewKeeper( + appCodec, keys[ibctransfertypes.StoreKey], app.GetSubspace(ibctransfertypes.ModuleName), + nil, // we are passing no ics4 wrapper + app.IBCKeeper.ChannelKeeper, app.IBCKeeper.PortKeeper, + app.AccountKeeper, app.BankKeeper, app.ScopedTransferKeeper, + app.Erc20Keeper, // Add ERC20 Keeper for ERC20 transfers + authAddr, + ) + + app.AssetKeeper = *assetkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[assettypes.StoreKey]), + app.BankKeeper, + app.AccountKeeper, + authAddr, + ) + + // Override the ICS20 app module + transferModule := transfer.NewAppModule(app.TransferKeeper) + + /* + Create Transfer Stack + + transfer stack contains (from bottom to top): + - ERC-20 Middleware + - IBC Transfer + + SendPacket, since it is originating from the application to core IBC: + transferKeeper.SendPacket -> erc20.SendPacket -> channel.SendPacket + + RecvPacket, message that originates from core IBC and goes down to app, the flow is the other way + channel.RecvPacket -> erc20.OnRecvPacket -> transfer.OnRecvPacket + */ + + // create IBC module from top to bottom of stack + var transferStack porttypes.IBCModule + + transferStack = transfer.NewIBCModule(app.TransferKeeper) + transferStack = erc20.NewIBCMiddleware(app.Erc20Keeper, transferStack) + + // Create static IBC router, add transfer route, then set and seal it + ibcRouter := porttypes.NewRouter() + ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferStack) + + app.IBCKeeper.SetRouter(ibcRouter) + + // NOTE: we are adding all available evmOS EVM extensions. + // Not all of them need to be enabled, which can be configured on a per-chain basis. + app.EVMKeeper.WithStaticPrecompiles( + NewAvailableStaticPrecompiles( + *app.StakingKeeper, + app.DistrKeeper, + app.BankKeeper, + app.Erc20Keeper, + app.AuthzKeeper, + app.TransferKeeper, + app.IBCKeeper.ChannelKeeper, + app.EVMKeeper, + app.GovKeeper, + app.SlashingKeeper, + app.EvidenceKeeper, + ), + ) + + /**** Module Options ****/ + + // NOTE: Any module instantiated in the module manager that is later modified + // must be passed by reference here. + app.ModuleManager = module.NewManager( + genutil.NewAppModule( + app.AccountKeeper, app.StakingKeeper, + app, app.txConfig, + ), + auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), + bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper, app.GetSubspace(banktypes.ModuleName)), + capability.NewAppModule(appCodec, *app.CapabilityKeeper, false), + feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), + gov.NewAppModule(appCodec, &app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(govtypes.ModuleName)), + mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, nil, app.GetSubspace(minttypes.ModuleName)), + slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(slashingtypes.ModuleName), app.interfaceRegistry), + distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(distrtypes.ModuleName)), + staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), + upgrade.NewAppModule(app.UpgradeKeeper, app.AccountKeeper.AddressCodec()), + evidence.NewAppModule(app.EvidenceKeeper), + params.NewAppModule(app.ParamsKeeper), + authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), + consensus.NewAppModule(appCodec, app.ConsensusParamsKeeper), + // IBC modules + ibc.NewAppModule(app.IBCKeeper), + ibctm.NewAppModule(), + transferModule, + // evmOS modules + evm.NewAppModule(app.EVMKeeper, app.AccountKeeper, app.GetSubspace(evmtypes.ModuleName)), + feemarket.NewAppModule(app.FeeMarketKeeper, app.GetSubspace(feemarkettypes.ModuleName)), + erc20.NewAppModule(app.Erc20Keeper, app.AccountKeeper, app.GetSubspace(erc20types.ModuleName)), + asset.NewAppModule(appCodec, app.AssetKeeper, app.BankKeeper, app.GetSubspace(assettypes.ModuleName)), + ) + + // BasicModuleManager defines the module BasicManager which is in charge of setting up basic, + // non-dependant module elements, such as codec registration and genesis verification. + // By default, it is composed of all the modules from the module manager. + // Additionally, app module basics can be overwritten by passing them as an argument. + app.BasicModuleManager = module.NewBasicManagerFromManager( + app.ModuleManager, + map[string]module.AppModuleBasic{ + genutiltypes.ModuleName: genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator), + stakingtypes.ModuleName: staking.AppModuleBasic{}, + govtypes.ModuleName: gov.NewAppModuleBasic( + []govclient.ProposalHandler{ + paramsclient.ProposalHandler, + }, + ), + ibctransfertypes.ModuleName: transfer.AppModuleBasic{AppModuleBasic: &ibctransfer.AppModuleBasic{}}, + }, + ) + app.BasicModuleManager.RegisterLegacyAminoCodec(legacyAmino) + app.BasicModuleManager.RegisterInterfaces(interfaceRegistry) + + // NOTE: upgrade module is required to be prioritized + app.ModuleManager.SetOrderPreBlockers( + upgradetypes.ModuleName, + ) + + // During begin block slashing happens after distr.BeginBlocker so that + // there is nothing left over in the validator fee pool, so as to keep the + // CanWithdrawInvariant invariant. + // + // NOTE: staking module is required if HistoricalEntries param > 0 + // NOTE: capability module's beginblocker must come before any modules using capabilities (e.g. IBC) + app.ModuleManager.SetOrderBeginBlockers( + capabilitytypes.ModuleName, minttypes.ModuleName, + + // IBC modules + ibcexported.ModuleName, ibctransfertypes.ModuleName, + + // evmOS BeginBlockers + erc20types.ModuleName, feemarkettypes.ModuleName, + evmtypes.ModuleName, // NOTE: EVM BeginBlocker must come after FeeMarket BeginBlocker + + // TODO: remove no-ops? check if all are no-ops before removing + distrtypes.ModuleName, slashingtypes.ModuleName, + evidencetypes.ModuleName, stakingtypes.ModuleName, + authtypes.ModuleName, banktypes.ModuleName, govtypes.ModuleName, genutiltypes.ModuleName, + authz.ModuleName, feegrant.ModuleName, + paramstypes.ModuleName, consensusparamtypes.ModuleName, + ) + + // NOTE: the feemarket module should go last in order of end blockers that are actually doing something, + // to get the full block gas used. + app.ModuleManager.SetOrderEndBlockers( + govtypes.ModuleName, stakingtypes.ModuleName, + capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, + + // evmOS EndBlockers + evmtypes.ModuleName, erc20types.ModuleName, feemarkettypes.ModuleName, + + // no-ops + ibcexported.ModuleName, ibctransfertypes.ModuleName, + distrtypes.ModuleName, + slashingtypes.ModuleName, minttypes.ModuleName, + genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, + feegrant.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, consensusparamtypes.ModuleName, + ) + + // NOTE: The genutils module must occur after staking so that pools are + // properly initialized with tokens from genesis accounts. + // NOTE: The genutils module must also occur after auth so that it can access the params from auth. + // NOTE: Capability module must occur first so that it can initialize any capabilities + // so that other modules that want to create or claim capabilities afterwards in InitChain + // can do so safely. + genesisModuleOrder := []string{ + capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, + distrtypes.ModuleName, stakingtypes.ModuleName, slashingtypes.ModuleName, govtypes.ModuleName, + minttypes.ModuleName, + ibcexported.ModuleName, + + // evmOS modules + // + // NOTE: feemarket module needs to be initialized before genutil module: + // gentx transactions use MinGasPriceDecorator.AnteHandle + evmtypes.ModuleName, + feemarkettypes.ModuleName, + erc20types.ModuleName, + + ibctransfertypes.ModuleName, + genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, + feegrant.ModuleName, upgradetypes.ModuleName, + assettypes.ModuleName, + } + app.ModuleManager.SetOrderInitGenesis(genesisModuleOrder...) + app.ModuleManager.SetOrderExportGenesis(genesisModuleOrder...) + + // Uncomment if you want to set a custom migration order here. + // app.ModuleManager.SetOrderMigrations(custom order) + + app.configurator = module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter()) + if err = app.ModuleManager.RegisterServices(app.configurator); err != nil { + panic(fmt.Sprintf("failed to register services in module manager: %s", err.Error())) + } + + // RegisterUpgradeHandlers is used for registering any on-chain upgrades. + // Make sure it's called after `app.ModuleManager` and `app.configurator` are set. + app.RegisterUpgradeHandlers() + + autocliv1.RegisterQueryServer(app.GRPCQueryRouter(), runtimeservices.NewAutoCLIQueryService(app.ModuleManager.Modules)) + + reflectionSvc, err := runtimeservices.NewReflectionService() + if err != nil { + panic(err) + } + reflectionv1.RegisterReflectionServiceServer(app.GRPCQueryRouter(), reflectionSvc) + + // add test gRPC service for testing gRPC queries in isolation + testdata_pulsar.RegisterQueryServer(app.GRPCQueryRouter(), testdata_pulsar.QueryImpl{}) + + // create the simulation manager and define the order of the modules for deterministic simulations + // + // NOTE: this is not required apps that don't use the simulator for fuzz testing + // transactions + overrideModules := map[string]module.AppModuleSimulation{ + authtypes.ModuleName: auth.NewAppModule(app.appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), + } + app.sm = module.NewSimulationManagerFromAppModules(app.ModuleManager.Modules, overrideModules) + + app.sm.RegisterStoreDecoders() + + // initialize stores + app.MountKVStores(keys) + app.MountTransientStores(tkeys) + app.MountMemoryStores(memKeys) + + maxGasWanted := cast.ToUint64(appOpts.Get(srvflags.EVMMaxTxGasWanted)) + + // initialize BaseApp + app.SetInitChainer(app.InitChainer) + app.SetPreBlocker(app.PreBlocker) + app.SetBeginBlocker(app.BeginBlocker) + app.SetEndBlocker(app.EndBlocker) + + app.setAnteHandler(app.txConfig, maxGasWanted) + + // In v0.46, the SDK introduces _postHandlers_. PostHandlers are like + // antehandlers, but are run _after_ the `runMsgs` execution. They are also + // defined as a chain, and have the same signature as antehandlers. + // + // In baseapp, postHandlers are run in the same store branch as `runMsgs`, + // meaning that both `runMsgs` and `postHandler` state will be committed if + // both are successful, and both will be reverted if any of the two fails. + // + // The SDK exposes a default postHandlers chain, which comprises of only + // one decorator: the Transaction Tips decorator. However, some chains do + // not need it by default, so feel free to comment the next line if you do + // not need tips. + // To read more about tips: + // https://docs.cosmos.network/main/core/tips.html + // + // Please note that changing any of the anteHandler or postHandler chain is + // likely to be a state-machine breaking change, which needs a coordinated + // upgrade. + app.setPostHandler() + + // At startup, after all modules have been registered, check that all prot + // annotations are correct. + protoFiles, err := proto.MergedRegistry() + if err != nil { + panic(err) + } + err = msgservice.ValidateProtoAnnotations(protoFiles) + if err != nil { + // TODO: Once we switch to using protoreflect-based antehandlers, we might + // want to panic here instead of logging a warning. + fmt.Fprintln(os.Stderr, err.Error()) + } + + if loadLatest { + if err := app.LoadLatestVersion(); err != nil { + logger.Error("error on loading last version", "err", err) + os.Exit(1) + } + } + + return app +} + +func (app *ExampleChain) setAnteHandler(txConfig client.TxConfig, maxGasWanted uint64) { + options := chainante.HandlerOptions{ + Cdc: app.appCodec, + AccountKeeper: app.AccountKeeper, + BankKeeper: app.BankKeeper, + ExtensionOptionChecker: evmostypes.HasDynamicFeeExtensionOption, + EvmKeeper: app.EVMKeeper, + FeegrantKeeper: app.FeeGrantKeeper, + IBCKeeper: app.IBCKeeper, + FeeMarketKeeper: app.FeeMarketKeeper, + SignModeHandler: txConfig.SignModeHandler(), + SigGasConsumer: evmosante.SigVerificationGasConsumer, + MaxTxGasWanted: maxGasWanted, + TxFeeChecker: evmosevmante.NewDynamicFeeChecker(app.FeeMarketKeeper), + } + if err := options.Validate(); err != nil { + panic(err) + } + + app.SetAnteHandler(chainante.NewAnteHandler(options)) +} + +func (app *ExampleChain) setPostHandler() { + postHandler, err := posthandler.NewPostHandler( + posthandler.HandlerOptions{}, + ) + if err != nil { + panic(err) + } + + app.SetPostHandler(postHandler) +} + +// Name returns the name of the App +func (app *ExampleChain) Name() string { return app.BaseApp.Name() } + +// BeginBlocker application updates every begin block +func (app *ExampleChain) BeginBlocker(ctx sdk.Context) (sdk.BeginBlock, error) { + return app.ModuleManager.BeginBlock(ctx) +} + +// EndBlocker application updates every end block +func (app *ExampleChain) EndBlocker(ctx sdk.Context) (sdk.EndBlock, error) { + return app.ModuleManager.EndBlock(ctx) +} + +func (app *ExampleChain) FinalizeBlock(req *abci.RequestFinalizeBlock) (res *abci.ResponseFinalizeBlock, err error) { + return app.BaseApp.FinalizeBlock(req) +} + +func (a *ExampleChain) Configurator() module.Configurator { + return a.configurator +} + +// InitChainer application update at chain initialization +func (app *ExampleChain) InitChainer(ctx sdk.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) { + var genesisState evmostypes.GenesisState + if err := json.Unmarshal(req.AppStateBytes, &genesisState); err != nil { + panic(err) + } + + if err := app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap()); err != nil { + panic(err) + } + + return app.ModuleManager.InitGenesis(ctx, app.appCodec, genesisState) +} + +func (app *ExampleChain) PreBlocker(ctx sdk.Context, _ *abci.RequestFinalizeBlock) (*sdk.ResponsePreBlock, error) { + return app.ModuleManager.PreBlock(ctx) +} + +// LoadHeight loads a particular height +func (app *ExampleChain) LoadHeight(height int64) error { + return app.LoadVersion(height) +} + +// LegacyAmino returns ExampleChain's amino codec. +// +// NOTE: This is solely to be used for testing purposes as it may be desirable +// for modules to register their own custom testing types. +func (app *ExampleChain) LegacyAmino() *codec.LegacyAmino { + return app.legacyAmino +} + +// AppCodec returns ExampleChain's app codec. +// +// NOTE: This is solely to be used for testing purposes as it may be desirable +// for modules to register their own custom testing types. +func (app *ExampleChain) AppCodec() codec.Codec { + return app.appCodec +} + +// InterfaceRegistry returns ExampleChain's InterfaceRegistry +func (app *ExampleChain) InterfaceRegistry() types.InterfaceRegistry { + return app.interfaceRegistry +} + +// TxConfig returns ExampleChain's TxConfig +func (app *ExampleChain) TxConfig() client.TxConfig { + return app.txConfig +} + +// DefaultGenesis returns a default genesis from the registered AppModuleBasic's. +// +// TODO: change all receivers to osApp +func (a *ExampleChain) DefaultGenesis() map[string]json.RawMessage { + genesis := a.BasicModuleManager.DefaultGenesis(a.appCodec) + + mintGenState := NewMintGenesisState() + genesis[minttypes.ModuleName] = a.appCodec.MustMarshalJSON(mintGenState) + + evmGenState := NewEVMGenesisState() + genesis[evmtypes.ModuleName] = a.appCodec.MustMarshalJSON(evmGenState) + + // NOTE: for the example chain implementation we are also adding a default token pair, + // which is the base denomination of the chain (i.e. the WEVMOS contract) + erc20GenState := NewErc20GenesisState() + genesis[erc20types.ModuleName] = a.appCodec.MustMarshalJSON(erc20GenState) + + assetGenState := NewAssetGenesisState() + genesis[assettypes.ModuleName] = a.appCodec.MustMarshalJSON(assetGenState) + + return genesis +} + +// GetKey returns the KVStoreKey for the provided store key. +// +// NOTE: This is solely to be used for testing purposes. +func (app *ExampleChain) GetKey(storeKey string) *storetypes.KVStoreKey { + return app.keys[storeKey] +} + +// GetTKey returns the TransientStoreKey for the provided store key. +// +// NOTE: This is solely to be used for testing purposes. +func (app *ExampleChain) GetTKey(storeKey string) *storetypes.TransientStoreKey { + return app.tkeys[storeKey] +} + +// GetMemKey returns the MemStoreKey for the provided mem key. +// +// NOTE: This is solely used for testing purposes. +func (app *ExampleChain) GetMemKey(storeKey string) *storetypes.MemoryStoreKey { + return app.memKeys[storeKey] +} + +// GetSubspace returns a param subspace for a given module name. +// +// NOTE: This is solely to be used for testing purposes. +func (app *ExampleChain) GetSubspace(moduleName string) paramstypes.Subspace { + subspace, _ := app.ParamsKeeper.GetSubspace(moduleName) + return subspace +} + +// SimulationManager implements the SimulationApp interface +func (app *ExampleChain) SimulationManager() *module.SimulationManager { + return app.sm +} + +// RegisterAPIRoutes registers all application module routes with the provided +// API server. +func (app *ExampleChain) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) { + clientCtx := apiSvr.ClientCtx + // Register new tx routes from grpc-gateway. + authtx.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // Register new tendermint queries routes from grpc-gateway. + cmtservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // Register node gRPC service for grpc-gateway. + nodeservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // Register grpc-gateway routes for all modules. + app.BasicModuleManager.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // register swagger API from root so that other applications can override easily + if err := server.RegisterSwaggerAPI(apiSvr.ClientCtx, apiSvr.Router, apiConfig.Swagger); err != nil { + panic(err) + } +} + +// RegisterTxService implements the Application.RegisterTxService method. +func (app *ExampleChain) RegisterTxService(clientCtx client.Context) { + authtx.RegisterTxService(app.GRPCQueryRouter(), clientCtx, app.BaseApp.Simulate, app.interfaceRegistry) +} + +// RegisterTendermintService implements the Application.RegisterTendermintService method. +func (app *ExampleChain) RegisterTendermintService(clientCtx client.Context) { + cmtservice.RegisterTendermintService( + clientCtx, + app.BaseApp.GRPCQueryRouter(), + app.interfaceRegistry, + app.Query, + ) +} + +func (app *ExampleChain) RegisterNodeService(clientCtx client.Context, cfg config.Config) { + node.RegisterNodeService(clientCtx, app.GRPCQueryRouter(), cfg) +} + +// --------------------------------------------- +// IBC Go TestingApp functions +// + +// GetBaseApp implements the TestingApp interface. +func (app *ExampleChain) GetBaseApp() *baseapp.BaseApp { + return app.BaseApp +} + +// GetStakingKeeper implements the TestingApp interface. +func (app *ExampleChain) GetStakingKeeper() ibctestingtypes.StakingKeeper { + return app.StakingKeeper +} + +// GetStakingKeeperSDK implements the TestingApp interface. +func (app *ExampleChain) GetStakingKeeperSDK() stakingkeeper.Keeper { + return *app.StakingKeeper +} + +// GetIBCKeeper implements the TestingApp interface. +func (app *ExampleChain) GetIBCKeeper() *ibckeeper.Keeper { + return app.IBCKeeper +} + +// GetScopedIBCKeeper implements the TestingApp interface. +func (app *ExampleChain) GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper { + return app.scopedIBCKeeper +} + +// GetTxConfig implements the TestingApp interface. +func (app *ExampleChain) GetTxConfig() client.TxConfig { + return app.txConfig +} + +// AutoCliOpts returns the autocli options for the app. +func (app *ExampleChain) AutoCliOpts() autocli.AppOptions { + modules := make(map[string]appmodule.AppModule, 0) + for _, m := range app.ModuleManager.Modules { + if moduleWithName, ok := m.(module.HasName); ok { + moduleName := moduleWithName.Name() + if appModule, ok := moduleWithName.(appmodule.AppModule); ok { + modules[moduleName] = appModule + } + } + } + + return autocli.AppOptions{ + Modules: modules, + ModuleOptions: runtimeservices.ExtractAutoCLIOptions(app.ModuleManager.Modules), + AddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()), + ValidatorAddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()), + ConsensusAddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()), + } +} + +// GetMaccPerms returns a copy of the module account permissions +func GetMaccPerms() map[string][]string { + return maps.Clone(maccPerms) +} + +// BlockedAddresses returns all the app's blocked account addresses. +// +// Note, this includes: +// - module accounts +// - Ethereum's native precompiled smart contracts +// - evmOS' available static precompiled contracts +func BlockedAddresses() map[string]bool { + blockedAddrs := make(map[string]bool) + + maccPerms := GetMaccPerms() + accs := make([]string, 0, len(maccPerms)) + for acc := range maccPerms { + accs = append(accs, acc) + } + sort.Strings(accs) + + for _, acc := range accs { + blockedAddrs[authtypes.NewModuleAddress(acc).String()] = true + } + + blockedPrecompilesHex := evmtypes.AvailableStaticPrecompiles + for _, addr := range vm.PrecompiledAddressesBerlin { + blockedPrecompilesHex = append(blockedPrecompilesHex, addr.Hex()) + } + + for _, precompile := range blockedPrecompilesHex { + blockedAddrs[evmosutils.EthHexToCosmosAddr(precompile).String()] = true + } + + return blockedAddrs +} + +// initParamsKeeper init params keeper and its subspaces +func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey storetypes.StoreKey) paramskeeper.Keeper { + paramsKeeper := paramskeeper.NewKeeper(appCodec, legacyAmino, key, tkey) + + paramsKeeper.Subspace(authtypes.ModuleName) + paramsKeeper.Subspace(banktypes.ModuleName) + paramsKeeper.Subspace(stakingtypes.ModuleName) + paramsKeeper.Subspace(minttypes.ModuleName) + paramsKeeper.Subspace(distrtypes.ModuleName) + paramsKeeper.Subspace(slashingtypes.ModuleName) + paramsKeeper.Subspace(govtypes.ModuleName) + + // ibc modules + keyTable := ibcclienttypes.ParamKeyTable() + keyTable.RegisterParamSet(&ibcconnectiontypes.Params{}) + paramsKeeper.Subspace(ibcexported.ModuleName).WithKeyTable(keyTable) + paramsKeeper.Subspace(ibctransfertypes.ModuleName).WithKeyTable(ibctransfertypes.ParamKeyTable()) + // TODO: do we need a keytable? copied from Evmos repo + + // evmOS modules + paramsKeeper.Subspace(evmtypes.ModuleName) + paramsKeeper.Subspace(feemarkettypes.ModuleName) + paramsKeeper.Subspace(erc20types.ModuleName) + + return paramsKeeper +} diff --git a/example_chain/config.go b/example_chain/config.go new file mode 100644 index 00000000..dd857ddd --- /dev/null +++ b/example_chain/config.go @@ -0,0 +1,90 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +//go:build !test +// +build !test + +package example_chain + +import ( + "fmt" + "strings" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + evmtypes "github.com/evmos/os/x/evm/types" +) + +// EvmosOptionsFn defines a function type for setting app options specifically for +// the Evmos app. The function should receive the chainID and return an error if +// any. +type EvmosOptionsFn func(string) error + +// NoOpEvmosOptions is a no-op function that can be used when the app does not +// need any specific configuration. +func NoOpEvmosOptions(_ string) error { + return nil +} + +var sealed = false + +// ChainsCoinInfo is a map of the chain id and its corresponding EvmCoinInfo +// that allows initializing the app with different coin info based on the +// chain id +var ChainsCoinInfo = map[string]evmtypes.EvmCoinInfo{ + EighteenDecimalsChainID: { + Denom: ExampleChainDenom, + DisplayDenom: ExampleDisplayDenom, + Decimals: evmtypes.EighteenDecimals, + }, +} + +// EvmosAppOptions allows to setup the global configuration +// for the Evmos chain. +func EvmosAppOptions(chainID string) error { + if sealed { + return nil + } + + id := strings.Split(chainID, "-")[0] + coinInfo, found := ChainsCoinInfo[id] + if !found { + return fmt.Errorf("unknown chain id: %s", id) + } + + // set the denom info for the chain + if err := setBaseDenom(coinInfo); err != nil { + return err + } + + baseDenom, err := sdk.GetBaseDenom() + if err != nil { + return err + } + + ethCfg := evmtypes.DefaultChainConfig(chainID) + + err = evmtypes.NewEVMConfigurator(). + WithChainConfig(ethCfg). + // NOTE: we're using the 18 decimals default for the example chain + WithEVMCoinInfo(baseDenom, uint8(coinInfo.Decimals)). + Configure() + if err != nil { + return err + } + + sealed = true + return nil +} + +// setBaseDenom registers the display denom and base denom and sets the +// base denom for the chain. +func setBaseDenom(ci evmtypes.EvmCoinInfo) error { + if err := sdk.RegisterDenom(ci.DisplayDenom, math.LegacyOneDec()); err != nil { + return err + } + + // sdk.RegisterDenom will automatically overwrite the base denom when the + // new setBaseDenom() are lower than the current base denom's units. + return sdk.RegisterDenom(ci.Denom, math.LegacyNewDecWithPrec(1, int64(ci.Decimals))) +} diff --git a/example_chain/constants.go b/example_chain/constants.go new file mode 100644 index 00000000..037a5c07 --- /dev/null +++ b/example_chain/constants.go @@ -0,0 +1,18 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package example_chain + +const ( + // ExampleChainDenom is the denomination of the evmOS example chain's base coin. + ExampleChainDenom = "aevmos" + + // ExampleDisplayDenom is the display denomination of the evmOS example chain's base coin. + ExampleDisplayDenom = "evmos" + + // EighteenDecimalsChainID is the chain ID for the 18 decimals chain. + EighteenDecimalsChainID = "os_9001" + + // SixDecimalsChainID is the chain ID for the 6 decimals chain. + SixDecimalsChainID = "ossix_9002" +) diff --git a/example_chain/export.go b/example_chain/export.go new file mode 100644 index 00000000..c13aaf38 --- /dev/null +++ b/example_chain/export.go @@ -0,0 +1,254 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package example_chain + +import ( + "encoding/json" + "fmt" + "log" + + storetypes "cosmossdk.io/store/types" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + sdk "github.com/cosmos/cosmos-sdk/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + "github.com/cosmos/cosmos-sdk/x/staking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// ExportAppStateAndValidators exports the state of the application for a genesis +// file. +func (app *ExampleChain) ExportAppStateAndValidators(forZeroHeight bool, jailAllowedAddrs []string, modulesToExport []string) (servertypes.ExportedApp, error) { + // as if they could withdraw from the start of the next block + ctx := app.NewContextLegacy(true, tmproto.Header{Height: app.LastBlockHeight()}) + + // We export at last height + 1, because that's the height at which + // Tendermint will start InitChain. + height := app.LastBlockHeight() + 1 + if forZeroHeight { + height = 0 + if err := app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs); err != nil { + return servertypes.ExportedApp{}, err + } + } + + genState, err := app.ModuleManager.ExportGenesisForModules(ctx, app.appCodec, modulesToExport) + if err != nil { + return servertypes.ExportedApp{}, err + } + + appState, err := json.MarshalIndent(genState, "", " ") + if err != nil { + return servertypes.ExportedApp{}, err + } + + validators, err := staking.WriteValidators(ctx, app.StakingKeeper) + return servertypes.ExportedApp{ + AppState: appState, + Validators: validators, + Height: height, + ConsensusParams: app.BaseApp.GetConsensusParams(ctx), + }, err +} + +// prepare for fresh start at zero height +// NOTE zero height genesis is a temporary feature which will be deprecated +// +// in favour of export at a block height +func (app *ExampleChain) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) error { + applyAllowedAddrs := false + + // check if there is a allowed address list + if len(jailAllowedAddrs) > 0 { + applyAllowedAddrs = true + } + + allowedAddrsMap := make(map[string]bool) + + for _, addr := range jailAllowedAddrs { + _, err := sdk.ValAddressFromBech32(addr) + if err != nil { + log.Fatal(err) + } + allowedAddrsMap[addr] = true + } + + /* Handle fee distribution state. */ + + // withdraw all validator commission + if err := app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { + _, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, sdk.ValAddress(val.GetOperator())) + return false + }); err != nil { + return err + } + + // withdraw all delegator rewards + dels, err := app.StakingKeeper.GetAllDelegations(ctx) + if err != nil { + return err + } + + for _, delegation := range dels { + valAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress) + if err != nil { + panic(err) + } + + delAddr := sdk.MustAccAddressFromBech32(delegation.DelegatorAddress) + + _, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delAddr, valAddr) + } + + // clear validator slash events + app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx) + + // clear validator historical rewards + app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx) + + // set context height to zero + height := ctx.BlockHeight() + ctx = ctx.WithBlockHeight(0) + + // reinitialize all validators + err = app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { + // donate any unwithdrawn outstanding reward fraction tokens to the community pool + scraps, err := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, sdk.ValAddress(val.GetOperator())) + if err != nil { + return true + } + feePool, err := app.DistrKeeper.FeePool.Get(ctx) + if err != nil { + return true + } + feePool.CommunityPool = feePool.CommunityPool.Add(scraps...) + err = app.DistrKeeper.FeePool.Set(ctx, feePool) + if err != nil { + return true + } + + err = app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, sdk.ValAddress(val.GetOperator())) + // this lets us stop in case there's an error + return err != nil + }) + if err != nil { + return err + } + + // reinitialize all delegations + for _, del := range dels { + valAddr, err := sdk.ValAddressFromBech32(del.ValidatorAddress) + if err != nil { + panic(err) + } + delAddr := sdk.MustAccAddressFromBech32(del.DelegatorAddress) + + if err := app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr); err != nil { + // never called as BeforeDelegationCreated always returns nil + panic(fmt.Errorf("error while incrementing period: %w", err)) + } + + if err := app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr); err != nil { + // never called as AfterDelegationModified always returns nil + panic(fmt.Errorf("error while creating a new delegation period record: %w", err)) + } + } + + // reset context height + ctx = ctx.WithBlockHeight(height) + + /* Handle staking state. */ + + // iterate through redelegations, reset creation height + var iterErr error + if err := app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) { + for i := range red.Entries { + red.Entries[i].CreationHeight = 0 + } + if iterErr = app.StakingKeeper.SetRedelegation(ctx, red); iterErr != nil { + return true + } + return false + }); err != nil { + return err + } + + if iterErr != nil { + return iterErr + } + + // iterate through unbonding delegations, reset creation height + if err := app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) { + for i := range ubd.Entries { + ubd.Entries[i].CreationHeight = 0 + } + if iterErr = app.StakingKeeper.SetUnbondingDelegation(ctx, ubd); iterErr != nil { + return true + } + return false + }); err != nil { + return err + } + + if iterErr != nil { + return iterErr + } + + // Iterate through validators by power descending, reset bond heights, and + // update bond intra-tx counters. + store := ctx.KVStore(app.GetKey(stakingtypes.StoreKey)) + iter := storetypes.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey) + counter := int16(0) + + for ; iter.Valid(); iter.Next() { + addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key())) + validator, err := app.StakingKeeper.GetValidator(ctx, addr) + if err != nil { + return fmt.Errorf("expected validator %s not found. Error: %w", addr, err) + } + + validator.UnbondingHeight = 0 + if applyAllowedAddrs && !allowedAddrsMap[addr.String()] { + validator.Jailed = true + } + + if err = app.StakingKeeper.SetValidator(ctx, validator); err != nil { + return err + } + counter++ + } + + if err := iter.Close(); err != nil { + app.Logger().Error("error while closing the key-value store reverse prefix iterator: ", err) + return nil + } + + _, err = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + if err != nil { + log.Fatal(err) + } + + /* Handle slashing state. */ + + // reset start height on signing infos + if err := app.SlashingKeeper.IterateValidatorSigningInfos( + ctx, + func(addr sdk.ConsAddress, info slashingtypes.ValidatorSigningInfo) (stop bool) { + info.StartHeight = 0 + if iterErr = app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info); iterErr != nil { + return true + } + return false + }, + ); err != nil { + return err + } + + if iterErr != nil { + return iterErr + } + + return nil +} diff --git a/example_chain/genesis.go b/example_chain/genesis.go new file mode 100644 index 00000000..1e7a712c --- /dev/null +++ b/example_chain/genesis.go @@ -0,0 +1,61 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package example_chain + +import ( + "encoding/json" + + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + erc20types "github.com/evmos/os/x/erc20/types" + evmtypes "github.com/evmos/os/x/evm/types" + assettypes "github.com/realiotech/realio-network/x/asset/types" +) + +// GenesisState of the blockchain is represented here as a map of raw json +// messages key'd by an identifier string. +// The identifier is used to determine which module genesis information belongs +// to so it may be appropriately routed during init chain. +// Within this application default genesis information is retrieved from +// the ModuleBasicManager which populates json from each BasicModule +// object provided to it during init. +type GenesisState map[string]json.RawMessage + +// NewEVMGenesisState returns the default genesis state for the EVM module. +// +// NOTE: for the example chain implementation we need to set the default EVM denomination +// and enable ALL precompiles. +func NewEVMGenesisState() *evmtypes.GenesisState { + evmGenState := evmtypes.DefaultGenesisState() + evmGenState.Params.ActiveStaticPrecompiles = evmtypes.AvailableStaticPrecompiles + + return evmGenState +} + +// NewErc20GenesisState returns the default genesis state for the ERC20 module. +// +// NOTE: for the example chain implementation we are also adding a default token pair, +// which is the base denomination of the chain (i.e. the WEVMOS contract). +func NewErc20GenesisState() *erc20types.GenesisState { + erc20GenState := erc20types.DefaultGenesisState() + erc20GenState.TokenPairs = ExampleTokenPairs + erc20GenState.Params.NativePrecompiles = append(erc20GenState.Params.NativePrecompiles, WEVMOSContractMainnet) + + return erc20GenState +} + +func NewAssetGenesisState() *assettypes.GenesisState { + erc20GenState := assettypes.DefaultGenesis() + + return erc20GenState +} + +// NewMintGenesisState returns the default genesis state for the mint module. +// +// NOTE: for the example chain implementation we are also adding a default minter. +func NewMintGenesisState() *minttypes.GenesisState { + mintGenState := minttypes.DefaultGenesisState() + mintGenState.Params.MintDenom = ExampleChainDenom + + return mintGenState +} diff --git a/example_chain/local_node.sh b/example_chain/local_node.sh new file mode 100755 index 00000000..11524b3a --- /dev/null +++ b/example_chain/local_node.sh @@ -0,0 +1,222 @@ +#!/bin/bash + +CHAINID="${CHAIN_ID:-os_9005-1}" +MONIKER="localtestnet" +# Remember to change to other types of keyring like 'file' in-case exposing to outside world, +# otherwise your balance will be wiped quickly +# The keyring test does not require private key to steal tokens from you +KEYRING="test" +KEYALGO="eth_secp256k1" + +LOGLEVEL="info" +# Set dedicated home directory for the osd instance +HOMEDIR="$HOME/.osd" + +BASEFEE=10000000 + +# Path variables +CONFIG=$HOMEDIR/config/config.toml +APP_TOML=$HOMEDIR/config/app.toml +GENESIS=$HOMEDIR/config/genesis.json +TMP_GENESIS=$HOMEDIR/config/tmp_genesis.json + +# validate dependencies are installed +command -v jq >/dev/null 2>&1 || { + echo >&2 "jq not installed. More info: https://stedolan.github.io/jq/download/" + exit 1 +} + +# used to exit on first error (any non-zero exit code) +set -e + +# Parse input flags +install=true +overwrite="" +BUILD_FOR_DEBUG=false + +while [[ $# -gt 0 ]]; do + key="$1" + case $key in + -y) + echo "Flag -y passed -> Overwriting the previous chain data." + overwrite="y" + shift # Move past the flag + ;; + -n) + echo "Flag -n passed -> Not overwriting the previous chain data." + overwrite="n" + shift # Move past the argument + ;; + --no-install) + echo "Flag --no-install passed -> Skipping installation of the osd binary." + install=false + shift # Move past the flag + ;; + --remote-debugging) + echo "Flag --remote-debugging passed -> Building with remote debugging options." + BUILD_FOR_DEBUG=true + shift # Move past the flag + ;; + *) + echo "Unknown flag passed: $key -> Exiting script!" + exit 1 + ;; + esac +done + +if [[ $install == true ]]; then + if [[ $BUILD_FOR_DEBUG == true ]]; then + # for remote debugging the optimization should be disabled and the debug info should not be stripped + make install COSMOS_BUILD_OPTIONS=nooptimization,nostrip + else + make install + fi +fi + +# User prompt if neither -y nor -n was passed as a flag +# and an existing local node configuration is found. +if [[ $overwrite = "" ]]; then + if [ -d "$HOMEDIR" ]; then + printf "\nAn existing folder at '%s' was found. You can choose to delete this folder and start a new local node with new keys from genesis. When declined, the existing local node is started. \n" "$HOMEDIR" + echo "Overwrite the existing configuration and start a new local node? [y/n]" + read -r overwrite + else + overwrite="y" + fi +fi + +# Setup local node if overwrite is set to Yes, otherwise skip setup +if [[ $overwrite == "y" || $overwrite == "Y" ]]; then + # Remove the previous folder + rm -rf "$HOMEDIR" + + # Set client config + osd config set client chain-id "$CHAINID" --home "$HOMEDIR" + osd config set client keyring-backend "$KEYRING" --home "$HOMEDIR" + + # myKey address 0x7cb61d4117ae31a12e393a1cfa3bac666481d02e | os10jmp6sgh4cc6zt3e8gw05wavvejgr5pwjnpcky + VAL_KEY="mykey" + VAL_MNEMONIC="gesture inject test cycle original hollow east ridge hen combine junk child bacon zero hope comfort vacuum milk pitch cage oppose unhappy lunar seat" + + # dev0 address 0xc6fe5d33615a1c52c08018c47e8bc53646a0e101 | os1cml96vmptgw99syqrrz8az79xer2pcgp84pdun + USER1_KEY="dev0" + USER1_MNEMONIC="copper push brief egg scan entry inform record adjust fossil boss egg comic alien upon aspect dry avoid interest fury window hint race symptom" + + # dev1 address 0x963ebdf2e1f8db8707d05fc75bfeffba1b5bac17 | os1jcltmuhplrdcwp7stlr4hlhlhgd4htqh3a79sq + USER2_KEY="dev1" + USER2_MNEMONIC="maximum display century economy unlock van census kite error heart snow filter midnight usage egg venture cash kick motor survey drastic edge muffin visual" + + # dev2 address 0x40a0cb1C63e026A81B55EE1308586E21eec1eFa9 | os1gzsvk8rruqn2sx64acfsskrwy8hvrmafqkaze8 + USER3_KEY="dev2" + USER3_MNEMONIC="will wear settle write dance topic tape sea glory hotel oppose rebel client problem era video gossip glide during yard balance cancel file rose" + + # dev3 address 0x498B5AeC5D439b733dC2F58AB489783A23FB26dA | os1fx944mzagwdhx0wz7k9tfztc8g3lkfk6rrgv6l + USER4_KEY="dev3" + USER4_MNEMONIC="doll midnight silk carpet brush boring pluck office gown inquiry duck chief aim exit gain never tennis crime fragile ship cloud surface exotic patch" + + # Import keys from mnemonics + echo "$VAL_MNEMONIC" | osd keys add "$VAL_KEY" --recover --keyring-backend "$KEYRING" --algo "$KEYALGO" --home "$HOMEDIR" + echo "$USER1_MNEMONIC" | osd keys add "$USER1_KEY" --recover --keyring-backend "$KEYRING" --algo "$KEYALGO" --home "$HOMEDIR" + echo "$USER2_MNEMONIC" | osd keys add "$USER2_KEY" --recover --keyring-backend "$KEYRING" --algo "$KEYALGO" --home "$HOMEDIR" + echo "$USER3_MNEMONIC" | osd keys add "$USER3_KEY" --recover --keyring-backend "$KEYRING" --algo "$KEYALGO" --home "$HOMEDIR" + echo "$USER4_MNEMONIC" | osd keys add "$USER4_KEY" --recover --keyring-backend "$KEYRING" --algo "$KEYALGO" --home "$HOMEDIR" + + # Set moniker and chain-id for the example chain (Moniker can be anything, chain-id must be an integer) + osd init $MONIKER -o --chain-id "$CHAINID" --home "$HOMEDIR" + + # Change parameter token denominations to desired value + jq '.app_state["staking"]["params"]["bond_denom"]="aevmos"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" + jq '.app_state["gov"]["deposit_params"]["min_deposit"][0]["denom"]="aevmos"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" + jq '.app_state["gov"]["params"]["min_deposit"][0]["denom"]="aevmos"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" + jq '.app_state["evm"]["params"]["evm_denom"]="aevmos"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" + jq '.app_state["mint"]["params"]["mint_denom"]="aevmos"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" + + # Enable precompiles in EVM params + jq '.app_state["evm"]["params"]["active_static_precompiles"]=["0x0000000000000000000000000000000000000100","0x0000000000000000000000000000000000000400","0x0000000000000000000000000000000000000800","0x0000000000000000000000000000000000000801","0x0000000000000000000000000000000000000802","0x0000000000000000000000000000000000000803","0x0000000000000000000000000000000000000804","0x0000000000000000000000000000000000000805"]' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" + + # Enable native denomination as a token pair for STRv2 + jq '.app_state.erc20.params.native_precompiles=["0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"]' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" + jq '.app_state.erc20.token_pairs=[{contract_owner:1,erc20_address:"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",denom:"aevmos",enabled:true}]' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" + + # Set gas limit in genesis + jq '.consensus_params["block"]["max_gas"]="10000000"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" + + if [[ $1 == "pending" ]]; then + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' 's/timeout_propose = "3s"/timeout_propose = "30s"/g' "$CONFIG" + sed -i '' 's/timeout_propose_delta = "500ms"/timeout_propose_delta = "5s"/g' "$CONFIG" + sed -i '' 's/timeout_prevote = "1s"/timeout_prevote = "10s"/g' "$CONFIG" + sed -i '' 's/timeout_prevote_delta = "500ms"/timeout_prevote_delta = "5s"/g' "$CONFIG" + sed -i '' 's/timeout_precommit = "1s"/timeout_precommit = "10s"/g' "$CONFIG" + sed -i '' 's/timeout_precommit_delta = "500ms"/timeout_precommit_delta = "5s"/g' "$CONFIG" + sed -i '' 's/timeout_commit = "5s"/timeout_commit = "150s"/g' "$CONFIG" + sed -i '' 's/timeout_broadcast_tx_commit = "10s"/timeout_broadcast_tx_commit = "150s"/g' "$CONFIG" + else + sed -i 's/timeout_propose = "3s"/timeout_propose = "30s"/g' "$CONFIG" + sed -i 's/timeout_propose_delta = "500ms"/timeout_propose_delta = "5s"/g' "$CONFIG" + sed -i 's/timeout_prevote = "1s"/timeout_prevote = "10s"/g' "$CONFIG" + sed -i 's/timeout_prevote_delta = "500ms"/timeout_prevote_delta = "5s"/g' "$CONFIG" + sed -i 's/timeout_precommit = "1s"/timeout_precommit = "10s"/g' "$CONFIG" + sed -i 's/timeout_precommit_delta = "500ms"/timeout_precommit_delta = "5s"/g' "$CONFIG" + sed -i 's/timeout_commit = "5s"/timeout_commit = "150s"/g' "$CONFIG" + sed -i 's/timeout_broadcast_tx_commit = "10s"/timeout_broadcast_tx_commit = "150s"/g' "$CONFIG" + fi + fi + + # enable prometheus metrics and all APIs for dev node + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' 's/prometheus = false/prometheus = true/' "$CONFIG" + sed -i '' 's/prometheus-retention-time = 0/prometheus-retention-time = 1000000000000/g' "$APP_TOML" + sed -i '' 's/enabled = false/enabled = true/g' "$APP_TOML" + sed -i '' 's/enable = false/enable = true/g' "$APP_TOML" + else + sed -i 's/prometheus = false/prometheus = true/' "$CONFIG" + sed -i 's/prometheus-retention-time = "0"/prometheus-retention-time = "1000000000000"/g' "$APP_TOML" + sed -i 's/enabled = false/enabled = true/g' "$APP_TOML" + sed -i 's/enable = false/enable = true/g' "$APP_TOML" + fi + + # Change proposal periods to pass within a reasonable time for local testing + sed -i.bak 's/"max_deposit_period": "172800s"/"max_deposit_period": "30s"/g' "$GENESIS" + sed -i.bak 's/"voting_period": "172800s"/"voting_period": "30s"/g' "$GENESIS" + sed -i.bak 's/"expedited_voting_period": "86400s"/"expedited_voting_period": "15s"/g' "$GENESIS" + + # set custom pruning settings + sed -i.bak 's/pruning = "default"/pruning = "custom"/g' "$APP_TOML" + sed -i.bak 's/pruning-keep-recent = "0"/pruning-keep-recent = "2"/g' "$APP_TOML" + sed -i.bak 's/pruning-interval = "0"/pruning-interval = "10"/g' "$APP_TOML" + + # Allocate genesis accounts (cosmos formatted addresses) + osd genesis add-genesis-account "$VAL_KEY" 100000000000000000000000000aevmos --keyring-backend "$KEYRING" --home "$HOMEDIR" + osd genesis add-genesis-account "$USER1_KEY" 1000000000000000000000aevmos --keyring-backend "$KEYRING" --home "$HOMEDIR" + osd genesis add-genesis-account "$USER2_KEY" 1000000000000000000000aevmos --keyring-backend "$KEYRING" --home "$HOMEDIR" + osd genesis add-genesis-account "$USER3_KEY" 1000000000000000000000aevmos --keyring-backend "$KEYRING" --home "$HOMEDIR" + osd genesis add-genesis-account "$USER4_KEY" 1000000000000000000000aevmos --keyring-backend "$KEYRING" --home "$HOMEDIR" + + # Sign genesis transaction + osd genesis gentx "$VAL_KEY" 1000000000000000000000aevmos --gas-prices ${BASEFEE}aevmos --keyring-backend "$KEYRING" --chain-id "$CHAINID" --home "$HOMEDIR" + ## In case you want to create multiple validators at genesis + ## 1. Back to `osd keys add` step, init more keys + ## 2. Back to `osd add-genesis-account` step, add balance for those + ## 3. Clone this ~/.osd home directory into some others, let's say `~/.clonedOsd` + ## 4. Run `gentx` in each of those folders + ## 5. Copy the `gentx-*` folders under `~/.clonedOsd/config/gentx/` folders into the original `~/.osd/config/gentx` + + # Collect genesis tx + osd genesis collect-gentxs --home "$HOMEDIR" + + # Run this to ensure everything worked and that the genesis file is setup correctly + osd genesis validate-genesis --home "$HOMEDIR" + + if [[ $1 == "pending" ]]; then + echo "pending mode is on, please wait for the first block committed." + fi +fi + +# Start the node +osd start "$TRACE" \ + --log_level $LOGLEVEL \ + --minimum-gas-prices=0.0001aevmos \ + --home "$HOMEDIR" \ + --json-rpc.api eth,txpool,personal,net,debug,web3 \ + --chain-id "$CHAINID" diff --git a/example_chain/precompiles.go b/example_chain/precompiles.go new file mode 100644 index 00000000..8bd35aeb --- /dev/null +++ b/example_chain/precompiles.go @@ -0,0 +1,123 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package example_chain + +import ( + "fmt" + "maps" + + evidencekeeper "cosmossdk.io/x/evidence/keeper" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + distributionkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + channelkeeper "github.com/cosmos/ibc-go/v8/modules/core/04-channel/keeper" + "github.com/ethereum/go-ethereum/common" + bankprecompile "github.com/evmos/os/precompiles/bank" + "github.com/evmos/os/precompiles/bech32" + distprecompile "github.com/evmos/os/precompiles/distribution" + evidenceprecompile "github.com/evmos/os/precompiles/evidence" + govprecompile "github.com/evmos/os/precompiles/gov" + ics20precompile "github.com/evmos/os/precompiles/ics20" + "github.com/evmos/os/precompiles/p256" + slashingprecompile "github.com/evmos/os/precompiles/slashing" + stakingprecompile "github.com/evmos/os/precompiles/staking" + erc20Keeper "github.com/evmos/os/x/erc20/keeper" + "github.com/evmos/os/x/evm/core/vm" + evmkeeper "github.com/evmos/os/x/evm/keeper" + transferkeeper "github.com/evmos/os/x/ibc/transfer/keeper" +) + +const bech32PrecompileBaseGas = 6_000 + +// NewAvailableStaticPrecompiles returns the list of all available static precompiled contracts from evmOS. +// +// NOTE: this should only be used during initialization of the Keeper. +func NewAvailableStaticPrecompiles( + stakingKeeper stakingkeeper.Keeper, + distributionKeeper distributionkeeper.Keeper, + bankKeeper bankkeeper.Keeper, + erc20Keeper erc20Keeper.Keeper, + authzKeeper authzkeeper.Keeper, + transferKeeper transferkeeper.Keeper, + channelKeeper channelkeeper.Keeper, + evmKeeper *evmkeeper.Keeper, + govKeeper govkeeper.Keeper, + slashingKeeper slashingkeeper.Keeper, + evidenceKeeper evidencekeeper.Keeper, +) map[common.Address]vm.PrecompiledContract { + // Clone the mapping from the latest EVM fork. + precompiles := maps.Clone(vm.PrecompiledContractsBerlin) + + // secp256r1 precompile as per EIP-7212 + p256Precompile := &p256.Precompile{} + + bech32Precompile, err := bech32.NewPrecompile(bech32PrecompileBaseGas) + if err != nil { + panic(fmt.Errorf("failed to instantiate bech32 precompile: %w", err)) + } + + stakingPrecompile, err := stakingprecompile.NewPrecompile(stakingKeeper, authzKeeper) + if err != nil { + panic(fmt.Errorf("failed to instantiate staking precompile: %w", err)) + } + + distributionPrecompile, err := distprecompile.NewPrecompile( + distributionKeeper, + stakingKeeper, + authzKeeper, + evmKeeper, + ) + if err != nil { + panic(fmt.Errorf("failed to instantiate distribution precompile: %w", err)) + } + + ibcTransferPrecompile, err := ics20precompile.NewPrecompile( + stakingKeeper, + transferKeeper, + channelKeeper, + authzKeeper, + evmKeeper, + ) + if err != nil { + panic(fmt.Errorf("failed to instantiate ICS20 precompile: %w", err)) + } + + bankPrecompile, err := bankprecompile.NewPrecompile(bankKeeper, erc20Keeper) + if err != nil { + panic(fmt.Errorf("failed to instantiate bank precompile: %w", err)) + } + + govPrecompile, err := govprecompile.NewPrecompile(govKeeper, authzKeeper) + if err != nil { + panic(fmt.Errorf("failed to instantiate gov precompile: %w", err)) + } + + slashingPrecompile, err := slashingprecompile.NewPrecompile(slashingKeeper, authzKeeper) + if err != nil { + panic(fmt.Errorf("failed to instantiate slashing precompile: %w", err)) + } + + evidencePrecompile, err := evidenceprecompile.NewPrecompile(evidenceKeeper, authzKeeper) + if err != nil { + panic(fmt.Errorf("failed to instantiate evidence precompile: %w", err)) + } + + // Stateless precompiles + precompiles[bech32Precompile.Address()] = bech32Precompile + precompiles[p256Precompile.Address()] = p256Precompile + + // Stateful precompiles + precompiles[stakingPrecompile.Address()] = stakingPrecompile + precompiles[distributionPrecompile.Address()] = distributionPrecompile + precompiles[ibcTransferPrecompile.Address()] = ibcTransferPrecompile + precompiles[bankPrecompile.Address()] = bankPrecompile + precompiles[govPrecompile.Address()] = govPrecompile + precompiles[slashingPrecompile.Address()] = slashingPrecompile + precompiles[evidencePrecompile.Address()] = evidencePrecompile + + return precompiles +} diff --git a/example_chain/test_helpers.go b/example_chain/test_helpers.go new file mode 100644 index 00000000..6c4b412e --- /dev/null +++ b/example_chain/test_helpers.go @@ -0,0 +1,138 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package example_chain + +import ( + "encoding/json" + "fmt" + "testing" + + "cosmossdk.io/log" + "cosmossdk.io/math" + abci "github.com/cometbft/cometbft/abci/types" + cmttypes "github.com/cometbft/cometbft/types" + dbm "github.com/cosmos/cosmos-db" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/server" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/testutil/mock" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + 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" + ibctesting "github.com/cosmos/ibc-go/v8/testing" + "github.com/evmos/os/cmd/config" + feemarkettypes "github.com/evmos/os/x/feemarket/types" + chainconfig "github.com/evmos/os/example_chain/osd/config" + "github.com/stretchr/testify/require" +) + +// SetupOptions defines arguments that are passed into `Simapp` constructor. +type SetupOptions struct { + Logger log.Logger + DB *dbm.MemDB + AppOpts servertypes.AppOptions +} + +func init() { + // we're setting the minimum gas price to 0 to simplify the tests + feemarkettypes.DefaultMinGasPrice = math.LegacyZeroDec() + + // Set the global SDK config for the tests + cfg := sdk.GetConfig() + chainconfig.SetBech32Prefixes(cfg) + config.SetBip44CoinType(cfg) +} + +func setup(withGenesis bool, invCheckPeriod uint, chainID string) (*ExampleChain, GenesisState) { + db := dbm.NewMemDB() + + appOptions := make(simtestutil.AppOptionsMap, 0) + appOptions[flags.FlagHome] = DefaultNodeHome + appOptions[server.FlagInvCheckPeriod] = invCheckPeriod + + app := NewExampleApp(log.NewNopLogger(), db, nil, true, appOptions, EvmosAppOptions, baseapp.SetChainID(chainID)) + if withGenesis { + return app, app.DefaultGenesis() + } + + return app, GenesisState{} +} + +// Setup initializes a new ExampleChain. A Nop logger is set in ExampleChain. +func Setup(t *testing.T, chainID string) *ExampleChain { + t.Helper() + + privVal := mock.NewPV() + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + + // create validator set with single validator + validator := cmttypes.NewValidator(pubKey, 1) + valSet := cmttypes.NewValidatorSet([]*cmttypes.Validator{validator}) + + // generate genesis account + senderPrivKey := secp256k1.GenPrivKey() + acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) + balance := banktypes.Balance{ + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(100000000000000))), + } + + app := SetupWithGenesisValSet(t, chainID, valSet, []authtypes.GenesisAccount{acc}, balance) + + return app +} + +// SetupWithGenesisValSet initializes a new ExampleChain with a validator set and genesis accounts +// that also act as delegators. For simplicity, each validator is bonded with a delegation +// of one consensus engine unit in the default token of the simapp from first genesis +// account. A Nop logger is set in ExampleChain. +func SetupWithGenesisValSet(t *testing.T, chainID string, valSet *cmttypes.ValidatorSet, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *ExampleChain { + t.Helper() + + app, genesisState := setup(true, 5, chainID) + genesisState, err := simtestutil.GenesisStateWithValSet(app.AppCodec(), genesisState, valSet, genAccs, balances...) + require.NoError(t, err) + + stateBytes, err := json.MarshalIndent(genesisState, "", " ") + require.NoError(t, err) + + // init chain will set the validator set and initialize the genesis accounts + if _, err = app.InitChain( + &abci.RequestInitChain{ + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: simtestutil.DefaultConsensusParams, + AppStateBytes: stateBytes, + ChainId: chainID, + }, + ); err != nil { + panic(fmt.Sprintf("app.InitChain failed: %v", err)) + } + + // NOTE: we are NOT committing the changes here as opposed to the function from simapp + // because that would already adjust e.g. the base fee in the params. + // We want to keep the genesis state as is for the tests unless we commit the changes manually. + + return app +} + +// SetupTestingApp initializes the IBC-go testing application +// need to keep this design to comply with the ibctesting SetupTestingApp func +// and be able to set the chainID for the tests properly +func SetupTestingApp(chainID string) func() (ibctesting.TestingApp, map[string]json.RawMessage) { + return func() (ibctesting.TestingApp, map[string]json.RawMessage) { + db := dbm.NewMemDB() + app := NewExampleApp( + log.NewNopLogger(), + db, nil, true, + simtestutil.NewAppOptionsWithFlagHome(DefaultNodeHome), + EvmosAppOptions, + baseapp.SetChainID(chainID), + ) + return app, app.DefaultGenesis() + } +} diff --git a/example_chain/token_pair.go b/example_chain/token_pair.go new file mode 100644 index 00000000..f97fd76c --- /dev/null +++ b/example_chain/token_pair.go @@ -0,0 +1,20 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package example_chain + +import erc20types "github.com/evmos/os/x/erc20/types" + +// WEVMOSContractMainnet is the WEVMOS contract address for mainnet +const WEVMOSContractMainnet = "0xD4949664cD82660AaE99bEdc034a0deA8A0bd517" + +// ExampleTokenPairs creates a slice of token pairs, that contains a pair for the native denom of the example chain +// implementation. +var ExampleTokenPairs = []erc20types.TokenPair{ + { + Erc20Address: WEVMOSContractMainnet, + Denom: ExampleChainDenom, + Enabled: true, + ContractOwner: erc20types.OWNER_MODULE, + }, +} diff --git a/example_chain/upgrades.go b/example_chain/upgrades.go new file mode 100644 index 00000000..8d554e21 --- /dev/null +++ b/example_chain/upgrades.go @@ -0,0 +1,8 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package example_chain + +func (app ExampleChain) RegisterUpgradeHandlers() { + // No upgrades registered yet +} diff --git a/go.mod b/go.mod index 8a580324..46b7c34c 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,15 @@ module github.com/realiotech/realio-network -go 1.22.5 +go 1.22.8 require ( cosmossdk.io/api v0.7.6 - cosmossdk.io/client/v2 v2.0.0-beta.4 + cosmossdk.io/client/v2 v2.0.0-beta.7 cosmossdk.io/collections v0.4.0 cosmossdk.io/core v0.11.1 cosmossdk.io/errors v1.0.1 - cosmossdk.io/log v1.4.1 - cosmossdk.io/math v1.4.0 + cosmossdk.io/log v1.5.0 + cosmossdk.io/math v1.5.0 cosmossdk.io/simapp v0.0.0-20240805084742-3fc80745eef2 cosmossdk.io/store v1.1.1 cosmossdk.io/tools/confix v0.1.2 @@ -17,29 +17,33 @@ require ( cosmossdk.io/x/feegrant v0.1.1 cosmossdk.io/x/tx v0.13.7 cosmossdk.io/x/upgrade v0.1.4 - github.com/cometbft/cometbft v0.38.12 - github.com/cosmos/cosmos-db v1.1.0 + github.com/cometbft/cometbft v0.38.15 + github.com/cosmos/cosmos-db v1.1.1 github.com/cosmos/cosmos-proto v1.0.0-beta.5 github.com/cosmos/cosmos-sdk v0.50.11 github.com/cosmos/gogoproto v1.7.0 github.com/cosmos/ibc-go/modules/capability v1.0.1 - github.com/cosmos/ibc-go/v8 v8.5.1 + github.com/cosmos/ibc-go/v8 v8.5.2 github.com/cosmos/rosetta v0.50.10 github.com/ethereum/go-ethereum v1.11.5 github.com/evmos/os v0.0.0-20241002122822-02a9121016ee + github.com/evmos/os/example_chain v0.0.0-20250130185216-d2cab8abc34d github.com/golang/protobuf v1.5.4 github.com/gorilla/mux v1.8.1 github.com/grpc-ecosystem/grpc-gateway v1.16.0 + github.com/onsi/ginkgo/v2 v2.22.2 + github.com/onsi/gomega v1.36.2 github.com/realio-tech/multi-staking-module v0.0.0-00010101000000-000000000000 - github.com/spf13/cast v1.7.0 + github.com/spf13/cast v1.7.1 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 - github.com/stretchr/testify v1.9.0 - golang.org/x/sync v0.8.0 - google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 - google.golang.org/grpc v1.67.1 - google.golang.org/protobuf v1.35.1 + github.com/stretchr/testify v1.10.0 + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 + golang.org/x/sync v0.10.0 + google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 + google.golang.org/grpc v1.69.2 + google.golang.org/protobuf v1.36.2 gopkg.in/yaml.v2 v2.4.0 ) @@ -47,7 +51,7 @@ require ( cloud.google.com/go v0.115.0 // indirect cloud.google.com/go/auth v0.6.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect - cloud.google.com/go/compute/metadata v0.5.0 // indirect + cloud.google.com/go/compute/metadata v0.5.2 // indirect cloud.google.com/go/iam v1.1.9 // indirect cloud.google.com/go/storage v1.41.0 // indirect cosmossdk.io/depinject v1.1.0 // indirect @@ -72,10 +76,15 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect github.com/btcsuite/btcd/btcutil v1.1.6 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect + github.com/bytedance/sonic v1.12.3 // indirect + github.com/bytedance/sonic/loader v0.2.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chzyer/readline v1.5.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect github.com/cockroachdb/apd/v2 v2.0.2 // indirect + github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/fifo v0.0.0-20240616162244-4768e80dfb9a // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect @@ -83,7 +92,7 @@ require ( github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/coinbase/rosetta-sdk-go/types v1.0.0 // indirect - github.com/cometbft/cometbft-db v0.12.0 // indirect + github.com/cometbft/cometbft-db v0.14.1 // indirect github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect @@ -106,7 +115,6 @@ require ( github.com/dvsekhvalnov/jose2go v1.7.0 // indirect github.com/edsrzf/mmap-go v1.1.0 // indirect github.com/emicklei/dot v1.6.2 // indirect - github.com/evmos/os/example_chain v0.0.0-20240924163020-b2a4187dad50 // indirect github.com/fatih/color v1.17.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -119,6 +127,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/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.3 // indirect @@ -130,6 +139,7 @@ require ( github.com/google/flatbuffers v24.3.25+incompatible // 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-20241210010833-40e02aabc2ad // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect @@ -153,7 +163,7 @@ require ( github.com/hashicorp/yamux v0.1.1 // indirect github.com/hdevalence/ed25519consensus v0.2.0 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect - github.com/holiman/uint256 v1.3.1 // indirect + github.com/holiman/uint256 v1.3.2 // indirect github.com/huandu/skiplist v1.2.0 // indirect github.com/iancoleman/strcase v0.3.0 // indirect github.com/improbable-eng/grpc-web v0.15.0 // indirect @@ -161,16 +171,17 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect - github.com/lib/pq v1.10.7 // indirect - github.com/linxGnu/grocksdb v1.9.2 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/linxGnu/grocksdb v1.9.8 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/minio/highwayhash v1.0.2 // indirect + github.com/minio/highwayhash v1.0.3 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -180,12 +191,12 @@ require ( github.com/oklog/run v1.1.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect - github.com/petermattis/goid v0.0.0-20240607163614-bb94eb51e7a7 // indirect + github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.20.1 // indirect + github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common v0.60.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/tsdb v0.10.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect @@ -196,7 +207,7 @@ require ( github.com/rs/zerolog v1.33.0 // indirect github.com/sagikazarmark/locafero v0.6.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/sasha-s/go-deadlock v0.3.5 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect @@ -204,12 +215,13 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect github.com/tidwall/btree v1.7.0 // indirect - github.com/tidwall/gjson v1.17.3 // indirect + github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/tidwall/sjson v1.2.5 // indirect github.com/tklauser/go-sysconf v0.3.11 // indirect github.com/tklauser/numcpus v0.6.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/ulikunitz/xz v0.5.11 // indirect github.com/zondax/hid v0.9.2 // indirect @@ -218,21 +230,22 @@ require ( go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect - go.opentelemetry.io/otel v1.24.0 // indirect - go.opentelemetry.io/otel/metric v1.24.0 // indirect - go.opentelemetry.io/otel/trace v1.24.0 // indirect + go.opentelemetry.io/otel v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.31.0 // indirect + go.opentelemetry.io/otel/trace v1.31.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.27.0 // indirect - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/net v0.29.0 // indirect - golang.org/x/oauth2 v0.22.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/term v0.24.0 // indirect - golang.org/x/text v0.18.0 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/term v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.28.0 // indirect google.golang.org/api v0.186.0 // indirect google.golang.org/genproto v0.0.0-20240701130421-f6361c86f094 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect @@ -247,9 +260,8 @@ replace ( cosmossdk.io/store => github.com/evmos/cosmos-sdk/store v0.0.0-20240718141609-414cbd051fbe // use Evmos geth fork github.com/ethereum/go-ethereum => github.com/evmos/go-ethereum v1.10.26-evmos-rc4 - // use Realio sdk v0.46.11-realio-4 - // github.com/cosmos/cosmos-sdk => github.com/realiotech/cosmos-sdk v0.46.11-realio-4 - github.com/evmos/os/example_chain => github.com/evmos/os/example_chain v0.0.0-20241002122822-02a9121016ee + + github.com/evmos/os => github.com/evmos/os v0.0.0-20250130185216-d2cab8abc34d // github.com/realio-tech/multi-staking-module => ../multi-staking // github.com/evmos/os => ../evmos-os @@ -261,4 +273,6 @@ replace ( // replace broken goleveldb github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 + +// github.com/realiotech/realio-network/x/asset => ../realio-network-decentrio/x/asset ) diff --git a/go.sum b/go.sum index 34f67412..2bea430f 100644 --- a/go.sum +++ b/go.sum @@ -72,8 +72,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= -cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= +cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo= +cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= @@ -188,8 +188,8 @@ cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1V cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= cosmossdk.io/api v0.7.6 h1:PC20PcXy1xYKH2KU4RMurVoFjjKkCgYRbVAD4PdqUuY= cosmossdk.io/api v0.7.6/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= -cosmossdk.io/client/v2 v2.0.0-beta.4 h1:LGIzWbVTOof/IHQZeoWwxPX0fq607ONXhsfA7eUrQIg= -cosmossdk.io/client/v2 v2.0.0-beta.4/go.mod h1:c753d0sBv3AQRx6X+BOKL1aGpKjZMTZAHGiLPbVi5TE= +cosmossdk.io/client/v2 v2.0.0-beta.7 h1:O0PfZL5kC3Sp54wZASLNihQ612Gd6duMp11aM9wawNg= +cosmossdk.io/client/v2 v2.0.0-beta.7/go.mod h1:TzwwrzeK+AfSVSESVEIOYO/9xuCh1fPv0HgeocmfVnM= cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= cosmossdk.io/core v0.11.1 h1:h9WfBey7NAiFfIcUhDVNS503I2P2HdZLebJlUIs8LPA= @@ -198,10 +198,10 @@ cosmossdk.io/depinject v1.1.0 h1:wLan7LG35VM7Yo6ov0jId3RHWCGRhe8E8bsuARorl5E= cosmossdk.io/depinject v1.1.0/go.mod h1:kkI5H9jCGHeKeYWXTqYdruogYrEeWvBQCw1Pj4/eCFI= 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.4.0 h1:XbgExXFnXmF/CccPPEto40gOO7FpWu9yWNAZPN3nkNQ= -cosmossdk.io/math v1.4.0/go.mod h1:O5PkD4apz2jZs4zqFdTr16e1dcaQCc5z6lkEnrrppuk= +cosmossdk.io/log v1.5.0 h1:dVdzPJW9kMrnAYyMf1duqacoidB9uZIl+7c6z0mnq0g= +cosmossdk.io/log v1.5.0/go.mod h1:Tr46PUJjiUthlwQ+hxYtUtPn4D/oCZXAkYevBeh5+FI= +cosmossdk.io/math v1.5.0 h1:sbOASxee9Zxdjd6OkzogvBZ25/hP929vdcYcBJQbkLc= +cosmossdk.io/math v1.5.0/go.mod h1:AAwwBmUhqtk2nlku174JwSll+/DepUXW3rWIXN5q+Nw= cosmossdk.io/simapp v0.0.0-20240805084742-3fc80745eef2 h1:f2zS+kpAqxGEbY1xHUK7ytfKeCVkmu89FMYGAK+FbYA= cosmossdk.io/simapp v0.0.0-20240805084742-3fc80745eef2/go.mod h1:prWoljZuPhMPxw7Xtc7FhceNtg6hSbla9S5GreyfM58= cosmossdk.io/tools/confix v0.1.2 h1:2hoM1oFCNisd0ltSAAZw2i4ponARPmlhuNu3yy0VwI4= @@ -252,8 +252,8 @@ github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= -github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= +github.com/adlio/schema v1.3.6 h1:k1/zc2jNfeiZBA5aFTRy37jlBIuCkXCm0XmvpzCKI9I= +github.com/adlio/schema v1.3.6/go.mod h1:qkxwLgPBd1FgLRHYVCmQT/rrBr3JH38J9LjmVzWNudg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -317,6 +317,11 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= +github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU= +github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= +github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -344,6 +349,10 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -355,6 +364,8 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= +github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= +github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= @@ -373,10 +384,10 @@ github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1: github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coinbase/rosetta-sdk-go/types v1.0.0 h1:jpVIwLcPoOeCR6o1tU+Xv7r5bMONNbHU7MuEHboiFuA= github.com/coinbase/rosetta-sdk-go/types v1.0.0/go.mod h1:eq7W2TMRH22GTW0N0beDnN931DW0/WOI1R2sdHNHG4c= -github.com/cometbft/cometbft v0.38.12 h1:OWsLZN2KcSSFe8bet9xCn07VwhBnavPea3VyPnNq1bg= -github.com/cometbft/cometbft v0.38.12/go.mod h1:GPHp3/pehPqgX1930HmK1BpBLZPxB75v/dZg8Viwy+o= -github.com/cometbft/cometbft-db v0.12.0 h1:v77/z0VyfSU7k682IzZeZPFZrQAKiQwkqGN0QzAjMi0= -github.com/cometbft/cometbft-db v0.12.0/go.mod h1:aX2NbCrjNVd2ZajYxt1BsiFf/Z+TQ2MN0VxdicheYuw= +github.com/cometbft/cometbft v0.38.15 h1:5veFd8k1uXM27PBg9sMO3hAfRJ3vbh4OmmLf6cVrqXg= +github.com/cometbft/cometbft v0.38.15/go.mod h1:+wh6ap6xctVG+JOHwbl8pPKZ0GeqdPYqISu7F4b43cQ= +github.com/cometbft/cometbft-db v0.14.1 h1:SxoamPghqICBAIcGpleHbmoPqy+crij/++eZz3DlerQ= +github.com/cometbft/cometbft-db v0.14.1/go.mod h1:KHP1YghilyGV/xjD5DP3+2hyigWx0WTp9X+0Gnx0RxQ= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -385,8 +396,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= -github.com/cosmos/cosmos-db v1.1.0 h1:KLHNVQ73h7vawXTpj9UJ7ZR2IXv51tsEHkQJJ9EBDzI= -github.com/cosmos/cosmos-db v1.1.0/go.mod h1:t7c4A6cfGdpUwwVxrQ0gQLeRQqGUBJu0yvE4F/26REg= +github.com/cosmos/cosmos-db v1.1.1 h1:FezFSU37AlBC8S98NlSagL76oqBRWq/prTPvFcEJNCM= +github.com/cosmos/cosmos-db v1.1.1/go.mod h1:AghjcIPqdhSLP/2Z0yha5xPH3nLnskz81pBx3tcVSAw= github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRApmI9hjHH05nefC0Ojec= github.com/cosmos/cosmos-sdk v0.50.11 h1:LxR1aAc8kixdrs3itO+3a44sFoc+vjxVAOyPFx22yjk= @@ -402,8 +413,8 @@ github.com/cosmos/iavl v1.2.2 h1:qHhKW3I70w+04g5KdsdVSHRbFLgt3yY3qTMd4Xa4rC8= github.com/cosmos/iavl v1.2.2/go.mod h1:GiM43q0pB+uG53mLxLDzimxM9l/5N9UuSY3/D0huuVw= github.com/cosmos/ibc-go/modules/capability v1.0.1 h1:ibwhrpJ3SftEEZRxCRkH0fQZ9svjthrX2+oXdZvzgGI= github.com/cosmos/ibc-go/modules/capability v1.0.1/go.mod h1:rquyOV262nGJplkumH+/LeYs04P3eV8oB7ZM4Ygqk4E= -github.com/cosmos/ibc-go/v8 v8.5.1 h1:3JleEMKBjRKa3FeTKt4fjg22za/qygLBo7mDkoYTNBs= -github.com/cosmos/ibc-go/v8 v8.5.1/go.mod h1:P5hkAvq0Qbg0h18uLxDVA9q1kOJ0l36htMsskiNwXbo= +github.com/cosmos/ibc-go/v8 v8.5.2 h1:27s9oeD2AxLQF3e9BQsYt9doONyZ7FwZi/qkBv6Sdks= +github.com/cosmos/ibc-go/v8 v8.5.2/go.mod h1:P5hkAvq0Qbg0h18uLxDVA9q1kOJ0l36htMsskiNwXbo= github.com/cosmos/ics23/go v0.11.0 h1:jk5skjT0TqX5e5QJbEnwXIS2yI2vnmLOgpQPeM5RtnU= github.com/cosmos/ics23/go v0.11.0/go.mod h1:A8OjxPE67hHST4Icw94hOxxFEJMBG031xIGF/JHNIY0= github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= @@ -492,10 +503,10 @@ github.com/evmos/cosmos-sdk/store v0.0.0-20240718141609-414cbd051fbe h1:CKvjP3Cc github.com/evmos/cosmos-sdk/store v0.0.0-20240718141609-414cbd051fbe/go.mod h1:Bm6h8ZkYgVTytHK5vhHOMKw9OHyiumm3b1UbkYIJ/Ug= github.com/evmos/go-ethereum v1.10.26-evmos-rc4 h1:vwDVMScuB2KSu8ze5oWUuxm6v3bMUp6dL3PWvJNJY+I= github.com/evmos/go-ethereum v1.10.26-evmos-rc4/go.mod h1:/6CsT5Ceen2WPLI/oCA3xMcZ5sWMF/D46SjM/ayY0Oo= -github.com/evmos/os v0.0.0-20241002122822-02a9121016ee h1:lPqHHT7ezQbrF7NLK2tCCjD8NQ31TeNXW7l4nNFWslI= -github.com/evmos/os v0.0.0-20241002122822-02a9121016ee/go.mod h1:uQQyppk8z2C7Ypk/zj8/wnJSrf74rY8cXLzsD8F1tEE= -github.com/evmos/os/example_chain v0.0.0-20241002122822-02a9121016ee h1:BZpePPAM2zYn8P9EkU14K0p4TTgwEQJLaQG4FjNh99Q= -github.com/evmos/os/example_chain v0.0.0-20241002122822-02a9121016ee/go.mod h1:+SPMqw9wtbWO3jG02uLbLtVVjMHBldmXTN51kxbWqJ8= +github.com/evmos/os v0.0.0-20250130185216-d2cab8abc34d h1:EAHT7l/YzzrbEPl2C8iQz5W7zDwQ8cYKmHsXzI3Uefw= +github.com/evmos/os v0.0.0-20250130185216-d2cab8abc34d/go.mod h1:zgs0dJ8M4AQtRDtGNCa6h2XqQDh4a5ar4Za+B80YJwc= +github.com/evmos/os/example_chain v0.0.0-20250130185216-d2cab8abc34d h1:4K8xoiN5ikYkcbZIxrxmzFtG1y6ZkT2OJqKdvHqU0MM= +github.com/evmos/os/example_chain v0.0.0-20250130185216-d2cab8abc34d/go.mod h1:NyqEZcwpY6sa+Re11S/Q1Q1cnfyy7I9YmSS0Pi72WtU= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= @@ -665,8 +676,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-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= -github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= 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= @@ -772,8 +783,8 @@ github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs= -github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= +github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= @@ -826,6 +837,10 @@ github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -841,12 +856,12 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/linxGnu/grocksdb v1.9.2 h1:O3mzvO0wuzQ9mtlHbDrShixyVjVbmuqTjFrzlf43wZ8= -github.com/linxGnu/grocksdb v1.9.2/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= +github.com/linxGnu/grocksdb v1.9.8 h1:vOIKv9/+HKiqJAElJIEYv3ZLcihRxyP7Suu/Mu8Dxjs= +github.com/linxGnu/grocksdb v1.9.8/go.mod h1:C3CNe9UYc9hlEM2pC82AqiGS3LRW537u9LFV4wIZuHk= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= @@ -872,8 +887,8 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= -github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= +github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -928,14 +943,14 @@ 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.19.1 h1:QXgq3Z8Crl5EL1WBAC98A5sEBHARrAJNzAmMxzLcRF0= -github.com/onsi/ginkgo/v2 v2.19.1/go.mod h1:O3DtEWQkPa/F7fBMgmZQKKsluAy8pd3rEQdrjkPb9zA= +github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= +github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= 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= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.34.0 h1:eSSPsPNp6ZpsG8X1OVmOTxig+CblTc4AxpPBykhe2Os= -github.com/onsi/gomega v1.34.0/go.mod h1:MIKI8c+f+QLWk+hxbePD4i0LMJSExPaZOVfkoex4cAo= +github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= +github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -961,9 +976,8 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= -github.com/petermattis/goid v0.0.0-20240607163614-bb94eb51e7a7 h1:CtBLeckhC0zAXgp5V8uR30CNYH0JgCJoxCg5+6i2zQk= -github.com/petermattis/goid v0.0.0-20240607163614-bb94eb51e7a7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= @@ -984,8 +998,8 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.20.1 h1:IMJXHOD6eARkQpxo8KkhgEVFlBNm+nkrFUyGlIu7Na8= -github.com/prometheus/client_golang v1.20.1/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -1000,8 +1014,8 @@ github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt2 github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -1045,8 +1059,8 @@ github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= -github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= +github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= +github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= @@ -1066,8 +1080,8 @@ github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIK github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= -github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= @@ -1098,8 +1112,9 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= @@ -1109,8 +1124,8 @@ github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoM github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94= -github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= @@ -1123,6 +1138,8 @@ github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYm github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= @@ -1167,20 +1184,24 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= -go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= -go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= -go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= -go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= -go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= -go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= -go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -1190,6 +1211,8 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1201,8 +1224,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1302,8 +1325,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1329,8 +1352,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1345,8 +1368,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.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= @@ -1356,7 +1379,6 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1443,15 +1465,17 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1462,8 +1486,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1530,8 +1554,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= 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= @@ -1710,10 +1734,10 @@ google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/genproto v0.0.0-20240701130421-f6361c86f094 h1:6whtk83KtD3FkGrVb2hFXuQ+ZMbCNdakARIn/aHMmG8= google.golang.org/genproto v0.0.0-20240701130421-f6361c86f094/go.mod h1:Zs4wYw8z1zr6RNF4cwYb31mvN/EGaKAdQjNCF3DW6K4= -google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= -google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f h1:cUMEy+8oS78BWIH9OWazBkzbr090Od9tWBNtZHkOhf0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U= +google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1755,8 +1779,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= +google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1773,8 +1797,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU= +google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1822,6 +1846,7 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0= nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/precompiles/erc20/IAccessControl.sol b/precompiles/erc20/IAccessControl.sol new file mode 100644 index 00000000..75f38e96 --- /dev/null +++ b/precompiles/erc20/IAccessControl.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IAccessControl { + + /** + * @dev Set role for an address + */ + function grantRole(bytes32 role, address addr) external returns (bool); + + /** + * @dev Unset role for an address + */ + function revokeRole(bytes32 role, address addr) external returns (bool); +} diff --git a/precompiles/erc20/IERC20.sol b/precompiles/erc20/IERC20.sol new file mode 100644 index 00000000..66c4e4d8 --- /dev/null +++ b/precompiles/erc20/IERC20.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `from` to `to` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address from, address to, uint256 amount) external returns (bool); +} diff --git a/precompiles/erc20/IERC20Metadata.sol b/precompiles/erc20/IERC20Metadata.sol new file mode 100644 index 00000000..982bc39e --- /dev/null +++ b/precompiles/erc20/IERC20Metadata.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) + +pragma solidity ^0.8.0; + +import "./IERC20.sol"; + +/** + * @dev Interface for the optional metadata functions from the ERC20 standard. + * + * _Available since v4.1._ + */ +interface IERC20Metadata is IERC20 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the decimals places of the token. + */ + function decimals() external view returns (uint8); +} diff --git a/precompiles/erc20/IERC20MetadataAllowance.sol b/precompiles/erc20/IERC20MetadataAllowance.sol new file mode 100644 index 00000000..4eb31bf7 --- /dev/null +++ b/precompiles/erc20/IERC20MetadataAllowance.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.8.18; + +import "./IERC20Metadata.sol"; + +/** + * @author Evmos Team + * @title ERC20 Metadata Allowance Interface + * @dev Interface for the optional metadata and allowance functions from the ERC20 standard. + */ +interface IERC20MetadataAllowance is IERC20Metadata { + /** @dev Atomically increases the allowance granted to spender by the caller. + * This is an alternative to approve that can be used as a mitigation for problems described in + * IERC20.approve. + * @param spender The address which will spend the funds. + * @param addedValue The amount of tokens added to the spender allowance. + * @return approved Boolean value to indicate if the approval was successful. + */ + function increaseAllowance( + address spender, + uint256 addedValue + ) external returns (bool approved); + + /** @dev Atomically decreases the allowance granted to spender by the caller. + * This is an alternative to approve that can be used as a mitigation for problems described in + * IERC20.approve. + * @param spender The address which will spend the funds. + * @param subtractedValue The amount to be subtracted from the spender allowance. + * @return approved Boolean value to indicate if the approval was successful. + */ + function decreaseAllowance( + address spender, + uint256 subtractedValue + ) external returns (bool approved); +} diff --git a/precompiles/erc20/abi.json b/precompiles/erc20/abi.json new file mode 100644 index 00000000..071e5942 --- /dev/null +++ b/precompiles/erc20/abi.json @@ -0,0 +1,790 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "ERC20MinterBurnerDecimals", + "sourceName": "solidity/ERC20MinterBurnerDecimals.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "decimals_", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "Freeze", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "BURNER_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MINTER_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PAUSER_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "freeze", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burnCoins", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burnFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRoleMember", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleMemberCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x", + "deployedBytecode": "0x", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/precompiles/erc20/access_control.go b/precompiles/erc20/access_control.go new file mode 100644 index 00000000..3224c810 --- /dev/null +++ b/precompiles/erc20/access_control.go @@ -0,0 +1,94 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package erc20 + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/evmos/os/x/evm/core/vm" + assettypes "github.com/realiotech/realio-network/x/asset/types" +) + +func (p *Precompile) GrantRole( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + to, role, err := ParseGrantRoleArgs(args) + if err != nil { + return nil, err + } + + return p.grantRole(ctx, contract, stateDB, method, to, role) +} + +func (p *Precompile) RevokeRole( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + to, role, err := ParseRevokeRoleArgs(args) + if err != nil { + return nil, err + } + + return p.grantRole(ctx, contract, stateDB, method, to, role) +} + +func (p *Precompile) grantRole( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + to common.Address, + role int32, +) (data []byte, err error) { + sender := contract.CallerAddress + + if role != int32(assettypes.ManagerRole) { + return nil, fmt.Errorf("only accept manager role") + } + err = p.assetKeep.GrantRole(ctx, p.denom, sender.Bytes(), to.Bytes()) + if err != nil { + return nil, err + } + + if err = p.EmitGrantRoleEvent(ctx, stateDB, sender, to, role); err != nil { + return nil, err + } + + return method.Outputs.Pack(true) +} + +func (p *Precompile) revokeRole( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + to common.Address, + role int32, +) (data []byte, err error) { + sender := contract.CallerAddress + + if role != int32(assettypes.ManagerRole) { + return nil, fmt.Errorf("only accept manager role") + } + err = p.assetKeep.RevokeRole(ctx, p.denom, sender.Bytes(), to.Bytes()) + if err != nil { + return nil, err + } + + if err = p.EmitRevokeRoleEvent(ctx, stateDB, sender, to, role); err != nil { + return nil, err + } + + return method.Outputs.Pack(true) +} diff --git a/precompiles/erc20/approve.go b/precompiles/erc20/approve.go new file mode 100644 index 00000000..8dc673ba --- /dev/null +++ b/precompiles/erc20/approve.go @@ -0,0 +1,336 @@ +package erc20 + +import ( + "bytes" + "errors" + "fmt" + "math/big" + "time" + + sdkerrors "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + auth "github.com/evmos/os/precompiles/authorization" + cmn "github.com/evmos/os/precompiles/common" + "github.com/evmos/os/x/evm/core/vm" +) + +// Approve sets the given amount as the allowance of the spender address over +// the caller’s tokens. It returns a boolean value indicating whether the +// operation succeeded and emits the Approval event on success. +// +// The Approve method handles 4 cases: +// 1. no authorization, amount negative -> return error +// 2. no authorization, amount positive -> create a new authorization +// 3. authorization exists, amount 0 or negative -> delete authorization +// 4. authorization exists, amount positive -> update authorization +// 5. no authorizaiton, amount 0 -> no-op but still emit Approval event +func (p Precompile) Approve( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + spender, amount, err := ParseApproveArgs(args) + if err != nil { + return nil, err + } + + grantee := spender + granter := contract.CallerAddress + + // NOTE: We do not support approvals if the grantee is the granter. + // This is different from the ERC20 standard but there is no reason to + // do so, since in that case the grantee can just transfer the tokens + // without authorization. + if bytes.Equal(grantee.Bytes(), granter.Bytes()) { + return nil, ErrSpenderIsOwner + } + + // TODO: owner should be the owner of the contract + authorization, expiration, _ := auth.CheckAuthzExists(ctx, p.AuthzKeeper, grantee, granter, SendMsgURL) //#nosec:G703 -- we are handling the error case (authorization == nil) in the switch statement below + + switch { + case authorization == nil && amount != nil && amount.Sign() < 0: + // case 1: no authorization, amount 0 or negative -> error + err = ErrNegativeAmount + case authorization == nil && amount != nil && amount.Sign() > 0: + // case 2: no authorization, amount positive -> create a new authorization + err = p.createAuthorization(ctx, grantee, granter, amount) + case authorization != nil && amount != nil && amount.Sign() <= 0: + // case 3: authorization exists, amount 0 or negative -> remove from spend limit and delete authorization if no spend limit left + err = p.removeSpendLimitOrDeleteAuthorization(ctx, grantee, granter, authorization, expiration) + case authorization != nil && amount != nil && amount.Sign() > 0: + // case 4: authorization exists, amount positive -> update authorization + sendAuthz, ok := authorization.(*banktypes.SendAuthorization) + if !ok { + return nil, authz.ErrUnknownAuthorizationType + } + + err = p.updateAuthorization(ctx, grantee, granter, amount, sendAuthz, expiration) + } + + if err != nil { + return nil, err + } + + // TODO: check owner? + if err := p.EmitApprovalEvent(ctx, stateDB, p.Address(), spender, amount); err != nil { + return nil, err + } + + return method.Outputs.Pack(true) +} + +// IncreaseAllowance increases the allowance of the spender address over +// the caller’s tokens by the given added value. It returns a boolean value +// indicating whether the operation succeeded and emits the Approval event on +// success. +// +// The IncreaseAllowance method handles 3 cases: +// 1. addedValue 0 or negative -> return error +// 2. no authorization, addedValue positive -> create a new authorization +// 3. authorization exists, addedValue positive -> update authorization +func (p Precompile) IncreaseAllowance( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + spender, addedValue, err := ParseApproveArgs(args) + if err != nil { + return nil, err + } + + grantee := spender + granter := contract.CallerAddress + + if bytes.Equal(grantee.Bytes(), granter.Bytes()) { + return nil, ErrSpenderIsOwner + } + + // TODO: owner should be the owner of the contract + authorization, expiration, _ := auth.CheckAuthzExists(ctx, p.AuthzKeeper, grantee, granter, SendMsgURL) //#nosec:G703 -- we are handling the error case (authorization == nil) in the switch statement below + + var amount *big.Int + switch { + case addedValue != nil && addedValue.Sign() <= 0: + // case 1: addedValue 0 or negative -> error + // TODO: (@fedekunze) check if this is correct by comparing behavior with + // regular ERC20 + err = ErrIncreaseNonPositiveValue + case authorization == nil && addedValue != nil && addedValue.Sign() > 0: + // case 2: no authorization, amount positive -> create a new authorization + amount = addedValue + err = p.createAuthorization(ctx, grantee, granter, addedValue) + case authorization != nil && addedValue != nil && addedValue.Sign() > 0: + // case 3: authorization exists, amount positive -> update authorization + amount, err = p.increaseAllowance(ctx, grantee, granter, addedValue, authorization, expiration) + } + + if err != nil { + return nil, err + } + + // TODO: check owner? + if err := p.EmitApprovalEvent(ctx, stateDB, p.Address(), spender, amount); err != nil { + return nil, err + } + + return method.Outputs.Pack(true) +} + +// DecreaseAllowance decreases the allowance of the spender address over +// the caller’s tokens by the given subtracted value. It returns a boolean value +// indicating whether the operation succeeded and emits the Approval event on +// success. +// +// The DecreaseAllowance method handles 4 cases: +// 1. subtractedValue 0 or negative -> return error +// 2. no authorization -> return error +// 3. authorization exists, subtractedValue positive and subtractedValue less than allowance -> update authorization +// 4. authorization exists, subtractedValue positive and subtractedValue equal to allowance -> delete authorization +// 5. authorization exists, subtractedValue positive but no allowance for given denomination -> return error +// 6. authorization exists, subtractedValue positive and subtractedValue higher than allowance -> return error +func (p Precompile) DecreaseAllowance( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + spender, subtractedValue, err := ParseApproveArgs(args) + if err != nil { + return nil, err + } + + grantee := spender + granter := contract.CallerAddress + + if bytes.Equal(grantee.Bytes(), granter.Bytes()) { + return nil, ErrSpenderIsOwner + } + // TODO: owner should be the owner of the contract + + authorization, expiration, allowance, err := GetAuthzExpirationAndAllowance(p.AuthzKeeper, ctx, grantee, granter, p.denom) + + // TODO: (@fedekunze) check if this is correct by comparing behavior with + // regular ERC-20 + var amount *big.Int + switch { + case subtractedValue != nil && subtractedValue.Sign() <= 0: + // case 1. subtractedValue 0 or negative -> return error + err = ErrDecreaseNonPositiveValue + case err != nil: + // case 2. no authorization -> return error + err = sdkerrors.Wrap(err, fmt.Sprintf(ErrNoAllowanceForToken, p.denom)) + case subtractedValue != nil && subtractedValue.Cmp(allowance) < 0: + // case 3. subtractedValue positive and subtractedValue less than allowance -> update authorization + amount, err = p.decreaseAllowance(ctx, grantee, granter, subtractedValue, authorization, expiration) + case subtractedValue != nil && subtractedValue.Cmp(allowance) == 0: + // case 4. subtractedValue positive and subtractedValue equal to allowance -> remove spend limit for token and delete authorization if no other denoms are approved for + err = p.removeSpendLimitOrDeleteAuthorization(ctx, grantee, granter, authorization, expiration) + amount = common.Big0 + case subtractedValue != nil && allowance.Sign() == 0: + // case 5. subtractedValue positive but no allowance for given denomination -> return error + err = fmt.Errorf(ErrNoAllowanceForToken, p.denom) + case subtractedValue != nil && subtractedValue.Cmp(allowance) > 0: + // case 6. subtractedValue positive and subtractedValue higher than allowance -> return error + err = ConvertErrToERC20Error(fmt.Errorf(ErrSubtractMoreThanAllowance, p.denom, subtractedValue, allowance)) + } + + if err != nil { + return nil, err + } + + // TODO: check owner? + if err := p.EmitApprovalEvent(ctx, stateDB, p.Address(), spender, amount); err != nil { + return nil, err + } + + return method.Outputs.Pack(true) +} + +func (p Precompile) createAuthorization(ctx sdk.Context, grantee, granter common.Address, amount *big.Int) error { + if amount.BitLen() > sdkmath.MaxBitLen { + return fmt.Errorf(ErrIntegerOverflow, amount) + } + + coins := sdk.Coins{{Denom: p.denom, Amount: sdkmath.NewIntFromBigInt(amount)}} + expiration := ctx.BlockTime().Add(p.ApprovalExpiration) + + // Check if granter is freezed + freezed := p.assetKeep.IsFreezed(ctx, granter) + if freezed { + return fmt.Errorf("address %s already be freezed", granter.String()) + } + + // NOTE: we leave the allowed arg empty as all recipients are allowed (per ERC20 standard) + authorization := banktypes.NewSendAuthorization(coins, []sdk.AccAddress{}) + if err := authorization.ValidateBasic(); err != nil { + return err + } + + return p.AuthzKeeper.SaveGrant(ctx, grantee.Bytes(), granter.Bytes(), authorization, &expiration) +} + +func (p Precompile) updateAuthorization(ctx sdk.Context, grantee, granter common.Address, amount *big.Int, authorization *banktypes.SendAuthorization, expiration *time.Time) error { + authorization.SpendLimit = updateOrAddCoin(authorization.SpendLimit, sdk.Coin{Denom: p.denom, Amount: sdkmath.NewIntFromBigInt(amount)}) + if err := authorization.ValidateBasic(); err != nil { + return err + } + + return p.AuthzKeeper.SaveGrant(ctx, grantee.Bytes(), granter.Bytes(), authorization, expiration) +} + +// removeSpendLimitOrDeleteAuthorization removes the spend limit for the given +// token and updates the grant or deletes the authorization if no spend limit in another +// denomination is set. +func (p Precompile) removeSpendLimitOrDeleteAuthorization(ctx sdk.Context, grantee, granter common.Address, authorization authz.Authorization, expiration *time.Time) error { + sendAuthz, ok := authorization.(*banktypes.SendAuthorization) + if !ok { + return authz.ErrUnknownAuthorizationType + } + + found, denomCoins := sendAuthz.SpendLimit.Find(p.denom) + if !found { + return fmt.Errorf(ErrNoAllowanceForToken, p.denom) + } + + newSpendLimit, hasNeg := sendAuthz.SpendLimit.SafeSub(denomCoins) + // NOTE: safety check only, this should never happen since we only subtract what was found in the slice. + if hasNeg { + return ConvertErrToERC20Error(fmt.Errorf(ErrSubtractMoreThanAllowance, + p.denom, denomCoins, sendAuthz.SpendLimit, + )) + } + + if newSpendLimit.IsZero() { + return p.AuthzKeeper.DeleteGrant(ctx, grantee.Bytes(), granter.Bytes(), SendMsgURL) + } + + sendAuthz.SpendLimit = newSpendLimit + return p.AuthzKeeper.SaveGrant(ctx, grantee.Bytes(), granter.Bytes(), sendAuthz, expiration) +} + +func (p Precompile) increaseAllowance( + ctx sdk.Context, + grantee, granter common.Address, + addedValue *big.Int, + authorization authz.Authorization, + expiration *time.Time, +) (amount *big.Int, err error) { + sendAuthz, ok := authorization.(*banktypes.SendAuthorization) + if !ok { + return nil, authz.ErrUnknownAuthorizationType + } + + allowance := sendAuthz.SpendLimit.AmountOfNoDenomValidation(p.denom) + sdkAddedValue := sdkmath.NewIntFromBigInt(addedValue) + amount, overflow := cmn.SafeAdd(allowance, sdkAddedValue) + if overflow { + return nil, ConvertErrToERC20Error(errors.New(cmn.ErrIntegerOverflow)) + } + + if err := p.updateAuthorization(ctx, grantee, granter, amount, sendAuthz, expiration); err != nil { + return nil, err + } + + return amount, nil +} + +func (p Precompile) decreaseAllowance( + ctx sdk.Context, + grantee, granter common.Address, + subtractedValue *big.Int, + authorization authz.Authorization, + expiration *time.Time, +) (amount *big.Int, err error) { + sendAuthz, ok := authorization.(*banktypes.SendAuthorization) + if !ok { + return nil, authz.ErrUnknownAuthorizationType + } + + found, allowance := sendAuthz.SpendLimit.Find(p.denom) + if !found { + return nil, fmt.Errorf(ErrNoAllowanceForToken, p.denom) + } + + amount = new(big.Int).Sub(allowance.Amount.BigInt(), subtractedValue) + // NOTE: Safety check only since this is checked in the DecreaseAllowance method already. + if amount.Sign() < 0 { + return nil, ConvertErrToERC20Error(fmt.Errorf(ErrSubtractMoreThanAllowance, p.denom, subtractedValue, allowance.Amount)) + } + + if err := p.updateAuthorization(ctx, grantee, granter, amount, sendAuthz, expiration); err != nil { + return nil, err + } + + return amount, nil +} diff --git a/precompiles/erc20/erc20.go b/precompiles/erc20/erc20.go new file mode 100644 index 00000000..5cc3a26f --- /dev/null +++ b/precompiles/erc20/erc20.go @@ -0,0 +1,251 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package erc20 + +import ( + "embed" + "fmt" + "slices" + + storetypes "cosmossdk.io/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + auth "github.com/evmos/os/precompiles/authorization" + cmn "github.com/evmos/os/precompiles/common" + "github.com/evmos/os/x/evm/core/vm" + transferkeeper "github.com/evmos/os/x/ibc/transfer/keeper" + assetkeeper "github.com/realiotech/realio-network/x/asset/keeper" +) + +const ( + // abiPath defines the path to the ERC-20 precompile ABI JSON file. + abiPath = "abi.json" + + GasTransfer = 3_000_000 + GasApprove = 30_956 + GasIncreaseAllowance = 34_605 + GasDecreaseAllowance = 34_519 + GasName = 3_421 + GasSymbol = 3_464 + GasDecimals = 427 + GasTotalSupply = 2_477 + GasBalanceOf = 2_851 + GasAllowance = 3_246 +) + +// Embed abi json file to the executable binary. Needed when importing as dependency. +// +//go:embed abi.json +var f embed.FS + +var _ vm.PrecompiledContract = &Precompile{} + +// Precompile defines the precompiled contract for ERC-20. +type Precompile struct { + cmn.Precompile + denom string + transferKeeper transferkeeper.Keeper + // BankKeeper is a public field so that the werc20 precompile can use it. + BankKeeper bankkeeper.Keeper + assetKeep assetkeeper.Keeper +} + +// NewPrecompile creates a new ERC-20 Precompile instance as a +// PrecompiledContract interface. +func NewPrecompile( + denom string, + address common.Address, + bankKeeper bankkeeper.Keeper, + authzKeeper authzkeeper.Keeper, + transferKeeper transferkeeper.Keeper, + assetKeeper assetkeeper.Keeper, +) (*Precompile, error) { + newABI, err := cmn.LoadABI(f, abiPath) + if err != nil { + return nil, err + } + + p := &Precompile{ + Precompile: cmn.Precompile{ + ABI: newABI, + AuthzKeeper: authzKeeper, + ApprovalExpiration: cmn.DefaultExpirationDuration, + KvGasConfig: storetypes.GasConfig{}, + TransientKVGasConfig: storetypes.GasConfig{}, + }, + BankKeeper: bankKeeper, + transferKeeper: transferKeeper, + assetKeep: assetKeeper, + denom: denom, + } + // Address defines the address of the ERC-20 precompile contract. + p.SetAddress(address) + return p, nil +} + +// RequiredGas calculates the contract gas used for the +func (p Precompile) RequiredGas(input []byte) uint64 { + // NOTE: This check avoid panicking when trying to decode the method ID + if len(input) < 4 { + return 0 + } + + methodID := input[:4] + method, err := p.MethodById(methodID) + if err != nil { + return 0 + } + + // TODO: these values were obtained from Remix using the ERC20.sol from OpenZeppelin. + // We should execute the transactions using the ERC20MinterBurnerDecimals.sol from Evmos testnet + // to ensure parity in the values. + switch method.Name { + // ERC-20 transactions + case TransferMethod: + return GasTransfer + case TransferFromMethod: + return GasTransfer + case auth.ApproveMethod: + return GasApprove + case auth.IncreaseAllowanceMethod: + return GasIncreaseAllowance + case auth.DecreaseAllowanceMethod: + return GasDecreaseAllowance + // ERC-20 queries + case NameMethod: + return GasName + case SymbolMethod: + return GasSymbol + case DecimalsMethod: + return GasDecimals + case TotalSupplyMethod: + return GasTotalSupply + case BalanceOfMethod: + return GasBalanceOf + case auth.AllowanceMethod: + return GasAllowance + default: + return 0 + } +} + +// Run executes the precompiled contract ERC-20 methods defined in the ABI. +func (p Precompile) Run(evm *vm.EVM, contract *vm.Contract, readOnly bool) (bz []byte, err error) { + // ERC20 precompiles cannot receive funds because they are not managed by an + // EOA and will not be possible to recover funds sent to an instance of + // them.This check is a safety measure because currently funds cannot be + // received due to the lack of a fallback handler. + if value := contract.Value(); value.Sign() == 1 { + return nil, fmt.Errorf(ErrCannotReceiveFunds, contract.Value().String()) + } + + ctx, stateDB, snapshot, method, initialGas, args, err := p.RunSetup(evm, contract, readOnly, p.IsTransaction) + if err != nil { + return nil, err + } + + // This handles any out of gas errors that may occur during the execution of a precompile tx or query. + // It avoids panics and returns the out of gas error so the EVM can continue gracefully. + defer cmn.HandleGasError(ctx, contract, initialGas, &err)() + + bz, err = p.HandleMethod(ctx, contract, stateDB, method, args) + if err != nil { + return nil, err + } + + cost := ctx.GasMeter().GasConsumed() - initialGas + + if !contract.UseGas(cost) { + return nil, vm.ErrOutOfGas + } + if err := p.AddJournalEntries(stateDB, snapshot); err != nil { + return nil, err + } + return bz, nil +} + +// IsTransaction checks if the given method name corresponds to a transaction or query. +func (Precompile) IsTransaction(method *abi.Method) bool { + switch method.Name { + case TransferMethod, + TransferFromMethod, + auth.ApproveMethod, + auth.IncreaseAllowanceMethod, + auth.DecreaseAllowanceMethod: + return true + default: + return false + } +} + +// HandleMethod handles the execution of each of the ERC-20 methods. +func (p *Precompile) HandleMethod( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + args []interface{}, +) (bz []byte, err error) { + tm, err := p.assetKeep.GetTokenManager(ctx, p.denom) + if err != nil { + return nil, err + } + allowedMethods := tm.ExtensionsList + if err != nil { + return nil, err + } + switch method.Name { + // ERC-20 transactions + case TransferMethod: + bz, err = p.Transfer(ctx, contract, stateDB, method, args) + case TransferFromMethod: + bz, err = p.TransferFrom(ctx, contract, stateDB, method, args) + case MintMethod: + if !slices.Contains(allowedMethods, MintMethod) { + return nil, fmt.Errorf("method %s is not supported", MintMethod) + } + bz, err = p.Mint(ctx, contract, stateDB, method, args) + case BurnMethod: + if !slices.Contains(allowedMethods, BurnMethod) { + return nil, fmt.Errorf("method %s is not supported", BurnMethod) + } + bz, err = p.Burn(ctx, contract, stateDB, method, args) + case BurnFromMethod: + if !slices.Contains(allowedMethods, BurnFromMethod) { + return nil, fmt.Errorf("method %s is not supported", BurnFromMethod) + } + bz, err = p.BurnFrom(ctx, contract, stateDB, method, args) + case FreezeMethod: + if !slices.Contains(allowedMethods, FreezeMethod) { + return nil, fmt.Errorf("method %s is not supported", FreezeMethod) + } + bz, err = p.Freeze(ctx, contract, stateDB, method, args) + case auth.ApproveMethod: + bz, err = p.Approve(ctx, contract, stateDB, method, args) + case auth.IncreaseAllowanceMethod: + bz, err = p.IncreaseAllowance(ctx, contract, stateDB, method, args) + case auth.DecreaseAllowanceMethod: + bz, err = p.DecreaseAllowance(ctx, contract, stateDB, method, args) + // ERC-20 queries + case NameMethod: + bz, err = p.Name(ctx, contract, stateDB, method, args) + case SymbolMethod: + bz, err = p.Symbol(ctx, contract, stateDB, method, args) + case DecimalsMethod: + bz, err = p.Decimals(ctx, contract, stateDB, method, args) + case TotalSupplyMethod: + bz, err = p.TotalSupply(ctx, contract, stateDB, method, args) + case BalanceOfMethod: + bz, err = p.BalanceOf(ctx, contract, stateDB, method, args) + case auth.AllowanceMethod: + bz, err = p.Allowance(ctx, contract, stateDB, method, args) + default: + return nil, fmt.Errorf(cmn.ErrUnknownMethod, method.Name) + } + + return bz, err +} diff --git a/precompiles/erc20/errors.go b/precompiles/erc20/errors.go new file mode 100644 index 00000000..1282505b --- /dev/null +++ b/precompiles/erc20/errors.go @@ -0,0 +1,98 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package erc20 + +import ( + "errors" + "strings" + + "github.com/cosmos/cosmos-sdk/x/authz" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/crypto" + "github.com/evmos/os/ibc" + cmn "github.com/evmos/os/precompiles/common" + "github.com/evmos/os/x/evm/core/vm" + evmtypes "github.com/evmos/os/x/evm/types" +) + +// Errors that have formatted information are defined here as a string. +const ( + ErrIntegerOverflow = "amount %s causes integer overflow" + ErrInvalidOwner = "invalid from address: %s" + ErrInvalidReceiver = "invalid to address: %s" + ErrNoAllowanceForToken = "allowance for token %s does not exist" + ErrSubtractMoreThanAllowance = "subtracted value cannot be greater than existing allowance for denom %s: %s > %s" + ErrCannotReceiveFunds = "cannot receive funds, received: %s" +) + +var ( + // errorSignature are the prefix bytes for the hex-encoded reason string. See UnpackRevert in ABI implementation in Geth. + errorSignature = crypto.Keccak256([]byte("Error(string)")) + + // Precompile errors + ErrDecreaseNonPositiveValue = errors.New("cannot decrease allowance with non-positive values") + ErrIncreaseNonPositiveValue = errors.New("cannot increase allowance with non-positive values") + ErrNegativeAmount = errors.New("cannot approve negative values") + ErrSpenderIsOwner = errors.New("spender cannot be the owner") + + // ERC20 errors + ErrDecreasedAllowanceBelowZero = errors.New("ERC20: decreased allowance below zero") + ErrInsufficientAllowance = errors.New("ERC20: insufficient allowance") + ErrTransferAmountExceedsBalance = errors.New("ERC20: transfer amount exceeds balance") +) + +// BuildExecRevertedErr returns a mocked error that should align with the +// behavior of the original ERC20 Solidity implementation. +// +// FIXME: This is not yet producing the correct reason bytes. Will fix on a follow up PR. +func BuildExecRevertedErr(reason string) (error, error) { + // This is reverse-engineering the ABI encoding of the revert reason. + typ, err := abi.NewType("string", "", nil) + if err != nil { + return nil, err + } + + packedReason, err := (abi.Arguments{{Type: typ}}).Pack(reason) + if err != nil { + return nil, errors.New("failed to pack revert reason") + } + + var reasonBytes []byte + reasonBytes = append(reasonBytes, errorSignature...) + reasonBytes = append(reasonBytes, packedReason...) + + return evmtypes.NewExecErrorWithReason(reasonBytes), nil +} + +// ConvertErrToERC20Error is a helper function which maps errors raised by the Cosmos SDK stack +// to the corresponding errors which are raised by an ERC20 contract. +// +// TODO: Create the full RevertError types instead of just the standard error type. +// +// TODO: Return ERC-6093 compliant errors. +func ConvertErrToERC20Error(err error) error { + switch { + case strings.Contains(err.Error(), "spendable balance"): + return ErrTransferAmountExceedsBalance + case strings.Contains(err.Error(), "requested amount is more than spend limit"): + return ErrInsufficientAllowance + case strings.Contains(err.Error(), authz.ErrNoAuthorizationFound.Error()): + return ErrInsufficientAllowance + case strings.Contains(err.Error(), "subtracted value cannot be greater than existing allowance"): + return ErrDecreasedAllowanceBelowZero + case strings.Contains(err.Error(), cmn.ErrIntegerOverflow): + return vm.ErrExecutionReverted + case errors.Is(err, ibc.ErrNoIBCVoucherDenom) || + errors.Is(err, ibc.ErrDenomTraceNotFound) || + strings.Contains(err.Error(), "invalid base denomination") || + strings.Contains(err.Error(), "display denomination not found") || + strings.Contains(err.Error(), "invalid decimals"): + // NOTE: These are the cases when trying to query metadata of a contract, which has no metadata available. + // The ERC20 contract raises an "execution reverted" error, without any further information here, which we + // reproduce (even though it's less verbose than the actual error). + return vm.ErrExecutionReverted + default: + return err + } +} diff --git a/precompiles/erc20/events.go b/precompiles/erc20/events.go new file mode 100644 index 00000000..d8e12172 --- /dev/null +++ b/precompiles/erc20/events.go @@ -0,0 +1,237 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package erc20 + +import ( + "fmt" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/evmos/os/x/evm/core/vm" + + auth "github.com/evmos/os/precompiles/authorization" + cmn "github.com/evmos/os/precompiles/common" +) + +const ( + // EventTypeTransfer defines the event type for the ERC-20 Transfer and TransferFrom transactions. + EventTypeTransfer = "Transfer" + EventTypeMint = "Mint" + EventTypeBurn = "Burn" + EventTypeFreeze = "Freeze" + EventTypeGrantRole = "RoleGranted" + EventTypeRevokeRole = "RoleRevoked" +) + +// EmitTransferEvent creates a new Transfer event emitted on transfer and transferFrom transactions. +func (p Precompile) EmitTransferEvent(ctx sdk.Context, stateDB vm.StateDB, from, to common.Address, value *big.Int) error { + // Prepare the event topics + event := p.ABI.Events[EventTypeTransfer] + topics := make([]common.Hash, 3) + + // The first topic is always the signature of the event. + topics[0] = event.ID + + var err error + topics[1], err = cmn.MakeTopic(from) + if err != nil { + return err + } + + topics[2], err = cmn.MakeTopic(to) + if err != nil { + return err + } + + fmt.Println("event.Inputs", event.Inputs) + + arguments := abi.Arguments{event.Inputs[2]} + packed, err := arguments.Pack(value) + if err != nil { + return err + } + + stateDB.AddLog(ðtypes.Log{ + Address: p.Address(), + Topics: topics, + Data: packed, + BlockNumber: uint64(ctx.BlockHeight()), //nolint:gosec // G115 // block height won't exceed uint64 + }) + + return nil +} + +func (p Precompile) EmitMintEvent(ctx sdk.Context, stateDB vm.StateDB, to common.Address, value *big.Int) error { + // Prepare the event topics + event := p.ABI.Events[EventTypeMint] + topics := make([]common.Hash, 2) + + // The first topic is always the signature of the event. + topics[0] = event.ID + + var err error + topics[1], err = cmn.MakeTopic(to) + if err != nil { + return err + } + + fmt.Println("event.Inputs", event.Inputs) + + arguments := abi.Arguments{event.Inputs[1]} + packed, err := arguments.Pack(value) + if err != nil { + return err + } + + stateDB.AddLog(ðtypes.Log{ + Address: p.Address(), + Topics: topics, + Data: packed, + BlockNumber: uint64(ctx.BlockHeight()), //nolint:gosec // G115 // block height won't exceed uint64 + }) + + return nil +} + +func (p Precompile) EmitBurnEvent(ctx sdk.Context, stateDB vm.StateDB, from common.Address, value *big.Int) error { + // Prepare the event topics + event := p.ABI.Events[EventTypeBurn] + topics := make([]common.Hash, 2) + + // The first topic is always the signature of the event. + topics[0] = event.ID + + var err error + topics[1], err = cmn.MakeTopic(from) + if err != nil { + return err + } + + arguments := abi.Arguments{event.Inputs[0]} + packed, err := arguments.Pack(value) + if err != nil { + return err + } + + stateDB.AddLog(ðtypes.Log{ + Address: p.Address(), + Topics: topics, + Data: packed, + BlockNumber: uint64(ctx.BlockHeight()), //nolint:gosec // G115 // block height won't exceed uint64 + }) + + return nil +} + +func (p Precompile) EmitFreezeEvent(ctx sdk.Context, stateDB vm.StateDB, to common.Address) error { + // Prepare the event topics + event := p.ABI.Events[EventTypeFreeze] + topics := make([]common.Hash, 2) + + // The first topic is always the signature of the event. + topics[0] = event.ID + + var err error + topics[1], err = cmn.MakeTopic(to) + if err != nil { + return err + } + + stateDB.AddLog(ðtypes.Log{ + Address: p.Address(), + Topics: topics, + BlockNumber: uint64(ctx.BlockHeight()), //nolint:gosec // G115 // block height won't exceed uint64 + }) + + return nil +} + +func (p Precompile) EmitGrantRoleEvent(ctx sdk.Context, stateDB vm.StateDB, sender, to common.Address, role int32) error { + // Prepare the event topics + event := p.ABI.Events[EventTypeGrantRole] + topics := make([]common.Hash, 1) + + // The first topic is always the signature of the event. + topics[0] = event.ID + + arguments := abi.Arguments{event.Inputs[0], event.Inputs[1], event.Inputs[2]} + packed, err := arguments.Pack(sender, to, role) + if err != nil { + return err + } + + stateDB.AddLog(ðtypes.Log{ + Address: p.Address(), + Topics: topics, + Data: packed, + BlockNumber: uint64(ctx.BlockHeight()), //nolint:gosec // G115 // block height won't exceed uint64 + }) + + return nil +} + +func (p Precompile) EmitRevokeRoleEvent(ctx sdk.Context, stateDB vm.StateDB, sender, to common.Address, role int32) error { + // Prepare the event topics + event := p.ABI.Events[EventTypeRevokeRole] + topics := make([]common.Hash, 1) + + // The first topic is always the signature of the event. + topics[0] = event.ID + + arguments := abi.Arguments{event.Inputs[0], event.Inputs[1], event.Inputs[2]} + packed, err := arguments.Pack(sender, to, role) + if err != nil { + return err + } + + stateDB.AddLog(ðtypes.Log{ + Address: p.Address(), + Topics: topics, + Data: packed, + BlockNumber: uint64(ctx.BlockHeight()), //nolint:gosec // G115 // block height won't exceed uint64 + }) + + return nil +} + +// EmitApprovalEvent creates a new approval event emitted on Approve, IncreaseAllowance +// and DecreaseAllowance transactions. +func (p Precompile) EmitApprovalEvent(ctx sdk.Context, stateDB vm.StateDB, owner, spender common.Address, value *big.Int) error { + // Prepare the event topics + event := p.ABI.Events[auth.EventTypeApproval] + topics := make([]common.Hash, 3) + + // The first topic is always the signature of the event. + topics[0] = event.ID + + var err error + topics[1], err = cmn.MakeTopic(owner) + if err != nil { + return err + } + + topics[2], err = cmn.MakeTopic(spender) + if err != nil { + return err + } + + arguments := abi.Arguments{event.Inputs[2]} + packed, err := arguments.Pack(value) + if err != nil { + return err + } + + stateDB.AddLog(ðtypes.Log{ + Address: p.Address(), + Topics: topics, + Data: packed, + BlockNumber: uint64(ctx.BlockHeight()), //nolint:gosec // G115 // block height won't exceed uint64 + }) + + return nil +} diff --git a/precompiles/erc20/query.go b/precompiles/erc20/query.go new file mode 100644 index 00000000..06e599fa --- /dev/null +++ b/precompiles/erc20/query.go @@ -0,0 +1,249 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package erc20 + +import ( + "bytes" + "fmt" + "math" + "math/big" + "strings" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/evmos/os/ibc" + auth "github.com/evmos/os/precompiles/authorization" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/evmos/os/x/evm/core/vm" +) + +const ( + // NameMethod defines the ABI method name for the ERC-20 Name + // query. + NameMethod = "name" + // SymbolMethod defines the ABI method name for the ERC-20 Symbol + // query. + SymbolMethod = "symbol" + // DecimalsMethod defines the ABI method name for the ERC-20 Decimals + // query. + DecimalsMethod = "decimals" + // TotalSupplyMethod defines the ABI method name for the ERC-20 TotalSupply + // query. + TotalSupplyMethod = "totalSupply" + // BalanceOfMethod defines the ABI method name for the ERC-20 BalanceOf + // query. + BalanceOfMethod = "balanceOf" +) + +// Name returns the name of the token. If the token metadata is registered in the +// bank module, it returns its name. Otherwise, it returns the base denomination of +// the token capitalized (e.g. uatom -> Atom). +func (p Precompile) Name( + ctx sdk.Context, + _ *vm.Contract, + _ vm.StateDB, + method *abi.Method, + _ []interface{}, +) ([]byte, error) { + metadata, found := p.BankKeeper.GetDenomMetaData(ctx, p.denom) + if found { + return method.Outputs.Pack(metadata.Name) + } + + baseDenom, err := p.getBaseDenomFromIBCVoucher(ctx, p.denom) + if err != nil { + return nil, ConvertErrToERC20Error(err) + } + + name := strings.ToUpper(string(baseDenom[1])) + baseDenom[2:] + return method.Outputs.Pack(name) +} + +// Symbol returns the symbol of the token. If the token metadata is registered in the +// bank module, it returns its symbol. Otherwise, it returns the base denomination of +// the token in uppercase (e.g. uatom -> ATOM). +func (p Precompile) Symbol( + ctx sdk.Context, + _ *vm.Contract, + _ vm.StateDB, + method *abi.Method, + _ []interface{}, +) ([]byte, error) { + metadata, found := p.BankKeeper.GetDenomMetaData(ctx, p.denom) + if found { + return method.Outputs.Pack(metadata.Symbol) + } + + baseDenom, err := p.getBaseDenomFromIBCVoucher(ctx, p.denom) + if err != nil { + return nil, ConvertErrToERC20Error(err) + } + + symbol := strings.ToUpper(baseDenom[1:]) + return method.Outputs.Pack(symbol) +} + +// Decimals returns the decimals places of the token. If the token metadata is registered in the +// bank module, it returns the display denomination exponent. Otherwise, it infers the decimal +// value from the first character of the base denomination (e.g. uatom -> 6). +func (p Precompile) Decimals( + ctx sdk.Context, + _ *vm.Contract, + _ vm.StateDB, + method *abi.Method, + _ []interface{}, +) ([]byte, error) { + metadata, found := p.BankKeeper.GetDenomMetaData(ctx, p.denom) + if !found { + denomTrace, err := ibc.GetDenomTrace(p.transferKeeper, ctx, p.denom) + if err != nil { + return nil, ConvertErrToERC20Error(err) + } + + // we assume the decimal from the first character of the denomination + decimals, err := ibc.DeriveDecimalsFromDenom(denomTrace.BaseDenom) + if err != nil { + return nil, ConvertErrToERC20Error(err) + } + return method.Outputs.Pack(decimals) + } + + var ( + decimals uint32 + displayFound bool + ) + for i := len(metadata.DenomUnits) - 1; i >= 0; i-- { + if metadata.DenomUnits[i].Denom == metadata.Display { + decimals = metadata.DenomUnits[i].Exponent + displayFound = true + break + } + } + + if !displayFound { + return nil, ConvertErrToERC20Error(fmt.Errorf( + "display denomination not found for denom: %q", + p.denom, + )) + } + + if decimals > math.MaxUint8 { + return nil, ConvertErrToERC20Error(fmt.Errorf( + "uint8 overflow: invalid decimals: %d", + decimals, + )) + } + + return method.Outputs.Pack(uint8(decimals)) //#nosec G115 // we are checking for overflow above +} + +// TotalSupply returns the amount of tokens in existence. It fetches the supply +// of the coin from the bank keeper and returns zero if not found. +func (p Precompile) TotalSupply( + ctx sdk.Context, + _ *vm.Contract, + _ vm.StateDB, + method *abi.Method, + _ []interface{}, +) ([]byte, error) { + supply := p.BankKeeper.GetSupply(ctx, p.denom) + + return method.Outputs.Pack(supply.Amount.BigInt()) +} + +// BalanceOf returns the amount of tokens owned by account. It fetches the balance +// of the coin from the bank keeper and returns zero if not found. +func (p Precompile) BalanceOf( + ctx sdk.Context, + _ *vm.Contract, + _ vm.StateDB, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + account, err := ParseBalanceOfArgs(args) + if err != nil { + return nil, err + } + + balance := p.BankKeeper.GetBalance(ctx, account.Bytes(), p.denom) + + return method.Outputs.Pack(balance.Amount.BigInt()) +} + +// Allowance returns the remaining allowance of a spender to the contract by +// checking the existence of a bank SendAuthorization. +func (p Precompile) Allowance( + ctx sdk.Context, + _ *vm.Contract, + _ vm.StateDB, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + owner, spender, err := ParseAllowanceArgs(args) + if err != nil { + return nil, err + } + + // NOTE: In case the allowance is queried by the owner, we return the max uint256 value, which + // resembles an infinite allowance. + if bytes.Equal(owner.Bytes(), spender.Bytes()) { + return method.Outputs.Pack(abi.MaxUint256) + } + + _, _, allowance, err := GetAuthzExpirationAndAllowance(p.AuthzKeeper, ctx, spender, owner, p.denom) + if err != nil { + // NOTE: We are not returning the error here, because we want to align the behavior with + // standard ERC20 smart contracts, which return zero if an allowance is not found. + allowance = common.Big0 + } + + return method.Outputs.Pack(allowance) +} + +// GetAuthzExpirationAndAllowance returns the authorization, its expiration as well as the amount of denom +// that the grantee is allowed to spend on behalf of the granter. +func GetAuthzExpirationAndAllowance( + authzKeeper authzkeeper.Keeper, + ctx sdk.Context, + grantee, granter common.Address, + denom string, +) (authz.Authorization, *time.Time, *big.Int, error) { + authorization, expiration, err := auth.CheckAuthzExists(ctx, authzKeeper, grantee, granter, SendMsgURL) + if err != nil { + return nil, nil, common.Big0, err + } + + sendAuth, ok := authorization.(*banktypes.SendAuthorization) + if !ok { + return nil, nil, common.Big0, fmt.Errorf( + "expected authorization to be a %T", banktypes.SendAuthorization{}, + ) + } + + allowance := sendAuth.SpendLimit.AmountOfNoDenomValidation(denom) + return authorization, expiration, allowance.BigInt(), nil +} + +// getBaseDenomFromIBCVoucher returns the base denomination from the given IBC voucher denomination. +func (p Precompile) getBaseDenomFromIBCVoucher(ctx sdk.Context, denom string) (string, error) { + // Infer the denomination name from the coin denomination base denom + denomTrace, err := ibc.GetDenomTrace(p.transferKeeper, ctx, denom) + if err != nil { + // FIXME: return 'not supported' (same error as when you call the method on an ERC20.sol) + return "", err + } + + // safety check + if len(denomTrace.BaseDenom) < 3 { + // FIXME: return not supported (same error as when you call the method on an ERC20.sol) + return "", fmt.Errorf("invalid base denomination; should be at least length 3; got: %q", denomTrace.BaseDenom) + } + + return denomTrace.BaseDenom, nil +} diff --git a/precompiles/erc20/setup_test.go b/precompiles/erc20/setup_test.go new file mode 100644 index 00000000..50650d6c --- /dev/null +++ b/precompiles/erc20/setup_test.go @@ -0,0 +1,62 @@ +package erc20 + +import ( + "testing" + + "github.com/evmos/os/testutil/integration/os/factory" + "github.com/evmos/os/testutil/integration/os/grpc" + testkeyring "github.com/evmos/os/testutil/integration/os/keyring" + "github.com/realiotech/realio-network/testutil/integration/os/network" + "github.com/stretchr/testify/suite" +) + +var s *PrecompileTestSuite + +// PrecompileTestSuite is the implementation of the TestSuite interface for ERC20 precompile +// unit tests. +type PrecompileTestSuite struct { + suite.Suite + + bondDenom string + // tokenDenom is the specific token denomination used in testing the ERC20 precompile. + // This denomination is used to instantiate the precompile. + tokenDenom string + network *network.UnitTestNetwork + factory factory.TxFactory + grpcHandler grpc.Handler + keyring testkeyring.Keyring + + precompile *Precompile +} + +func TestPrecompileTestSuite(t *testing.T) { + s = new(PrecompileTestSuite) + suite.Run(t, s) +} + +func (s *PrecompileTestSuite) SetupTest() { + keyring := testkeyring.New(2) + integrationNetwork := network.NewUnitTestNetwork( + network.WithPreFundedAccounts(keyring.GetAllAccAddrs()...), + ) + grpcHandler := grpc.NewIntegrationHandler(integrationNetwork) + txFactory := factory.New(integrationNetwork, grpcHandler) + + ctx := integrationNetwork.GetContext() + sk := integrationNetwork.App.StakingKeeper + bondDenom, err := sk.BondDenom(ctx) + s.Require().NoError(err) + s.Require().NotEmpty(bondDenom, "bond denom cannot be empty") + + s.bondDenom = bondDenom + s.factory = txFactory + s.grpcHandler = grpcHandler + s.keyring = keyring + s.network = integrationNetwork + + // Instantiate the precompile with an exemplary token denomination. + // + // NOTE: This has to be done AFTER assigning the suite fields. + s.tokenDenom = "xmpl" + s.precompile = s.setupERC20Precompile(s.tokenDenom) +} diff --git a/precompiles/erc20/tx.go b/precompiles/erc20/tx.go new file mode 100644 index 00000000..40457080 --- /dev/null +++ b/precompiles/erc20/tx.go @@ -0,0 +1,353 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package erc20 + +import ( + "fmt" + "math/big" + + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + cmn "github.com/evmos/os/precompiles/common" + "github.com/evmos/os/x/evm/core/vm" + evmtypes "github.com/evmos/os/x/evm/types" + assettypes "github.com/realiotech/realio-network/x/asset/types" +) + +const ( + // TransferMethod defines the ABI method name for the ERC-20 transfer + // transaction. + TransferMethod = "transfer" + // TransferFromMethod defines the ABI method name for the ERC-20 transferFrom + // transaction. + TransferFromMethod = "transferFrom" + + BurnMethod = "burn" + + BurnFromMethod = "burnFrom" + + MintMethod = "mint" + + FreezeMethod = "freeze" +) + +// SendMsgURL defines the authorization type for MsgSend +var SendMsgURL = sdk.MsgTypeURL(&banktypes.MsgSend{}) + +// Transfer executes a direct transfer from the caller address to the +// destination address. +func (p *Precompile) Transfer( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + from := contract.CallerAddress + to, amount, err := ParseTransferArgs(args) + if err != nil { + return nil, err + } + + return p.transfer(ctx, contract, stateDB, method, from, to, amount) +} + +// TransferFrom executes a transfer on behalf of the specified from address in +// the call data to the destination address. +func (p *Precompile) TransferFrom( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + from, to, amount, err := ParseTransferFromArgs(args) + if err != nil { + return nil, err + } + + return p.transfer(ctx, contract, stateDB, method, from, to, amount) +} + +// transfer is a common function that handles transfers for the ERC-20 Transfer +// and TransferFrom methods. It executes a bank Send message if the spender is +// the sender of the transfer, otherwise it executes an authorization. +func (p *Precompile) transfer( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + from, to common.Address, + amount *big.Int, +) (data []byte, err error) { + coins := sdk.Coins{{Denom: p.denom, Amount: math.NewIntFromBigInt(amount)}} + + msg := banktypes.NewMsgSend(from.Bytes(), to.Bytes(), coins) + + if err = msg.Amount.Validate(); err != nil { + return nil, err + } + + // Check if from is freezed + freezed := p.assetKeep.IsFreezed(ctx, from) + if freezed { + return nil, fmt.Errorf("address %s already be freezed", from.String()) + } + + isTransferFrom := method.Name == TransferFromMethod + owner := sdk.AccAddress(from.Bytes()) + spenderAddr := contract.CallerAddress + spender := sdk.AccAddress(spenderAddr.Bytes()) // aka. grantee + ownerIsSpender := spender.Equals(owner) + + var prevAllowance *big.Int + if ownerIsSpender { + msgSrv := bankkeeper.NewMsgServerImpl(p.BankKeeper) + _, err = msgSrv.Send(ctx, msg) + } else { + _, _, prevAllowance, err = GetAuthzExpirationAndAllowance(p.AuthzKeeper, ctx, spenderAddr, from, p.denom) + if err != nil { + return nil, ConvertErrToERC20Error(errorsmod.Wrap(err, authz.ErrNoAuthorizationFound.Error())) + } + + _, err = p.AuthzKeeper.DispatchActions(ctx, spender, []sdk.Msg{msg}) + } + + if err != nil { + err = ConvertErrToERC20Error(err) + // This should return an error to avoid the contract from being executed and an event being emitted + return nil, err + } + + evmDenom := evmtypes.GetEVMCoinDenom() + if p.denom == evmDenom { + convertedAmount := evmtypes.ConvertAmountTo18DecimalsBigInt(amount) + p.SetBalanceChangeEntries(cmn.NewBalanceChangeEntry(from, convertedAmount, cmn.Sub), + cmn.NewBalanceChangeEntry(to, convertedAmount, cmn.Add)) + } + + if err = p.EmitTransferEvent(ctx, stateDB, from, to, amount); err != nil { + return nil, err + } + + // NOTE: if it's a direct transfer, we return here but if used through transferFrom, + // we need to emit the approval event with the new allowance. + if !isTransferFrom { + return method.Outputs.Pack(true) + } + + var newAllowance *big.Int + if ownerIsSpender { + // NOTE: in case the spender is the owner we emit an approval event with + // the maxUint256 value. + newAllowance = abi.MaxUint256 + } else { + newAllowance = new(big.Int).Sub(prevAllowance, amount) + } + + if err = p.EmitApprovalEvent(ctx, stateDB, from, spenderAddr, newAllowance); err != nil { + return nil, err + } + + return method.Outputs.Pack(true) +} + +func (p *Precompile) Mint( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + to, amount, err := ParseMintArgs(args) + if err != nil { + return nil, err + } + + return p.mint(ctx, contract, stateDB, method, to, amount) +} + +func (p *Precompile) mint( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + to common.Address, + amount *big.Int, +) (data []byte, err error) { + + minter := contract.CallerAddress + havePerm, err := p.assetKeep.IsTokenManager(ctx, p.denom, minter.Bytes()) + fmt.Println("have perm", havePerm, err) + if err != nil || !havePerm { + return nil, fmt.Errorf("sender is not token manager") + } + + mintToAddr := sdk.AccAddress(to.Bytes()) + + coins := sdk.Coins{{Denom: p.denom, Amount: math.NewIntFromBigInt(amount)}} + + // Check if new supply exceed max supply + + tm, err := p.assetKeep.GetTokenManager(ctx, p.denom) + if err != nil { + return nil, err + } + maxSupply := tm.MaxSupply + currentSupply := p.BankKeeper.GetSupply(ctx, p.denom).Amount + newSupply := currentSupply.Add(math.NewIntFromBigInt(amount)) + if newSupply.GT(maxSupply) { + return nil, ConvertErrToERC20Error(fmt.Errorf("Exceed max supply, expected: %d, got: %d", maxSupply, newSupply)) + } + + // Mint coins to asset module then transfer to minter addr + err = p.BankKeeper.MintCoins(ctx, assettypes.ModuleName, coins) + if err != nil { + return nil, ConvertErrToERC20Error(err) + } + + err = p.BankKeeper.SendCoinsFromModuleToAccount(ctx, assettypes.ModuleName, mintToAddr, coins) + if err != nil { + return nil, ConvertErrToERC20Error(err) + } + + evmDenom := evmtypes.GetEVMCoinDenom() + if p.denom == evmDenom { + convertedAmount := evmtypes.ConvertAmountTo18DecimalsBigInt(amount) + p.SetBalanceChangeEntries(cmn.NewBalanceChangeEntry(to, convertedAmount, cmn.Add)) + } + + if err = p.EmitMintEvent(ctx, stateDB, to, amount); err != nil { + return nil, err + } + + return method.Outputs.Pack(true) +} + +func (p *Precompile) Burn( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + from := contract.CallerAddress + amount, err := ParseBurnArgs(args) + if err != nil { + return nil, err + } + + return p.burn(ctx, contract, stateDB, method, from, amount) +} + +func (p *Precompile) BurnFrom( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + from, amount, err := ParseBurnFromArgs(args) + if err != nil { + return nil, err + } + + return p.burn(ctx, contract, stateDB, method, from, amount) +} + +func (p *Precompile) burn( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + from common.Address, + amount *big.Int, +) (data []byte, err error) { + + minter := contract.CallerAddress + havePerm, err := p.assetKeep.IsTokenManager(ctx, p.denom, minter.Bytes()) + if err != nil || !havePerm { + return nil, fmt.Errorf("sender is not token manager") + } + + burnFromAddr := sdk.AccAddress(from.Bytes()) + + coins := sdk.Coins{{Denom: p.denom, Amount: math.NewIntFromBigInt(amount)}} + + // Transfer to asset module then burn + err = p.BankKeeper.SendCoinsFromAccountToModule(ctx, burnFromAddr, assettypes.ModuleName, coins) + if err != nil { + return nil, ConvertErrToERC20Error(err) + } + + err = p.BankKeeper.BurnCoins(ctx, assettypes.ModuleName, coins) + if err != nil { + return nil, ConvertErrToERC20Error(err) + } + + evmDenom := evmtypes.GetEVMCoinDenom() + if p.denom == evmDenom { + convertedAmount := evmtypes.ConvertAmountTo18DecimalsBigInt(amount) + p.SetBalanceChangeEntries(cmn.NewBalanceChangeEntry(from, convertedAmount, cmn.Add)) + } + + if err = p.EmitBurnEvent(ctx, stateDB, from, amount); err != nil { + return nil, err + } + + return method.Outputs.Pack(true) +} + +func (p *Precompile) Freeze( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + from, err := ParseFreezeArgs(args) + if err != nil { + return nil, err + } + + return p.freeze(ctx, contract, stateDB, method, from) +} + +func (p *Precompile) freeze( + ctx sdk.Context, + contract *vm.Contract, + stateDB vm.StateDB, + method *abi.Method, + to common.Address, +) (data []byte, err error) { + + sender := contract.CallerAddress + havePerm, err := p.assetKeep.IsTokenManager(ctx, p.denom, sender.Bytes()) + if err != nil || !havePerm { + return nil, fmt.Errorf("sender is not token manager") + } + + // Check if addr already freeze + exist := p.assetKeep.IsFreezed(ctx, to) + if exist { + return nil, fmt.Errorf("address already freezed") + } + + err = p.assetKeep.SetFreezeAddress(ctx, to) + if err != nil { + return nil, err + } + + if err = p.EmitFreezeEvent(ctx, stateDB, to); err != nil { + return nil, err + } + + return method.Outputs.Pack(true) +} diff --git a/precompiles/erc20/tx_test.go b/precompiles/erc20/tx_test.go new file mode 100644 index 00000000..e8924a6f --- /dev/null +++ b/precompiles/erc20/tx_test.go @@ -0,0 +1,730 @@ +package erc20 + +import ( + "fmt" + "math/big" + "time" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/evmos/os/precompiles/testutil" + "github.com/evmos/os/testutil/integration/os/keyring" + erc20types "github.com/evmos/os/x/erc20/types" + "github.com/evmos/os/x/evm/core/vm" + "github.com/evmos/os/x/evm/statedb" + utiltx "github.com/realiotech/realio-network/testutil/tx" + assettypes "github.com/realiotech/realio-network/x/asset/types" +) + +var ( + tokenDenom = "xmpl" + // XMPLCoin is a dummy coin used for testing purposes. + XMPLCoin = sdk.NewCoins(sdk.NewInt64Coin(tokenDenom, 1e18)) + // toAddr is a dummy address used for testing purposes. + toAddr = utiltx.GenerateAddress() + // toAddr is a dummy address used for testing purposes. + fromAddr = utiltx.GenerateAddress() +) + +func (s *PrecompileTestSuite) TestTransfer() { + method := s.precompile.Methods[TransferMethod] + // fromAddr is the address of the keyring account used for testing. + fromAddr := s.keyring.GetKey(0).Addr + testcases := []struct { + name string + malleate func() []interface{} + postCheck func() + expErr bool + errContains string + }{ + { + "fail - negative amount", + func() []interface{} { + return []interface{}{toAddr, big.NewInt(-1)} + }, + func() {}, + true, + "coin -1xmpl amount is not positive", + }, + { + "fail - invalid to address", + func() []interface{} { + return []interface{}{"", big.NewInt(100)} + }, + func() {}, + true, + "invalid to address", + }, + { + "fail - invalid amount", + func() []interface{} { + return []interface{}{toAddr, ""} + }, + func() {}, + true, + "invalid amount", + }, + { + "fail - not enough balance", + func() []interface{} { + return []interface{}{toAddr, big.NewInt(2e18)} + }, + func() {}, + true, + ErrTransferAmountExceedsBalance.Error(), + }, + { + "pass", + func() []interface{} { + return []interface{}{toAddr, big.NewInt(100)} + }, + func() { + toAddrBalance := s.network.App.BankKeeper.GetBalance(s.network.GetContext(), toAddr.Bytes(), tokenDenom) + s.Require().Equal(big.NewInt(100), toAddrBalance.Amount.BigInt(), "expected toAddr to have 100 XMPL") + }, + false, + "", + }, + } + + for _, tc := range testcases { + s.Run(tc.name, func() { + s.SetupTest() + stateDB := s.network.GetStateDB() + + var contract *vm.Contract + contract, ctx := testutil.NewPrecompileContract(s.T(), s.network.GetContext(), fromAddr, s.precompile, 0) + + // Mint some coins to the module account and then send to the from address + err := s.network.App.BankKeeper.MintCoins(s.network.GetContext(), erc20types.ModuleName, XMPLCoin) + s.Require().NoError(err, "failed to mint coins") + err = s.network.App.BankKeeper.SendCoinsFromModuleToAccount(s.network.GetContext(), erc20types.ModuleName, fromAddr.Bytes(), XMPLCoin) + s.Require().NoError(err, "failed to send coins from module to account") + + _, err = s.precompile.Transfer(ctx, contract, stateDB, &method, tc.malleate()) + if tc.expErr { + s.Require().Error(err, "expected transfer transaction to fail") + s.Require().Contains(err.Error(), tc.errContains, "expected transfer transaction to fail with specific error") + } else { + s.Require().NoError(err, "expected transfer transaction succeeded") + tc.postCheck() + } + }) + } +} + +func (s *PrecompileTestSuite) TestTransferFrom() { + var ( + ctx sdk.Context + stDB *statedb.StateDB + ) + method := s.precompile.Methods[TransferFromMethod] + // owner of the tokens + owner := s.keyring.GetKey(0) + // spender of the tokens + spender := s.keyring.GetKey(1) + + testcases := []struct { + name string + malleate func() []interface{} + postCheck func() + expErr bool + errContains string + }{ + { + "fail - negative amount", + func() []interface{} { + return []interface{}{owner.Addr, toAddr, big.NewInt(-1)} + }, + func() {}, + true, + "coin -1xmpl amount is not positive", + }, + { + "fail - invalid from address", + func() []interface{} { + return []interface{}{"", toAddr, big.NewInt(100)} + }, + func() {}, + true, + "invalid from address", + }, + { + "fail - invalid to address", + func() []interface{} { + return []interface{}{owner.Addr, "", big.NewInt(100)} + }, + func() {}, + true, + "invalid to address", + }, + { + "fail - invalid amount", + func() []interface{} { + return []interface{}{owner.Addr, toAddr, ""} + }, + func() {}, + true, + "invalid amount", + }, + { + "fail - not enough allowance", + func() []interface{} { + return []interface{}{owner.Addr, toAddr, big.NewInt(100)} + }, + func() {}, + true, + ErrInsufficientAllowance.Error(), + }, + { + "fail - owner was freezed", + func() []interface{} { + err := s.network.App.AssetKeeper.SetFreezeAddress(ctx, owner.Addr) + s.Require().NoError(err) + return []interface{}{owner.Addr, toAddr, big.NewInt(100)} + }, + func() {}, + true, + "already be freezed", + }, + { + "fail - not enough balance", + func() []interface{} { + expiration := time.Now().Add(time.Hour) + err := s.network.App.AuthzKeeper.SaveGrant( + ctx, + spender.AccAddr, + owner.AccAddr, + &banktypes.SendAuthorization{SpendLimit: sdk.Coins{sdk.Coin{Denom: s.tokenDenom, Amount: math.NewInt(5e18)}}}, + &expiration, + ) + s.Require().NoError(err, "failed to save grant") + + return []interface{}{owner.Addr, toAddr, big.NewInt(2e18)} + }, + func() {}, + true, + ErrTransferAmountExceedsBalance.Error(), + }, + { + "pass - spend on behalf of other account", + func() []interface{} { + expiration := time.Now().Add(time.Hour) + err := s.network.App.AuthzKeeper.SaveGrant( + ctx, + spender.AccAddr, + owner.AccAddr, + &banktypes.SendAuthorization{SpendLimit: sdk.Coins{sdk.Coin{Denom: tokenDenom, Amount: math.NewInt(300)}}}, + &expiration, + ) + s.Require().NoError(err, "failed to save grant") + + return []interface{}{owner.Addr, toAddr, big.NewInt(100)} + }, + func() { + toAddrBalance := s.network.App.BankKeeper.GetBalance(ctx, toAddr.Bytes(), tokenDenom) + s.Require().Equal(big.NewInt(100), toAddrBalance.Amount.BigInt(), "expected toAddr to have 100 XMPL") + }, + false, + "", + }, + { + "fail - spend on behalf of freezed account", + func() []interface{} { + expiration := time.Now().Add(time.Hour) + err := s.network.App.AuthzKeeper.SaveGrant( + ctx, + spender.AccAddr, + owner.AccAddr, + &banktypes.SendAuthorization{SpendLimit: sdk.Coins{sdk.Coin{Denom: tokenDenom, Amount: math.NewInt(300)}}}, + &expiration, + ) + s.Require().NoError(err, "failed to save grant") + + err = s.network.App.AssetKeeper.SetFreezeAddress(ctx, owner.Addr) + s.Require().NoError(err) + + return []interface{}{owner.Addr, toAddr, big.NewInt(100)} + }, + func() {}, + true, + "already be freezed", + }, + { + "pass - spend on behalf of own account", + func() []interface{} { + // Mint some coins to the module account and then send to the spender address + err := s.network.App.BankKeeper.MintCoins(ctx, erc20types.ModuleName, XMPLCoin) + s.Require().NoError(err, "failed to mint coins") + err = s.network.App.BankKeeper.SendCoinsFromModuleToAccount(ctx, erc20types.ModuleName, spender.AccAddr, XMPLCoin) + s.Require().NoError(err, "failed to send coins from module to account") + + // NOTE: no authorization is necessary to spend on behalf of the same account + return []interface{}{spender.Addr, toAddr, big.NewInt(100)} + }, + func() { + toAddrBalance := s.network.App.BankKeeper.GetBalance(ctx, toAddr.Bytes(), tokenDenom) + s.Require().Equal(big.NewInt(100), toAddrBalance.Amount.BigInt(), "expected toAddr to have 100 XMPL") + }, + false, + "", + }, + } + + for _, tc := range testcases { + s.Run(tc.name, func() { + s.SetupTest() + ctx = s.network.GetContext() + stDB = s.network.GetStateDB() + + var contract *vm.Contract + contract, ctx = testutil.NewPrecompileContract(s.T(), ctx, spender.Addr, s.precompile, 0) + + // Mint some coins to the module account and then send to the from address + err := s.network.App.BankKeeper.MintCoins(ctx, erc20types.ModuleName, XMPLCoin) + s.Require().NoError(err, "failed to mint coins") + err = s.network.App.BankKeeper.SendCoinsFromModuleToAccount(ctx, erc20types.ModuleName, owner.AccAddr, XMPLCoin) + s.Require().NoError(err, "failed to send coins from module to account") + + _, err = s.precompile.TransferFrom(ctx, contract, stDB, &method, tc.malleate()) + if tc.expErr { + s.Require().Error(err, "expected transfer transaction to fail") + s.Require().Contains(err.Error(), tc.errContains, "expected transfer transaction to fail with specific error") + } else { + s.Require().NoError(err, "expected transfer transaction succeeded") + tc.postCheck() + } + }) + } +} + +func (s *PrecompileTestSuite) TestMint() { + method := s.precompile.Methods[MintMethod] + // fromAddr is the address of the keyring account used for testing. + sender := s.keyring.GetKey(0) + invalidSender := s.keyring.GetKey(1) + maxSupply := math.NewInt(200) + testcases := []struct { + name string + malleate func() []interface{} + postCheck func() + expErr bool + errContains string + sender keyring.Key + }{ + { + "fail - negative amount", + func() []interface{} { + return []interface{}{toAddr, big.NewInt(-1)} + }, + func() {}, + true, + "coin -1xmpl amount is not positive", + sender, + }, + { + "fail - invalid to address", + func() []interface{} { + return []interface{}{"", big.NewInt(100)} + }, + func() {}, + true, + "invalid to address", + sender, + }, + { + "fail - invalid amount", + func() []interface{} { + return []interface{}{toAddr, ""} + }, + func() {}, + true, + "invalid amount", + sender, + }, + { + "fail - sender is not manager", + func() []interface{} { + return []interface{}{toAddr, big.NewInt(2e18)} + }, + func() {}, + true, + ErrTransferAmountExceedsBalance.Error(), + invalidSender, + }, + { + "fail - exceed max supply", + func() []interface{} { + return []interface{}{toAddr, big.NewInt(300)} + }, + func() {}, + true, + "Exceed max supply", + invalidSender, + }, + { + "pass", + func() []interface{} { + return []interface{}{toAddr, big.NewInt(100)} + }, + func() { + toAddrBalance := s.network.App.BankKeeper.GetBalance(s.network.GetContext(), toAddr.Bytes(), tokenDenom) + s.Require().Equal(big.NewInt(100), toAddrBalance.Amount.BigInt(), "expected toAddr to have 100 XMPL") + }, + false, + "", + sender, + }, + } + + for _, tc := range testcases { + s.Run(tc.name, func() { + s.SetupTest() + stateDB := s.network.GetStateDB() + + var contract *vm.Contract + contract, ctx := testutil.NewPrecompileContract(s.T(), s.network.GetContext(), tc.sender.Addr, s.precompile, 0) + + // Set up manager role for valid sender + err := s.precompile.assetKeep.TokenManagement.Set( + ctx, + s.tokenDenom, + assettypes.TokenManagement{ + Managers: [][]byte{sender.AccAddr}, + ExtensionsList: []string{"mint"}, + MaxSupply: maxSupply, + }, + ) + s.Require().NoError(err) + + _, err = s.precompile.Mint(ctx, contract, stateDB, &method, tc.malleate()) + fmt.Println("errrrr", err) + if tc.expErr { + s.Require().Error(err, "expected mint transaction to fail") + // s.Require().Contains(err.Error(), tc.errContains, "expected transfer transaction to fail with specific error") + } else { + s.Require().NoError(err, "expected transfer transaction succeeded") + tc.postCheck() + } + }) + } +} + +func (s *PrecompileTestSuite) TestBurn() { + method := s.precompile.Methods[BurnMethod] + // fromAddr is the address of the keyring account used for testing. + sender := s.keyring.GetKey(0) + invalidSender := s.keyring.GetKey(1) + maxSupply := math.NewInt(200) + testcases := []struct { + name string + malleate func() []interface{} + postCheck func() + expErr bool + errContains string + sender keyring.Key + }{ + { + "fail - negative amount", + func() []interface{} { + return []interface{}{big.NewInt(-1)} + }, + func() {}, + true, + "coin -1xmpl amount is not positive", + sender, + }, + { + "fail - invalid amount", + func() []interface{} { + return []interface{}{""} + }, + func() {}, + true, + "invalid amount", + sender, + }, + { + "fail - sender is not manager", + func() []interface{} { + return []interface{}{big.NewInt(100)} + }, + func() {}, + true, + "sender is not token manager", + invalidSender, + }, + { + "fail - not enough balance", + func() []interface{} { + return []interface{}{big.NewInt(300)} + }, + func() {}, + true, + "not enough balance", + sender, + }, + { + "pass", + func() []interface{} { + return []interface{}{big.NewInt(100)} + }, + func() { + addrBalance := s.network.App.BankKeeper.GetBalance(s.network.GetContext(), sender.AccAddr.Bytes(), tokenDenom) + s.Require().Equal(big.NewInt(100), addrBalance.Amount.BigInt(), "expected toAddr to have 100 XMPL") + }, + false, + "", + sender, + }, + } + + for _, tc := range testcases { + s.Run(tc.name, func() { + s.SetupTest() + stateDB := s.network.GetStateDB() + + var contract *vm.Contract + contract, ctx := testutil.NewPrecompileContract(s.T(), s.network.GetContext(), tc.sender.Addr, s.precompile, 0) + + // Set up manager role for valid sender + err := s.precompile.assetKeep.TokenManagement.Set( + ctx, + s.tokenDenom, + assettypes.TokenManagement{ + Managers: [][]byte{sender.AccAddr}, + ExtensionsList: []string{"mint"}, + MaxSupply: maxSupply, + }, + ) + s.Require().NoError(err) + + // Mint amount to fromAddr to burn lately + // _, err = s.precompile.Mint(ctx, contract, stateDB, &method, []interface{}{sender.Addr, big.NewInt(maxSupply.Int64())}) + // fmt.Println("errrrr", err) + // s.Require().NoError(err) + + // Mint some coins to the module account and then send to the from address + err = s.network.App.BankKeeper.MintCoins(ctx, erc20types.ModuleName, sdk.NewCoins(sdk.NewCoin(tokenDenom, maxSupply))) + s.Require().NoError(err, "failed to mint coins") + err = s.network.App.BankKeeper.SendCoinsFromModuleToAccount(ctx, erc20types.ModuleName, sender.AccAddr, sdk.NewCoins(sdk.NewCoin(tokenDenom, maxSupply))) + s.Require().NoError(err, "failed to send coins from module to account") + + _, err = s.precompile.Burn(ctx, contract, stateDB, &method, tc.malleate()) + if tc.expErr { + s.Require().Error(err, "expected burn transaction to fail") + // s.Require().Contains(err.Error(), tc.errContains, "expected transfer transaction to fail with specific error") + } else { + s.Require().NoError(err, "expected burn transaction succeeded") + tc.postCheck() + } + }) + } +} + +func (s *PrecompileTestSuite) TestBurnFrom() { + method := s.precompile.Methods[BurnFromMethod] + // fromAddr is the address of the keyring account used for testing. + sender := s.keyring.GetKey(0) + invalidSender := s.keyring.GetKey(1) + maxSupply := math.NewInt(200) + testcases := []struct { + name string + malleate func() []interface{} + postCheck func() + expErr bool + errContains string + sender keyring.Key + }{ + { + "fail - negative amount", + func() []interface{} { + return []interface{}{fromAddr, big.NewInt(-1)} + }, + func() {}, + true, + "coin -1xmpl amount is not positive", + sender, + }, + { + "fail - invalid from address", + func() []interface{} { + return []interface{}{"", big.NewInt(100)} + }, + func() {}, + true, + "invalid from address", + sender, + }, + { + "fail - invalid amount", + func() []interface{} { + return []interface{}{fromAddr, ""} + }, + func() {}, + true, + "invalid amount", + sender, + }, + { + "fail - sender is not manager", + func() []interface{} { + return []interface{}{fromAddr, big.NewInt(100)} + }, + func() {}, + true, + "sender is not token manager", + invalidSender, + }, + { + "fail - not enough balance", + func() []interface{} { + return []interface{}{fromAddr, big.NewInt(300)} + }, + func() {}, + true, + "not enough balance", + sender, + }, + { + "pass", + func() []interface{} { + return []interface{}{fromAddr, big.NewInt(100)} + }, + func() { + addrBalance := s.network.App.BankKeeper.GetBalance(s.network.GetContext(), fromAddr.Bytes(), tokenDenom) + s.Require().Equal(big.NewInt(100), addrBalance.Amount.BigInt(), "expected toAddr to have 100 XMPL") + }, + false, + "", + sender, + }, + } + + for _, tc := range testcases { + s.Run(tc.name, func() { + s.SetupTest() + stateDB := s.network.GetStateDB() + + var contract *vm.Contract + contract, ctx := testutil.NewPrecompileContract(s.T(), s.network.GetContext(), tc.sender.Addr, s.precompile, 0) + + // Set up manager role for valid sender + err := s.precompile.assetKeep.TokenManagement.Set( + ctx, + s.tokenDenom, + assettypes.TokenManagement{ + Managers: [][]byte{sender.AccAddr}, + ExtensionsList: []string{"mint"}, + MaxSupply: maxSupply, + }, + ) + s.Require().NoError(err) + + // Mint amount to fromAddr to burn lately + // _, err = s.precompile.Mint(ctx, contract, stateDB, &method, []interface{}{sender.Addr, big.NewInt(maxSupply.Int64())}) + // fmt.Println("errrrr", err) + // s.Require().NoError(err) + + // Mint some coins to the module account and then send to the from address + err = s.network.App.BankKeeper.MintCoins(ctx, erc20types.ModuleName, sdk.NewCoins(sdk.NewCoin(tokenDenom, maxSupply))) + s.Require().NoError(err, "failed to mint coins") + err = s.network.App.BankKeeper.SendCoinsFromModuleToAccount(ctx, erc20types.ModuleName, fromAddr.Bytes(), sdk.NewCoins(sdk.NewCoin(tokenDenom, maxSupply))) + s.Require().NoError(err, "failed to send coins from module to account") + + _, err = s.precompile.BurnFrom(ctx, contract, stateDB, &method, tc.malleate()) + if tc.expErr { + s.Require().Error(err, "expected burn transaction to fail") + // s.Require().Contains(err.Error(), tc.errContains, "expected transfer transaction to fail with specific error") + } else { + s.Require().NoError(err, "expected burn transaction succeeded") + tc.postCheck() + } + }) + } +} + +func (s *PrecompileTestSuite) TestFreeze() { + method := s.precompile.Methods[FreezeMethod] + // fromAddr is the address of the keyring account used for testing. + sender := s.keyring.GetKey(0) + invalidSender := s.keyring.GetKey(1) + testcases := []struct { + name string + malleate func() []interface{} + postCheck func() + expErr bool + errContains string + sender keyring.Key + }{ + { + "fail - invalid to address", + func() []interface{} { + return []interface{}{""} + }, + func() {}, + true, + "invalid from address", + sender, + }, + { + "fail - sender is not manager", + func() []interface{} { + return []interface{}{toAddr} + }, + func() {}, + true, + "sender is not token manager", + invalidSender, + }, + { + "pass", + func() []interface{} { + return []interface{}{toAddr} + }, + func() { + exist := s.network.App.AssetKeeper.IsFreezed(s.network.GetContext(), toAddr) + s.Require().True(exist) + }, + false, + "", + sender, + }, + } + + for _, tc := range testcases { + s.Run(tc.name, func() { + s.SetupTest() + stateDB := s.network.GetStateDB() + + var contract *vm.Contract + contract, ctx := testutil.NewPrecompileContract(s.T(), s.network.GetContext(), tc.sender.Addr, s.precompile, 0) + + // Set up manager role for valid sender + err := s.precompile.assetKeep.TokenManagement.Set( + ctx, + s.tokenDenom, + assettypes.TokenManagement{ + Managers: [][]byte{sender.AccAddr}, + ExtensionsList: []string{"mint"}, + }, + ) + s.Require().NoError(err) + + // Mint amount to fromAddr to burn lately + // _, err = s.precompile.Mint(ctx, contract, stateDB, &method, []interface{}{sender.Addr, big.NewInt(maxSupply.Int64())}) + // fmt.Println("errrrr", err) + // s.Require().NoError(err) + + _, err = s.precompile.Freeze(ctx, contract, stateDB, &method, tc.malleate()) + if tc.expErr { + s.Require().Error(err, "expected burn transaction to fail") + // s.Require().Contains(err.Error(), tc.errContains, "expected transfer transaction to fail with specific error") + } else { + s.Require().NoError(err, "expected burn transaction succeeded") + tc.postCheck() + } + }) + } +} diff --git a/precompiles/erc20/types.go b/precompiles/erc20/types.go new file mode 100644 index 00000000..d73c0ea2 --- /dev/null +++ b/precompiles/erc20/types.go @@ -0,0 +1,263 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package erc20 + +import ( + "fmt" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/ethereum/go-ethereum/common" +) + +// EventTransfer defines the event data for the ERC20 Transfer events. +type EventTransfer struct { + From common.Address + To common.Address + Value *big.Int +} + +// EventApproval defines the event data for the ERC20 Approval events. +type EventApproval struct { + Owner common.Address + Spender common.Address + Value *big.Int +} + +// ParseTransferArgs parses the arguments from the transfer method and returns +// the destination address (to) and amount. +func ParseTransferArgs(args []interface{}) ( + to common.Address, amount *big.Int, err error, +) { + if len(args) != 2 { + return common.Address{}, nil, fmt.Errorf("invalid number of arguments; expected 2; got: %d", len(args)) + } + + to, ok := args[0].(common.Address) + if !ok { + return common.Address{}, nil, fmt.Errorf("invalid to address: %v", args[0]) + } + + amount, ok = args[1].(*big.Int) + if !ok { + return common.Address{}, nil, fmt.Errorf("invalid amount: %v", args[1]) + } + + return to, amount, nil +} + +// ParseTransferFromArgs parses the arguments from the transferFrom method and returns +// the sender address (from), destination address (to) and amount. +func ParseTransferFromArgs(args []interface{}) ( + from, to common.Address, amount *big.Int, err error, +) { + if len(args) != 3 { + return common.Address{}, common.Address{}, nil, fmt.Errorf("invalid number of arguments; expected 3; got: %d", len(args)) + } + + from, ok := args[0].(common.Address) + if !ok { + return common.Address{}, common.Address{}, nil, fmt.Errorf("invalid from address: %v", args[0]) + } + + to, ok = args[1].(common.Address) + if !ok { + return common.Address{}, common.Address{}, nil, fmt.Errorf("invalid to address: %v", args[1]) + } + + amount, ok = args[2].(*big.Int) + if !ok { + return common.Address{}, common.Address{}, nil, fmt.Errorf("invalid amount: %v", args[2]) + } + + return from, to, amount, nil +} + +// ParseApproveArgs parses the approval arguments and returns the spender address +// and amount. +func ParseApproveArgs(args []interface{}) ( + spender common.Address, amount *big.Int, err error, +) { + if len(args) != 2 { + return common.Address{}, nil, fmt.Errorf("invalid number of arguments; expected 2; got: %d", len(args)) + } + + spender, ok := args[0].(common.Address) + if !ok { + return common.Address{}, nil, fmt.Errorf("invalid spender address: %v", args[0]) + } + + amount, ok = args[1].(*big.Int) + if !ok { + return common.Address{}, nil, fmt.Errorf("invalid amount: %v", args[1]) + } + + return spender, amount, nil +} + +// ParseAllowanceArgs parses the allowance arguments and returns the owner and +// the spender addresses. +func ParseAllowanceArgs(args []interface{}) ( + owner, spender common.Address, err error, +) { + if len(args) != 2 { + return common.Address{}, common.Address{}, fmt.Errorf("invalid number of arguments; expected 2; got: %d", len(args)) + } + + owner, ok := args[0].(common.Address) + if !ok { + return common.Address{}, common.Address{}, fmt.Errorf("invalid owner address: %v", args[0]) + } + + spender, ok = args[1].(common.Address) + if !ok { + return common.Address{}, common.Address{}, fmt.Errorf("invalid spender address: %v", args[1]) + } + + return owner, spender, nil +} + +// ParseBalanceOfArgs parses the balanceOf arguments and returns the account address. +func ParseBalanceOfArgs(args []interface{}) (common.Address, error) { + if len(args) != 1 { + return common.Address{}, fmt.Errorf("invalid number of arguments; expected 1; got: %d", len(args)) + } + + account, ok := args[0].(common.Address) + if !ok { + return common.Address{}, fmt.Errorf("invalid account address: %v", args[0]) + } + + return account, nil +} + +// updateOrAddCoin replaces the coin of the given denomination in the coins slice or adds it if it +// does not exist yet. +// +// CONTRACT: Requires the coins struct to contain at most one coin of the given +// denom. +func updateOrAddCoin(coins sdk.Coins, coin sdk.Coin) sdk.Coins { + for idx, c := range coins { + if c.Denom == coin.Denom { + coins[idx] = coin + return coins + } + } + + // NOTE: if no coin with the correct denomination is in the coins slice, we + // add it here. + return coins.Add(coin) +} + +func ParseMintArgs(args []interface{}) ( + to common.Address, amount *big.Int, err error, +) { + if len(args) != 2 { + return common.Address{}, nil, fmt.Errorf("invalid number of arguments; expected 2; got: %d", len(args)) + } + + to, ok := args[0].(common.Address) + if !ok { + return common.Address{}, nil, fmt.Errorf("invalid to address: %v", args[0]) + } + + amount, ok = args[1].(*big.Int) + if !ok { + return common.Address{}, nil, fmt.Errorf("invalid amount: %v", args[1]) + } + + return to, amount, nil +} + +func ParseBurnArgs(args []interface{}) ( + amount *big.Int, err error, +) { + if len(args) != 1 { + return nil, fmt.Errorf("invalid number of arguments; expected 1; got: %d", len(args)) + } + + amount, ok := args[0].(*big.Int) + if !ok { + return nil, fmt.Errorf("invalid amount: %v", args[0]) + } + + return amount, nil +} + +func ParseBurnFromArgs(args []interface{}) ( + from common.Address, amount *big.Int, err error, +) { + if len(args) != 2 { + return common.Address{}, nil, fmt.Errorf("invalid number of arguments; expected 2; got: %d", len(args)) + } + + from, ok := args[0].(common.Address) + if !ok { + return common.Address{}, nil, fmt.Errorf("invalid to address: %v", args[0]) + } + + amount, ok = args[1].(*big.Int) + if !ok { + return common.Address{}, nil, fmt.Errorf("invalid amount: %v", args[1]) + } + + return from, amount, nil +} + +func ParseFreezeArgs(args []interface{}) ( + from common.Address, err error, +) { + if len(args) != 1 { + return common.Address{}, fmt.Errorf("invalid number of arguments; expected 1; got: %d", len(args)) + } + + to, ok := args[0].(common.Address) + if !ok { + return common.Address{}, fmt.Errorf("invalid to address: %v", args[0]) + } + + return to, nil +} + +func ParseGrantRoleArgs(args []interface{}) ( + to common.Address, role int32, err error, +) { + if len(args) != 2 { + return common.Address{}, 0, fmt.Errorf("invalid number of arguments; expected 2; got: %d", len(args)) + } + + role, ok := args[0].(int32) + if !ok { + return common.Address{}, 0, fmt.Errorf("invalid to address: %v", args[0]) + } + + to, ok = args[0].(common.Address) + if !ok { + return common.Address{}, 0, fmt.Errorf("invalid to address: %v", args[0]) + } + + return to, role, nil +} + +func ParseRevokeRoleArgs(args []interface{}) ( + to common.Address, role int32, err error, +) { + if len(args) != 2 { + return common.Address{}, 0, fmt.Errorf("invalid number of arguments; expected 2; got: %d", len(args)) + } + + role, ok := args[0].(int32) + if !ok { + return common.Address{}, 0, fmt.Errorf("invalid to address: %v", args[0]) + } + + to, ok = args[0].(common.Address) + if !ok { + return common.Address{}, 0, fmt.Errorf("invalid to address: %v", args[0]) + } + + return to, role, nil +} + diff --git a/precompiles/erc20/utils_test.go b/precompiles/erc20/utils_test.go new file mode 100644 index 00000000..ba265ef5 --- /dev/null +++ b/precompiles/erc20/utils_test.go @@ -0,0 +1,269 @@ +package erc20 + +import ( + "fmt" + "math/big" + "slices" + "time" + + errorsmod "cosmossdk.io/errors" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/evmos/os/precompiles/erc20" + commonfactory "github.com/evmos/os/testutil/integration/common/factory" + commonnetwork "github.com/evmos/os/testutil/integration/common/network" + "github.com/evmos/os/testutil/integration/os/factory" + testutils "github.com/evmos/os/testutil/integration/os/utils" + erc20types "github.com/evmos/os/x/erc20/types" + network "github.com/realiotech/realio-network/testutil/integration/os/network" + utiltx "github.com/realiotech/realio-network/testutil/tx" +) + +// setupSendAuthz is a helper function to set up a SendAuthorization for +// a given grantee and granter combination for a given amount. +// +// NOTE: A default expiration of 1 hour after the current block time is used. +func (s *PrecompileTestSuite) setupSendAuthz( + grantee sdk.AccAddress, granterPriv cryptotypes.PrivKey, amount sdk.Coins, +) { + err := setupSendAuthz( + s.network, + s.factory, + grantee, + granterPriv, + amount, + ) + s.Require().NoError(err, "failed to set up send authorization") +} + +func setupSendAuthz( + network commonnetwork.Network, + factory commonfactory.BaseTxFactory, + grantee sdk.AccAddress, + granterPriv cryptotypes.PrivKey, + amount sdk.Coins, +) error { + granter := sdk.AccAddress(granterPriv.PubKey().Address()) + expiration := network.GetContext().BlockHeader().Time.Add(time.Hour) + sendAuthz := banktypes.NewSendAuthorization( + amount, + []sdk.AccAddress{}, + ) + + msgGrant, err := authz.NewMsgGrant( + granter, + grantee, + sendAuthz, + &expiration, + ) + if err != nil { + return errorsmod.Wrap(err, "failed to create MsgGrant") + } + + // Create an authorization + txArgs := commonfactory.CosmosTxArgs{Msgs: []sdk.Msg{msgGrant}} + _, err = factory.ExecuteCosmosTx(granterPriv, txArgs) + if err != nil { + return errorsmod.Wrap(err, "failed to execute MsgGrant") + } + + return nil +} + +// requireOut is a helper utility to reduce the amount of boilerplate code in the query tests. +// +// It requires the output bytes and error to match the expected values. Additionally, the method outputs +// are unpacked and the first value is compared to the expected value. +// +// NOTE: It's sufficient to only check the first value because all methods in the ERC20 precompile only +// return a single value. +func (s *PrecompileTestSuite) requireOut( + bz []byte, + err error, + method abi.Method, + expPass bool, + errContains string, + expValue interface{}, +) { + if expPass { + s.Require().NoError(err, "expected no error") + s.Require().NotEmpty(bz, "expected bytes not to be empty") + + // Unpack the name into a string + out, err := method.Outputs.Unpack(bz) + s.Require().NoError(err, "expected no error unpacking") + + // Check if expValue is a big.Int. Because of a difference in uninitialized/empty values for big.Ints, + // this comparison is often not working as expected, so we convert to Int64 here and compare those values. + bigExp, ok := expValue.(*big.Int) + if ok { + bigOut, ok := out[0].(*big.Int) + s.Require().True(ok, "expected output to be a big.Int") + s.Require().Equal(bigExp.Int64(), bigOut.Int64(), "expected different value") + } else { + s.Require().Equal(expValue, out[0], "expected different value") + } + } else { + s.Require().Error(err, "expected error") + s.Require().Contains(err.Error(), errContains, "expected different error") + } +} + +// requireSendAuthz is a helper function to check that a SendAuthorization +// exists for a given grantee and granter combination for a given amount. +// +// NOTE: This helper expects only one authorization to exist. +func (s *PrecompileTestSuite) requireSendAuthz(grantee, granter sdk.AccAddress, amount sdk.Coins, allowList []string) { + grants, err := s.grpcHandler.GetGrantsByGrantee(grantee.String()) + s.Require().NoError(err, "expected no error querying the grants") + s.Require().Len(grants, 1, "expected one grant") + s.Require().Equal(grantee.String(), grants[0].Grantee, "expected different grantee") + s.Require().Equal(granter.String(), grants[0].Granter, "expected different granter") + + authzs, err := s.grpcHandler.GetAuthorizationsByGrantee(grantee.String()) + s.Require().NoError(err, "expected no error unpacking the authorization") + s.Require().Len(authzs, 1, "expected one authorization") + + sendAuthz, ok := authzs[0].(*banktypes.SendAuthorization) + s.Require().True(ok, "expected send authorization") + + s.Require().Equal(amount, sendAuthz.SpendLimit, "expected different spend limit amount") + if len(allowList) == 0 { + s.Require().Empty(sendAuthz.AllowList, "expected empty allow list") + } else { + s.Require().Equal(allowList, sendAuthz.AllowList, "expected different allow list") + } +} + +// setupERC20Precompile is a helper function to set up an instance of the ERC20 precompile for +// a given token denomination, set the token pair in the ERC20 keeper and adds the precompile +// to the available and active precompiles. +func (s *PrecompileTestSuite) setupERC20Precompile(denom string) *Precompile { + tokenPair := erc20types.NewTokenPair(utiltx.GenerateAddress(), denom, erc20types.OWNER_MODULE) + s.network.App.Erc20Keeper.SetTokenPair(s.network.GetContext(), tokenPair) + + precompile, err := setupERC20PrecompileForTokenPair(*s.network, tokenPair) + s.Require().NoError(err, "failed to set up %q erc20 precompile", tokenPair.Denom) + + return precompile +} + +// setupERC20PrecompileForTokenPair is a helper function to set up an instance of the ERC20 precompile for +// a given token pair and adds the precompile to the available and active precompiles. +// Do not use this function for integration tests. +func setupERC20PrecompileForTokenPair( + unitNetwork network.UnitTestNetwork, tokenPair erc20types.TokenPair, +) (*Precompile, error) { + precompile, err := NewPrecompile( + tokenPair.Denom, + tokenPair.GetERC20Contract(), + unitNetwork.App.BankKeeper, + unitNetwork.App.AuthzKeeper, + unitNetwork.App.TransferKeeper, + unitNetwork.App.AssetKeeper, + ) + if err != nil { + return nil, errorsmod.Wrapf(err, "failed to create %q erc20 precompile", tokenPair.Denom) + } + + err = unitNetwork.App.Erc20Keeper.EnableDynamicPrecompiles( + unitNetwork.GetContext(), + precompile.Address(), + ) + if err != nil { + return nil, errorsmod.Wrapf(err, "failed to add %q erc20 precompile to EVM extensions", tokenPair.Denom) + } + + return precompile, nil +} + +// setupNewERC20PrecompileForTokenPair is a helper function to set up an instance of the ERC20 precompile for +// a given token pair and adds the precompile to the available and active precompiles. +// This function should be used for integration tests +func setupNewERC20PrecompileForTokenPair( + privKey cryptotypes.PrivKey, + unitNetwork *network.UnitTestNetwork, + tf factory.TxFactory, tokenPair erc20types.TokenPair, +) (*erc20.Precompile, error) { + precompile, err := erc20.NewPrecompile( + tokenPair, + unitNetwork.App.BankKeeper, + unitNetwork.App.AuthzKeeper, + unitNetwork.App.TransferKeeper, + ) + if err != nil { + return nil, errorsmod.Wrapf(err, "failed to create %q erc20 precompile", tokenPair.Denom) + } + + // Update the params via gov proposal + params := unitNetwork.App.Erc20Keeper.GetParams(unitNetwork.GetContext()) + params.DynamicPrecompiles = append(params.DynamicPrecompiles, precompile.Address().Hex()) + slices.Sort(params.DynamicPrecompiles) + + if err := params.Validate(); err != nil { + return nil, err + } + + if err := testutils.UpdateERC20Params(testutils.UpdateParamsInput{ + Pk: privKey, + Tf: tf, + Network: unitNetwork, + Params: params, + }); err != nil { + return nil, errorsmod.Wrapf(err, "failed to add %q erc20 precompile to EVM extensions", tokenPair.Denom) + } + + return precompile, nil +} + +// CallType indicates which type of contract call is made during the integration tests. +type CallType int + +// callType constants to differentiate between direct calls and calls through a contract. +const ( + directCall CallType = iota + 1 + directCallToken2 + contractCall + contractCallToken2 + erc20Call + erc20CallerCall + erc20V5Call + erc20V5CallerCall +) + +var ( + nativeCallTypes = []CallType{directCall, directCallToken2, contractCall, contractCallToken2} + erc20CallTypes = []CallType{erc20Call, erc20CallerCall, erc20V5Call, erc20V5CallerCall} +) + +// ExpectedBalance is a helper struct to check the balances of accounts. +type ExpectedBalance struct { + address sdk.AccAddress + expCoins sdk.Coins +} + +// ContractsData is a helper struct to hold the addresses and ABIs for the +// different contract instances that are subject to testing here. +type ContractsData struct { + contractData map[CallType]ContractData + ownerPriv cryptotypes.PrivKey +} + +// ContractData is a helper struct to hold the address and ABI for a given contract. +type ContractData struct { + Address common.Address + ABI abi.ABI +} + +// GetContractData is a helper function to return the contract data for a given call type. +func (cd ContractsData) GetContractData(callType CallType) ContractData { + data, found := cd.contractData[callType] + if !found { + panic(fmt.Sprintf("no contract data found for call type: %d", callType)) + } + return data +} diff --git a/proto/realionetwork/asset/v1/params.proto b/proto/realionetwork/asset/v1/params.proto index afdfdcfc..e3ef76ea 100644 --- a/proto/realionetwork/asset/v1/params.proto +++ b/proto/realionetwork/asset/v1/params.proto @@ -6,4 +6,7 @@ import "gogoproto/gogo.proto"; option go_package = "github.com/realiotech/realio-network/x/asset/types"; // Params defines the parameters for the module. -message Params { option (gogoproto.goproto_stringer) = false; } \ No newline at end of file +message Params { + option (gogoproto.goproto_stringer) = false; + repeated string allow_extensions = 1; +} \ No newline at end of file diff --git a/proto/realionetwork/asset/v1/query.proto b/proto/realionetwork/asset/v1/query.proto index a07a2525..a3a3c427 100644 --- a/proto/realionetwork/asset/v1/query.proto +++ b/proto/realionetwork/asset/v1/query.proto @@ -24,13 +24,6 @@ service Query { rpc Token(QueryTokenRequest) returns (QueryTokenResponse) { option (google.api.http).get = "/realionetwork/asset/v1/tokens/{symbol}"; } - - // Parameters queries the tokens of the module. - rpc IsAuthorized(QueryIsAuthorizedRequest) - returns (QueryIsAuthorizedResponse) { - option (google.api.http).get = - "/realionetwork/asset/v1/isauthorized/{symbol}/{address}"; - } } // QueryParamsRequest is request type for the Query/Params RPC method. @@ -62,16 +55,3 @@ message QueryTokenResponse { // params holds all the parameters of this module. Token token = 1 [ (gogoproto.nullable) = false ]; } - -// QueryParamsRequest is request type for the Query/Params RPC method. -message QueryIsAuthorizedRequest { - // symbol is the token symbol to query for. - string symbol = 1; - string address = 2; -} - -// QueryParamsResponse is response type for the Query/Params RPC method. -message QueryIsAuthorizedResponse { - // params holds all the parameters of this module. - bool isAuthorized = 1; -} \ No newline at end of file diff --git a/proto/realionetwork/asset/v1/token.proto b/proto/realionetwork/asset/v1/token.proto index bf455d14..31e70c1d 100644 --- a/proto/realionetwork/asset/v1/token.proto +++ b/proto/realionetwork/asset/v1/token.proto @@ -2,16 +2,36 @@ syntax = "proto3"; package realionetwork.asset.v1; import "gogoproto/gogo.proto"; -import "realionetwork/asset/v1/tokenauthorization.proto"; option go_package = "github.com/realiotech/realio-network/x/asset/types"; // Token represents an asset in the module message Token { - string name = 1; - string symbol = 2; - string total = 3; - bool authorizationRequired = 4; - string manager = 5; - repeated TokenAuthorization authorized = 6; + string token_id = 1; + bytes issuer = 2; + string name = 3; + string symbol = 4; + uint32 decimal = 5; + string description = 6; + string evm_address = 9; +} + +// TokenManagement represents the asset manager's execute functions. +message TokenManagement { + repeated bytes managers = 1; + bool allow_new_extensions = 2; + repeated string extensions_list = 3; + string max_supply = 4 [ + (gogoproto.customtype) = "cosmossdk.io/math.Int", + (gogoproto.nullable) = false + ]; +} + +enum Role { + option (gogoproto.goproto_enum_prefix) = false; + + // ROLE_UNSPECIFIED defines a no-op role. + ROLE_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "EmptyRole"]; + // ROLE_MANAGER defines a token manager role. + ROLE_MANAGER = 1 [(gogoproto.enumvalue_customname) = "ManagerRole"]; } \ No newline at end of file diff --git a/proto/realionetwork/asset/v1/tokenauthorization.proto b/proto/realionetwork/asset/v1/tokenauthorization.proto deleted file mode 100644 index 1fef9144..00000000 --- a/proto/realionetwork/asset/v1/tokenauthorization.proto +++ /dev/null @@ -1,13 +0,0 @@ -syntax = "proto3"; -package realionetwork.asset.v1; - -import "gogoproto/gogo.proto"; - -option go_package = "github.com/realiotech/realio-network/x/asset/types"; - -// TokenAuthorization represents the current authorization state for an -// address:token -message TokenAuthorization { - string address = 2; - bool authorized = 3; -} \ No newline at end of file diff --git a/proto/realionetwork/asset/v1/tx.proto b/proto/realionetwork/asset/v1/tx.proto index e6e25986..205b1f85 100644 --- a/proto/realionetwork/asset/v1/tx.proto +++ b/proto/realionetwork/asset/v1/tx.proto @@ -5,6 +5,7 @@ option go_package = "github.com/realiotech/realio-network/x/asset/types"; import "gogoproto/gogo.proto"; import "realionetwork/asset/v1/params.proto"; +import "realionetwork/asset/v1/token.proto"; import "cosmos_proto/cosmos.proto"; import "cosmos/msg/v1/msg.proto"; @@ -12,62 +13,55 @@ import "cosmos/msg/v1/msg.proto"; service Msg { option (cosmos.msg.v1.service) = true; rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); - rpc CreateToken(MsgCreateToken) returns (MsgCreateTokenResponse); - rpc UpdateToken(MsgUpdateToken) returns (MsgUpdateTokenResponse); - rpc AuthorizeAddress(MsgAuthorizeAddress) - returns (MsgAuthorizeAddressResponse); - rpc UnAuthorizeAddress(MsgUnAuthorizeAddress) - returns (MsgUnAuthorizeAddressResponse); - rpc TransferToken(MsgTransferToken) returns (MsgTransferTokenResponse); // this line is used by starport scaffolding # proto/tx/rpc + rpc CreateToken(MsgCreateToken) returns (MsgCreateTokenResponse); + rpc AssignRoles(MsgAssignRoles) returns (MsgAssignRolesResponse); + rpc UnassignRoles(MsgUnassignRoles) returns (MsgUnassignRolesResponse); } message MsgCreateToken { - option (cosmos.msg.v1.signer) = "manager"; - string manager = 1; + option (cosmos.msg.v1.signer) = "issuer"; + // issuer is the address that defines the token + bytes issuer = 1; + string name = 2; - string symbol = 3; - string total = 4; - bool authorizationRequired = 6; + uint32 decimal = 3; + string symbol = 4; + string description = 5; + repeated bytes managers = 6; + bool allow_new_extensions = 8; + repeated string extensions_list = 9; + string max_supply = 10 [ + (gogoproto.customtype) = "cosmossdk.io/math.Int", + (gogoproto.nullable) = false + ]; } -message MsgCreateTokenResponse {} - -message MsgUpdateToken { - option (cosmos.msg.v1.signer) = "manager"; - string manager = 1; - string symbol = 2; - bool authorizationRequired = 3; +message MsgCreateTokenResponse { + string token_id = 1; } -message MsgUpdateTokenResponse {} +message MsgAssignRoles { + option (cosmos.msg.v1.signer) = "issuer"; + // issuer is the address that defines the token + bytes issuer = 1; -message MsgAuthorizeAddress { - option (cosmos.msg.v1.signer) = "manager"; - string manager = 1; - string symbol = 2; - string address = 3; + string token_id = 2; + bytes managers = 3; } -message MsgAuthorizeAddressResponse {} - -message MsgUnAuthorizeAddress { - option (cosmos.msg.v1.signer) = "manager"; - string manager = 1; - string symbol = 2; - string address = 3; -} +message MsgAssignRolesResponse {} -message MsgUnAuthorizeAddressResponse {} +message MsgUnassignRoles { + option (cosmos.msg.v1.signer) = "issuer"; + // issuer is the address that defines the token + bytes issuer = 1; -message MsgTransferToken { - string symbol = 1; - string from = 2; - string to = 3; - string amount = 4; + string token_id = 2; + bytes managers = 3; } -message MsgTransferTokenResponse {} +message MsgUnassignRolesResponse {} message MsgUpdateParams { option (cosmos.msg.v1.signer) = "authority"; diff --git a/testutil/integration/common/factory/base.go b/testutil/integration/common/factory/base.go new file mode 100644 index 00000000..7a716bbf --- /dev/null +++ b/testutil/integration/common/factory/base.go @@ -0,0 +1,119 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package factory + +import ( + "fmt" + + errorsmod "cosmossdk.io/errors" + abcitypes "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/cosmos-sdk/client" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + testutiltypes "github.com/cosmos/cosmos-sdk/types/module/testutil" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + "github.com/evmos/os/testutil/integration/os/grpc" + "github.com/realiotech/realio-network/testutil/integration/os/network" +) + +// BaseTxFactory is the interface that wraps the common methods to build and broadcast transactions +// within cosmos chains +type BaseTxFactory interface { + // BuildCosmosTx builds a Cosmos tx with the provided private key and txArgs + BuildCosmosTx(privKey cryptotypes.PrivKey, txArgs CosmosTxArgs) (authsigning.Tx, error) + // SignCosmosTx signs a Cosmos transaction with the provided + // private key and tx builder + SignCosmosTx(privKey cryptotypes.PrivKey, txBuilder client.TxBuilder) error + // ExecuteCosmosTx builds, signs and broadcasts a Cosmos tx with the provided private key and txArgs + ExecuteCosmosTx(privKey cryptotypes.PrivKey, txArgs CosmosTxArgs) (abcitypes.ExecTxResult, error) + // EncodeTx encodes the provided transaction + EncodeTx(tx sdktypes.Tx) ([]byte, error) + // CommitCosmosTx creates, signs and commits a cosmos tx + // (produces a block with the specified transaction) + CommitCosmosTx(privKey cryptotypes.PrivKey, txArgs CosmosTxArgs) (abcitypes.ExecTxResult, error) +} + +// baseTxFactory is the struct of the basic tx factory +// to build and broadcast transactions. +// This is to simulate the behavior of a real user. +type baseTxFactory struct { + grpcHandler grpc.Handler + network network.Network + ec testutiltypes.TestEncodingConfig +} + +// newBaseTxFactory instantiates a new baseTxFactory +func newBaseTxFactory( + network network.Network, + grpcHandler grpc.Handler, +) BaseTxFactory { + return &baseTxFactory{ + grpcHandler: grpcHandler, + network: network, + ec: network.GetEncodingConfig(), + } +} + +func (tf *baseTxFactory) BuildCosmosTx(privKey cryptotypes.PrivKey, txArgs CosmosTxArgs) (authsigning.Tx, error) { + txBuilder, err := tf.buildTx(privKey, txArgs) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to build tx") + } + return txBuilder.GetTx(), nil +} + +// ExecuteCosmosTx creates, signs and broadcasts a Cosmos transaction +func (tf *baseTxFactory) ExecuteCosmosTx(privKey cryptotypes.PrivKey, txArgs CosmosTxArgs) (abcitypes.ExecTxResult, error) { + signedTx, err := tf.BuildCosmosTx(privKey, txArgs) + if err != nil { + return abcitypes.ExecTxResult{}, errorsmod.Wrap(err, "failed to build tx") + } + + txBytes, err := tf.EncodeTx(signedTx) + if err != nil { + return abcitypes.ExecTxResult{}, errorsmod.Wrap(err, "failed to encode tx") + } + + return tf.network.BroadcastTxSync(txBytes) +} + +// CommitCosmosTx creates and signs a Cosmos transaction, and then includes it in +// a block and commits the state changes on the chain +func (tf *baseTxFactory) CommitCosmosTx(privKey cryptotypes.PrivKey, txArgs CosmosTxArgs) (abcitypes.ExecTxResult, error) { + signedTx, err := tf.BuildCosmosTx(privKey, txArgs) + if err != nil { + return abcitypes.ExecTxResult{}, errorsmod.Wrap(err, "failed to build tx") + } + + txBytes, err := tf.EncodeTx(signedTx) + if err != nil { + return abcitypes.ExecTxResult{}, errorsmod.Wrap(err, "failed to encode tx") + } + + blockRes, err := tf.network.NextBlockWithTxs(txBytes) + if err != nil { + return abcitypes.ExecTxResult{}, errorsmod.Wrap(err, "failed to include the tx in a block") + } + txResCount := len(blockRes.TxResults) + if txResCount != 1 { + return abcitypes.ExecTxResult{}, fmt.Errorf("expected to receive only one tx result, but got %d", txResCount) + } + return *blockRes.TxResults[0], nil +} + +// SignCosmosTx is a helper function that signs a Cosmos transaction +// with the provided private key and transaction builder +func (tf *baseTxFactory) SignCosmosTx(privKey cryptotypes.PrivKey, txBuilder client.TxBuilder) error { + txConfig := tf.ec.TxConfig + signMode, err := authsigning.APISignModeToInternal(txConfig.SignModeHandler().DefaultMode()) + if err != nil { + return errorsmod.Wrap(err, "invalid sign mode") + } + signerData, err := tf.setSignatures(privKey, txBuilder, signMode) + if err != nil { + return errorsmod.Wrap(err, "failed to set tx signatures") + } + + return tf.signWithPrivKey(privKey, txBuilder, signerData, signMode) +} diff --git a/testutil/integration/common/factory/distribution.go b/testutil/integration/common/factory/distribution.go new file mode 100644 index 00000000..392a2418 --- /dev/null +++ b/testutil/integration/common/factory/distribution.go @@ -0,0 +1,88 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package factory + +import ( + "fmt" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +type DistributionTxFactory interface { + // SetWithdrawAddress is a method to create and broadcast a MsgSetWithdrawAddress + SetWithdrawAddress(delegatorPriv cryptotypes.PrivKey, withdrawerAddr sdk.AccAddress) error + // WithdrawDelegationRewards is a method to create and broadcast a MsgWithdrawDelegationRewards + WithdrawDelegationRewards(delegatorPriv cryptotypes.PrivKey, validatorAddr string) error + // WithdrawValidatorCommission is a method to create and broadcast a MsgWithdrawValidatorCommission + WithdrawValidatorCommission(validatorPriv cryptotypes.PrivKey) error +} + +type distributionTxFactory struct { + BaseTxFactory +} + +func newDistrTxFactory(bf BaseTxFactory) DistributionTxFactory { + return &distributionTxFactory{bf} +} + +func (tf *distributionTxFactory) SetWithdrawAddress(delegatorPriv cryptotypes.PrivKey, withdrawerAddr sdk.AccAddress) error { + delegatorAccAddr := sdk.AccAddress(delegatorPriv.PubKey().Address()) + + msg := distrtypes.NewMsgSetWithdrawAddress( + delegatorAccAddr, + withdrawerAddr, + ) + + resp, err := tf.ExecuteCosmosTx(delegatorPriv, CosmosTxArgs{ + Msgs: []sdk.Msg{msg}, + }) + + if resp.Code != 0 { + err = fmt.Errorf("received error code %d on SetWithdrawAddress transaction. Logs: %s", resp.Code, resp.Log) + } + + return err +} + +// WithdrawDelegationRewards will withdraw any unclaimed staking rewards for the delegator associated with +// the given private key from the validator. +// The validator address should be in the format `evmosvaloper1...`. +func (tf *distributionTxFactory) WithdrawDelegationRewards(delegatorPriv cryptotypes.PrivKey, validatorAddr string) error { + delegatorAccAddr := sdk.AccAddress(delegatorPriv.PubKey().Address()) + + msg := distrtypes.NewMsgWithdrawDelegatorReward( + delegatorAccAddr.String(), + validatorAddr, + ) + + resp, err := tf.ExecuteCosmosTx(delegatorPriv, CosmosTxArgs{ + Msgs: []sdk.Msg{msg}, + }) + + if resp.Code != 0 { + err = fmt.Errorf("received error code %d on WithdrawDelegationRewards transaction. Logs: %s", resp.Code, resp.Log) + } + + return err +} + +func (tf *distributionTxFactory) WithdrawValidatorCommission(validatorPriv cryptotypes.PrivKey) error { + validatorAddr := sdk.ValAddress(validatorPriv.PubKey().Address()) + + msg := distrtypes.NewMsgWithdrawValidatorCommission( + validatorAddr.String(), + ) + + resp, err := tf.ExecuteCosmosTx(validatorPriv, CosmosTxArgs{ + Msgs: []sdk.Msg{msg}, + }) + + if resp.Code != 0 { + err = fmt.Errorf("received error code %d on WithdrawValidatorCommission transaction. Logs: %s", resp.Code, resp.Log) + } + + return err +} diff --git a/testutil/integration/common/factory/factory.go b/testutil/integration/common/factory/factory.go new file mode 100644 index 00000000..eb05d853 --- /dev/null +++ b/testutil/integration/common/factory/factory.go @@ -0,0 +1,48 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package factory + +import ( + "github.com/evmos/os/testutil/integration/os/grpc" + "github.com/realiotech/realio-network/testutil/integration/os/network" +) + +const ( + GasAdjustment = float64(1.7) +) + +// CoreTxFactory is the interface that wraps the methods +// to build and broadcast cosmos transactions, and also +// includes module-specific transactions +type CoreTxFactory interface { + BaseTxFactory + DistributionTxFactory + StakingTxFactory + FundTxFactory +} + +var _ CoreTxFactory = (*IntegrationTxFactory)(nil) + +// IntegrationTxFactory is a helper struct to build and broadcast transactions +// to the network on integration tests. This is to simulate the behavior of a real user. +type IntegrationTxFactory struct { + BaseTxFactory + DistributionTxFactory + StakingTxFactory + FundTxFactory +} + +// New creates a new IntegrationTxFactory instance +func New( + network network.Network, + grpcHandler grpc.Handler, +) CoreTxFactory { + bf := newBaseTxFactory(network, grpcHandler) + return &IntegrationTxFactory{ + bf, + newDistrTxFactory(bf), + newStakingTxFactory(bf), + newFundTxFactory(bf), + } +} diff --git a/testutil/integration/common/factory/fund.go b/testutil/integration/common/factory/fund.go new file mode 100644 index 00000000..0a2b56fa --- /dev/null +++ b/testutil/integration/common/factory/fund.go @@ -0,0 +1,51 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package factory + +import ( + "fmt" + + sdktypes "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/evmos/os/testutil/integration/os/keyring" +) + +// FundTxFactory is the interface that wraps the common methods to fund accounts +// via a bank send transaction +type FundTxFactory interface { + // FundAccount funds the given account with the given amount. + FundAccount(sender keyring.Key, receiver sdktypes.AccAddress, amount sdktypes.Coins) error +} + +// baseTxFactory is the struct of the basic tx factory +// to build and broadcast transactions. +// This is to simulate the behavior of a real user. +type fundTxFactory struct { + BaseTxFactory +} + +// newBaseTxFactory instantiates a new baseTxFactory +func newFundTxFactory(bf BaseTxFactory) FundTxFactory { + return &fundTxFactory{bf} +} + +// FundAccount funds the given account with the given amount of coins. +func (tf *fundTxFactory) FundAccount(sender keyring.Key, receiver sdktypes.AccAddress, coins sdktypes.Coins) error { + bankmsg := banktypes.NewMsgSend( + sender.AccAddr, + receiver, + coins, + ) + txArgs := CosmosTxArgs{Msgs: []sdktypes.Msg{bankmsg}} + txRes, err := tf.ExecuteCosmosTx(sender.Priv, txArgs) + if err != nil { + return err + } + + if txRes.Code != 0 { + return fmt.Errorf("transaction returned non-zero code %d", txRes.Code) + } + + return nil +} diff --git a/testutil/integration/common/factory/helper.go b/testutil/integration/common/factory/helper.go new file mode 100644 index 00000000..70e2bf53 --- /dev/null +++ b/testutil/integration/common/factory/helper.go @@ -0,0 +1,119 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package factory + +import ( + "math/big" + + errorsmod "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/client" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +// EncodeTx encodes the tx using the txConfig's encoder. +func (tf *baseTxFactory) EncodeTx(tx sdktypes.Tx) ([]byte, error) { + txConfig := tf.ec.TxConfig + txBytes, err := txConfig.TxEncoder()(tx) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to encode tx") + } + return txBytes, nil +} + +// buildTx builds a tx with the provided private key and txArgs +func (tf *baseTxFactory) buildTx(privKey cryptotypes.PrivKey, txArgs CosmosTxArgs) (client.TxBuilder, error) { + txConfig := tf.ec.TxConfig + txBuilder := txConfig.NewTxBuilder() + + if err := txBuilder.SetMsgs(txArgs.Msgs...); err != nil { + return nil, errorsmod.Wrap(err, "failed to set tx msgs") + } + + if txArgs.FeeGranter != nil { + txBuilder.SetFeeGranter(txArgs.FeeGranter) + } + + senderAddress := sdktypes.AccAddress(privKey.PubKey().Address().Bytes()) + + if txArgs.FeeGranter != nil { + txBuilder.SetFeeGranter(txArgs.FeeGranter) + } + + txBuilder.SetFeePayer(senderAddress) + + // need to sign the tx to simulate the tx to get the gas estimation + signMode, err := authsigning.APISignModeToInternal(txConfig.SignModeHandler().DefaultMode()) + if err != nil { + return nil, errorsmod.Wrap(err, "invalid sign mode") + } + signerData, err := tf.setSignatures(privKey, txBuilder, signMode) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to set tx signatures") + } + + gasLimit, err := tf.estimateGas(txArgs, txBuilder) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to estimate gas") + } + txBuilder.SetGasLimit(gasLimit) + + fees := txArgs.Fees + if fees.IsZero() { + fees, err = tf.calculateFees(txArgs.GasPrice, gasLimit) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to calculate fees") + } + } + txBuilder.SetFeeAmount(fees) + + if err := tf.signWithPrivKey(privKey, txBuilder, signerData, signMode); err != nil { + return nil, errorsmod.Wrap(err, "failed to sign Cosmos Tx") + } + + return txBuilder, nil +} + +// calculateFees calculates the fees for the transaction. +func (tf *baseTxFactory) calculateFees(gasPrice *sdkmath.Int, gasLimit uint64) (sdktypes.Coins, error) { + denom := tf.network.GetBaseDenom() + var fees sdktypes.Coins + if gasPrice != nil { + fees = sdktypes.Coins{{Denom: denom, Amount: gasPrice.MulRaw(int64(gasLimit))}} //#nosec G115 + } else { + resp, err := tf.grpcHandler.GetBaseFee() + if err != nil { + return sdktypes.Coins{}, errorsmod.Wrap(err, "failed to get base fee") + } + price := resp.BaseFee + fees = sdktypes.Coins{{Denom: denom, Amount: price.MulInt64(int64(gasLimit)).TruncateInt()}} //#nosec G115 + } + return fees, nil +} + +// estimateGas estimates the gas needed for the transaction. +func (tf *baseTxFactory) estimateGas(txArgs CosmosTxArgs, txBuilder client.TxBuilder) (uint64, error) { + txConfig := tf.ec.TxConfig + simulateBytes, err := txConfig.TxEncoder()(txBuilder.GetTx()) + if err != nil { + return 0, errorsmod.Wrap(err, "failed to encode tx") + } + + var gasLimit uint64 + if txArgs.Gas == nil { + simulateRes, err := tf.network.Simulate(simulateBytes) + if err != nil { + return 0, errorsmod.Wrap(err, "failed to simulate tx") + } + + gasAdj := new(big.Float).SetFloat64(GasAdjustment) + gasUsed := new(big.Float).SetUint64(simulateRes.GasInfo.GasUsed) + gasLimit, _ = gasAdj.Mul(gasAdj, gasUsed).Uint64() + } else { + gasLimit = *txArgs.Gas + } + return gasLimit, nil +} diff --git a/testutil/integration/common/factory/sign.go b/testutil/integration/common/factory/sign.go new file mode 100644 index 00000000..6686dfe0 --- /dev/null +++ b/testutil/integration/common/factory/sign.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 factory + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/client" + cosmostx "github.com/cosmos/cosmos-sdk/client/tx" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +// setSignatures is a helper function that sets the signature for +// the transaction in the tx builder. It returns the signerData to be used +// when signing the transaction (e.g. when calling signWithPrivKey) +func (tf *baseTxFactory) setSignatures(privKey cryptotypes.PrivKey, txBuilder client.TxBuilder, signMode signing.SignMode) (signerData authsigning.SignerData, err error) { + senderAddress := sdktypes.AccAddress(privKey.PubKey().Address().Bytes()) + account, err := tf.grpcHandler.GetAccount(senderAddress.String()) + if err != nil { + return signerData, err + } + sequence := account.GetSequence() + signerData = authsigning.SignerData{ + ChainID: tf.network.GetChainID(), + AccountNumber: account.GetAccountNumber(), + Sequence: sequence, + Address: senderAddress.String(), + PubKey: privKey.PubKey(), + } + + sigsV2 := signing.SignatureV2{ + PubKey: privKey.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: signMode, + Signature: nil, + }, + Sequence: sequence, + } + + return signerData, txBuilder.SetSignatures(sigsV2) +} + +// signWithPrivKey is a helper function that signs a transaction +// with the provided private key +func (tf *baseTxFactory) signWithPrivKey(privKey cryptotypes.PrivKey, txBuilder client.TxBuilder, signerData authsigning.SignerData, signMode signing.SignMode) error { + txConfig := tf.ec.TxConfig + signature, err := cosmostx.SignWithPrivKey(context.TODO(), signMode, signerData, txBuilder, privKey, txConfig, signerData.Sequence) + if err != nil { + return errorsmod.Wrap(err, "failed to sign tx") + } + + return txBuilder.SetSignatures(signature) +} diff --git a/testutil/integration/common/factory/staking.go b/testutil/integration/common/factory/staking.go new file mode 100644 index 00000000..72799a4d --- /dev/null +++ b/testutil/integration/common/factory/staking.go @@ -0,0 +1,88 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package factory + +import ( + "fmt" + + "cosmossdk.io/math" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +type StakingTxFactory interface { + // Delegate is a method to create and execute a MsgDelegate paying always the same fee amount + // The tx is included in a block and committed in the chain state + Delegate(delegatorPriv cryptotypes.PrivKey, validatorAddr string, amount sdk.Coin) error + // CreateValidator is a method to create and broadcast a MsgCreateValidator + CreateValidator(operatorPriv cryptotypes.PrivKey, pubKey cryptotypes.PubKey, selfDelegation sdk.Coin, description stakingtypes.Description, commission stakingtypes.CommissionRates, minSelfDelegation math.Int) error +} + +type stakingTxFactory struct { + BaseTxFactory +} + +func newStakingTxFactory(bf BaseTxFactory) StakingTxFactory { + return &stakingTxFactory{bf} +} + +// Delegate on behalf of the account associated with the given private key. +// The defined amount will delegated to the specified validator. +// The validator address should be in the format `evmosvaloper1...`. +func (tf *stakingTxFactory) Delegate(delegatorPriv cryptotypes.PrivKey, validatorAddr string, amount sdk.Coin) error { + delegatorAccAddr := sdk.AccAddress(delegatorPriv.PubKey().Address()) + + msgDelegate := stakingtypes.NewMsgDelegate( + delegatorAccAddr.String(), + validatorAddr, + amount, + ) + + // set gas and gas prices to pay the same fees + // every time this function is called + feesToPay := math.NewInt(1e16) + gas := uint64(400_000) + gasPrice := feesToPay.QuoRaw(int64(gas)) //#nosec G115 -- gas will not exceed int64 + + res, err := tf.CommitCosmosTx(delegatorPriv, CosmosTxArgs{ + Msgs: []sdk.Msg{msgDelegate}, + Gas: &gas, + GasPrice: &gasPrice, + }) + + if res.IsErr() { + return fmt.Errorf("tx result with code %d. Logs: %s", res.Code, res.Log) + } + + return err +} + +// CreateValidator executes the transaction to create a validator +// with the parameters specified +func (tf *stakingTxFactory) CreateValidator(operatorPriv cryptotypes.PrivKey, pubKey cryptotypes.PubKey, selfDelegation sdk.Coin, description stakingtypes.Description, commission stakingtypes.CommissionRates, minSelfDelegation math.Int) error { + operatorAccAddr := sdk.ValAddress(operatorPriv.PubKey().Address()) + + msgCreateValidator, err := stakingtypes.NewMsgCreateValidator( + operatorAccAddr.String(), + pubKey, + selfDelegation, + description, + commission, + minSelfDelegation, + ) + if err != nil { + return err + } + + resp, err := tf.ExecuteCosmosTx(operatorPriv, CosmosTxArgs{ + Msgs: []sdk.Msg{msgCreateValidator}, + }) + + if resp.Code != 0 { + err = fmt.Errorf("received error code %d on CreateValidator transaction. Logs: %s", resp.Code, resp.Log) + } + + return err +} diff --git a/testutil/integration/common/factory/types.go b/testutil/integration/common/factory/types.go new file mode 100644 index 00000000..abd35161 --- /dev/null +++ b/testutil/integration/common/factory/types.go @@ -0,0 +1,25 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package factory + +import ( + sdkmath "cosmossdk.io/math" + sdktypes "github.com/cosmos/cosmos-sdk/types" +) + +// CosmosTxArgs contains the params to create a cosmos tx +type CosmosTxArgs struct { + // ChainID is the chain's id in cosmos format, e.g. 'evmos_9000-1' + ChainID string + // Gas to be used on the tx + Gas *uint64 + // GasPrice to use on tx + GasPrice *sdkmath.Int + // Fees is the fee to be used on the tx (amount and denom) + Fees sdktypes.Coins + // FeeGranter is the account address of the fee granter + FeeGranter sdktypes.AccAddress + // Msgs slice of messages to include on the tx + Msgs []sdktypes.Msg +} diff --git a/testutil/integration/common/grpc/account.go b/testutil/integration/common/grpc/account.go new file mode 100644 index 00000000..38777290 --- /dev/null +++ b/testutil/integration/common/grpc/account.go @@ -0,0 +1,29 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package grpc + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// GetAccount returns the account for the given address. +func (gqh *IntegrationHandler) GetAccount(address string) (sdk.AccountI, error) { + authClient := gqh.network.GetAuthClient() + res, err := authClient.Account(context.Background(), &authtypes.QueryAccountRequest{ + Address: address, + }) + if err != nil { + return nil, err + } + + encodingCgf := gqh.network.GetEncodingConfig() + var acc sdk.AccountI + if err = encodingCgf.InterfaceRegistry.UnpackAny(res.Account, &acc); err != nil { + return nil, err + } + return acc, nil +} diff --git a/testutil/integration/common/grpc/authz.go b/testutil/integration/common/grpc/authz.go new file mode 100644 index 00000000..bd60044e --- /dev/null +++ b/testutil/integration/common/grpc/authz.go @@ -0,0 +1,117 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package grpc + +import ( + "context" + + "github.com/cosmos/cosmos-sdk/x/authz" +) + +// GetGrants returns the grants for the given grantee and granter combination. +// +// NOTE: To extract the concrete authorizations, use the GetAuthorizations method. +func (gqh *IntegrationHandler) GetGrants(grantee, granter string) ([]*authz.Grant, error) { + authzClient := gqh.network.GetAuthzClient() + res, err := authzClient.Grants(context.Background(), &authz.QueryGrantsRequest{ + Grantee: grantee, + Granter: granter, + }) + if err != nil { + return nil, err + } + + return res.Grants, nil +} + +// GetGrantsByGrantee returns the grants for the given grantee. +// +// NOTE: To extract the concrete authorizations, use the GetAuthorizationsByGrantee method. +func (gqh *IntegrationHandler) GetGrantsByGrantee(grantee string) ([]*authz.GrantAuthorization, error) { + authzClient := gqh.network.GetAuthzClient() + res, err := authzClient.GranteeGrants(context.Background(), &authz.QueryGranteeGrantsRequest{ + Grantee: grantee, + }) + if err != nil { + return nil, err + } + + return res.Grants, nil +} + +// GetGrantsByGranter returns the grants for the given granter. +// +// NOTE: To extract the concrete authorizations, use the GetAuthorizationsByGranter method. +func (gqh *IntegrationHandler) GetGrantsByGranter(granter string) ([]*authz.GrantAuthorization, error) { + authzClient := gqh.network.GetAuthzClient() + res, err := authzClient.GranterGrants(context.Background(), &authz.QueryGranterGrantsRequest{ + Granter: granter, + }) + if err != nil { + return nil, err + } + + return res.Grants, nil +} + +// GetAuthorizations returns the concrete authorizations for the given grantee and granter combination. +func (gqh *IntegrationHandler) GetAuthorizations(grantee, granter string) ([]authz.Authorization, error) { + encodingCfg := gqh.network.GetEncodingConfig() + + grants, err := gqh.GetGrants(grantee, granter) + if err != nil { + return nil, err + } + + auths := make([]authz.Authorization, 0, len(grants)) + for _, grant := range grants { + var auth authz.Authorization + err := encodingCfg.InterfaceRegistry.UnpackAny(grant.Authorization, &auth) + if err != nil { + return nil, err + } + + auths = append(auths, auth) + } + + return auths, nil +} + +// GetAuthorizationsByGrantee returns the concrete authorizations for the given grantee. +func (gqh *IntegrationHandler) GetAuthorizationsByGrantee(grantee string) ([]authz.Authorization, error) { + grants, err := gqh.GetGrantsByGrantee(grantee) + if err != nil { + return nil, err + } + + return gqh.unpackGrantAuthzs(grants) +} + +// GetAuthorizationsByGranter returns the concrete authorizations for the given granter. +func (gqh *IntegrationHandler) GetAuthorizationsByGranter(granter string) ([]authz.Authorization, error) { + grants, err := gqh.GetGrantsByGranter(granter) + if err != nil { + return nil, err + } + + return gqh.unpackGrantAuthzs(grants) +} + +// unpackGrantAuthzs unpacks the given grant authorization. +func (gqh *IntegrationHandler) unpackGrantAuthzs(grantAuthzs []*authz.GrantAuthorization) ([]authz.Authorization, error) { + encodingCfg := gqh.network.GetEncodingConfig() + + auths := make([]authz.Authorization, 0, len(grantAuthzs)) + for _, grantAuthz := range grantAuthzs { + var auth authz.Authorization + err := encodingCfg.InterfaceRegistry.UnpackAny(grantAuthz.Authorization, &auth) + if err != nil { + return nil, err + } + + auths = append(auths, auth) + } + + return auths, nil +} diff --git a/testutil/integration/common/grpc/bank.go b/testutil/integration/common/grpc/bank.go new file mode 100644 index 00000000..c82f114e --- /dev/null +++ b/testutil/integration/common/grpc/bank.go @@ -0,0 +1,40 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package grpc + +import ( + "context" + + sdktypes "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +// GetBalanceFromBank returns the balance for the given address and denom. +func (gqh *IntegrationHandler) GetBalanceFromBank(address sdktypes.AccAddress, denom string) (*banktypes.QueryBalanceResponse, error) { + bankClient := gqh.network.GetBankClient() + return bankClient.Balance(context.Background(), &banktypes.QueryBalanceRequest{ + Address: address.String(), + Denom: denom, + }) +} + +// GetAllBalances returns all the balances for the given address. +func (gqh *IntegrationHandler) GetAllBalances(address sdktypes.AccAddress) (*banktypes.QueryAllBalancesResponse, error) { + bankClient := gqh.network.GetBankClient() + return bankClient.AllBalances(context.Background(), &banktypes.QueryAllBalancesRequest{ + Address: address.String(), + }) +} + +// GetTotalSupply returns all the balances for the given address. +func (gqh *IntegrationHandler) GetTotalSupply() (*banktypes.QueryTotalSupplyResponse, error) { + bankClient := gqh.network.GetBankClient() + return bankClient.TotalSupply(context.Background(), &banktypes.QueryTotalSupplyRequest{}) +} + +// GetSpendableBalance returns the spendable balance for the given denomination. +func (gqh *IntegrationHandler) GetSpendableBalance(address sdktypes.AccAddress, denom string) (*banktypes.QuerySpendableBalanceByDenomResponse, error) { + bankClient := gqh.network.GetBankClient() + return bankClient.SpendableBalanceByDenom(context.Background(), &banktypes.QuerySpendableBalanceByDenomRequest{Address: address.String(), Denom: denom}) +} diff --git a/testutil/integration/common/grpc/distribution.go b/testutil/integration/common/grpc/distribution.go new file mode 100644 index 00000000..6b67be85 --- /dev/null +++ b/testutil/integration/common/grpc/distribution.go @@ -0,0 +1,57 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package grpc + +import ( + "context" + + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +// GetDelegationTotalRewards returns the total delegation rewards for the given delegator. +func (gqh *IntegrationHandler) GetDelegationTotalRewards(delegatorAddress string) (*distrtypes.QueryDelegationTotalRewardsResponse, error) { + distrClient := gqh.network.GetDistrClient() + return distrClient.DelegationTotalRewards(context.Background(), &distrtypes.QueryDelegationTotalRewardsRequest{ + DelegatorAddress: delegatorAddress, + }) +} + +// GetDelegationRewards returns the delegation rewards for the given delegator and validator. +func (gqh *IntegrationHandler) GetDelegationRewards(delegatorAddress string, validatorAddress string) (*distrtypes.QueryDelegationRewardsResponse, error) { + distrClient := gqh.network.GetDistrClient() + return distrClient.DelegationRewards(context.Background(), &distrtypes.QueryDelegationRewardsRequest{ + DelegatorAddress: delegatorAddress, + ValidatorAddress: validatorAddress, + }) +} + +// GetDelegatorWithdrawAddr returns the withdraw address the given delegator. +func (gqh *IntegrationHandler) GetDelegatorWithdrawAddr(delegatorAddress string) (*distrtypes.QueryDelegatorWithdrawAddressResponse, error) { + distrClient := gqh.network.GetDistrClient() + return distrClient.DelegatorWithdrawAddress(context.Background(), &distrtypes.QueryDelegatorWithdrawAddressRequest{ + DelegatorAddress: delegatorAddress, + }) +} + +// GetValidatorCommission returns the commission for the given validator. +func (gqh *IntegrationHandler) GetValidatorCommission(validatorAddress string) (*distrtypes.QueryValidatorCommissionResponse, error) { + distrClient := gqh.network.GetDistrClient() + return distrClient.ValidatorCommission(context.Background(), &distrtypes.QueryValidatorCommissionRequest{ + ValidatorAddress: validatorAddress, + }) +} + +// GetValidatorOutstandingRewards returns the delegation rewards for the given delegator and validator. +func (gqh *IntegrationHandler) GetValidatorOutstandingRewards(validatorAddress string) (*distrtypes.QueryValidatorOutstandingRewardsResponse, error) { + distrClient := gqh.network.GetDistrClient() + return distrClient.ValidatorOutstandingRewards(context.Background(), &distrtypes.QueryValidatorOutstandingRewardsRequest{ + ValidatorAddress: validatorAddress, + }) +} + +// GetCommunityPool queries the community pool coins. +func (gqh *IntegrationHandler) GetCommunityPool() (*distrtypes.QueryCommunityPoolResponse, error) { + distrClient := gqh.network.GetDistrClient() + return distrClient.CommunityPool(context.Background(), &distrtypes.QueryCommunityPoolRequest{}) +} diff --git a/testutil/integration/common/grpc/grpc.go b/testutil/integration/common/grpc/grpc.go new file mode 100644 index 00000000..a0a9736a --- /dev/null +++ b/testutil/integration/common/grpc/grpc.go @@ -0,0 +1,67 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package grpc + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/evmos/os/testutil/integration/common/network" +) + +// Handler is an interface that defines the common methods that are used to query +// the network's modules via gRPC. +type Handler interface { + // Account methods + GetAccount(address string) (sdk.AccountI, error) + + // Authz methods + GetAuthorizations(grantee, granter string) ([]authz.Authorization, error) + GetAuthorizationsByGrantee(grantee string) ([]authz.Authorization, error) + GetAuthorizationsByGranter(granter string) ([]authz.Authorization, error) + GetGrants(grantee, granter string) ([]*authz.Grant, error) + GetGrantsByGrantee(grantee string) ([]*authz.GrantAuthorization, error) + GetGrantsByGranter(granter string) ([]*authz.GrantAuthorization, error) + + // Bank methods + GetBalanceFromBank(address sdk.AccAddress, denom string) (*banktypes.QueryBalanceResponse, error) + GetSpendableBalance(address sdk.AccAddress, denom string) (*banktypes.QuerySpendableBalanceByDenomResponse, error) + GetAllBalances(address sdk.AccAddress) (*banktypes.QueryAllBalancesResponse, error) + GetTotalSupply() (*banktypes.QueryTotalSupplyResponse, error) + + // Staking methods + GetDelegation(delegatorAddress string, validatorAddress string) (*stakingtypes.QueryDelegationResponse, error) + GetDelegatorDelegations(delegatorAddress string) (*stakingtypes.QueryDelegatorDelegationsResponse, error) + GetValidatorDelegations(validatorAddress string) (*stakingtypes.QueryValidatorDelegationsResponse, error) + GetRedelegations(delegatorAddress, srcValidator, dstValidator string) (*stakingtypes.QueryRedelegationsResponse, error) + GetValidatorUnbondingDelegations(validatorAddress string) (*stakingtypes.QueryValidatorUnbondingDelegationsResponse, error) + GetDelegatorUnbondingDelegations(delegatorAddress string) (*stakingtypes.QueryDelegatorUnbondingDelegationsResponse, error) + + // Distribution methods + GetDelegationTotalRewards(delegatorAddress string) (*distrtypes.QueryDelegationTotalRewardsResponse, error) + GetDelegationRewards(delegatorAddress string, validatorAddress string) (*distrtypes.QueryDelegationRewardsResponse, error) + GetDelegatorWithdrawAddr(delegatorAddress string) (*distrtypes.QueryDelegatorWithdrawAddressResponse, error) + GetValidatorCommission(validatorAddress string) (*distrtypes.QueryValidatorCommissionResponse, error) + GetValidatorOutstandingRewards(validatorAddress string) (*distrtypes.QueryValidatorOutstandingRewardsResponse, error) + GetCommunityPool() (*distrtypes.QueryCommunityPoolResponse, error) + GetBondedValidators() (*stakingtypes.QueryValidatorsResponse, error) +} + +var _ Handler = (*IntegrationHandler)(nil) + +// IntegrationHandler is a helper struct to query the network's modules +// via gRPC. This is to simulate the behavior of a real user and avoid querying +// the modules directly. +type IntegrationHandler struct { + network network.Network +} + +// NewIntegrationHandler creates a new IntegrationHandler instance. +func NewIntegrationHandler(network network.Network) *IntegrationHandler { + return &IntegrationHandler{ + network: network, + } +} diff --git a/testutil/integration/common/grpc/staking.go b/testutil/integration/common/grpc/staking.go new file mode 100644 index 00000000..0d959346 --- /dev/null +++ b/testutil/integration/common/grpc/staking.go @@ -0,0 +1,69 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package grpc + +import ( + "context" + + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// GetDelegation returns the delegation for the given delegator and validator addresses. +func (gqh *IntegrationHandler) GetDelegation(delegatorAddress string, validatorAddress string) (*stakingtypes.QueryDelegationResponse, error) { + stakingClient := gqh.network.GetStakingClient() + return stakingClient.Delegation(context.Background(), &stakingtypes.QueryDelegationRequest{ + DelegatorAddr: delegatorAddress, + ValidatorAddr: validatorAddress, + }) +} + +// GetValidatorDelegations returns the delegations to a given validator. +func (gqh *IntegrationHandler) GetValidatorDelegations(validatorAddress string) (*stakingtypes.QueryValidatorDelegationsResponse, error) { + stakingClient := gqh.network.GetStakingClient() + return stakingClient.ValidatorDelegations(context.Background(), &stakingtypes.QueryValidatorDelegationsRequest{ + ValidatorAddr: validatorAddress, + }) +} + +// GetDelegatorDelegations returns the delegations to a given delegator. +func (gqh *IntegrationHandler) GetDelegatorDelegations(delegatorAddress string) (*stakingtypes.QueryDelegatorDelegationsResponse, error) { + stakingClient := gqh.network.GetStakingClient() + return stakingClient.DelegatorDelegations(context.Background(), &stakingtypes.QueryDelegatorDelegationsRequest{ + DelegatorAddr: delegatorAddress, + }) +} + +// GetRedelegations returns the redelegations to a given delegator and validators. +func (gqh *IntegrationHandler) GetRedelegations(delegatorAddress, srcValidator, dstValidator string) (*stakingtypes.QueryRedelegationsResponse, error) { + stakingClient := gqh.network.GetStakingClient() + return stakingClient.Redelegations(context.Background(), &stakingtypes.QueryRedelegationsRequest{ + DelegatorAddr: delegatorAddress, + SrcValidatorAddr: srcValidator, + DstValidatorAddr: dstValidator, + }) +} + +// GetValidatorUnbondingDelegations returns the unbonding delegations to a given validator. +func (gqh *IntegrationHandler) GetValidatorUnbondingDelegations(validatorAddress string) (*stakingtypes.QueryValidatorUnbondingDelegationsResponse, error) { + stakingClient := gqh.network.GetStakingClient() + return stakingClient.ValidatorUnbondingDelegations(context.Background(), &stakingtypes.QueryValidatorUnbondingDelegationsRequest{ + ValidatorAddr: validatorAddress, + }) +} + +// GetDelegatorUnbondingDelegations returns all the unbonding delegations for given delegator. +func (gqh *IntegrationHandler) GetDelegatorUnbondingDelegations(delegatorAddress string) (*stakingtypes.QueryDelegatorUnbondingDelegationsResponse, error) { + stakingClient := gqh.network.GetStakingClient() + return stakingClient.DelegatorUnbondingDelegations(context.Background(), &stakingtypes.QueryDelegatorUnbondingDelegationsRequest{ + DelegatorAddr: delegatorAddress, + }) +} + +// GetValidators returns the list of all bonded validators. +func (gqh *IntegrationHandler) GetBondedValidators() (*stakingtypes.QueryValidatorsResponse, error) { + stakingClient := gqh.network.GetStakingClient() + return stakingClient.Validators(context.Background(), &stakingtypes.QueryValidatorsRequest{ + Status: stakingtypes.BondStatusBonded, + }) +} diff --git a/testutil/integration/common/network/network.go b/testutil/integration/common/network/network.go new file mode 100644 index 00000000..26d4c5b0 --- /dev/null +++ b/testutil/integration/common/network/network.go @@ -0,0 +1,53 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package network + +import ( + "testing" + "time" + + abcitypes "github.com/cometbft/cometbft/abci/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + sdktestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + ibctesting "github.com/cosmos/ibc-go/v8/testing" +) + +// Network is the interface that wraps the common methods to interact with integration test network. +// +// It was designed to avoid users to access module's keepers directly and force integration tests +// to be closer to the real user's behavior. +type Network interface { + GetContext() sdktypes.Context + GetChainID() string + GetBaseDenom() string + GetOtherDenoms() []string + GetValidators() []stakingtypes.Validator + + NextBlock() error + NextBlockAfter(duration time.Duration) error + NextBlockWithTxs(txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) + + // Clients + GetAuthClient() authtypes.QueryClient + GetAuthzClient() authz.QueryClient + GetBankClient() banktypes.QueryClient + GetStakingClient() stakingtypes.QueryClient + GetDistrClient() distrtypes.QueryClient + + BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxResult, error) + Simulate(txBytes []byte) (*txtypes.SimulateResponse, error) + CheckTx(txBytes []byte) (*abcitypes.ResponseCheckTx, error) + + // GetIBCChain returns the IBC test chain. + // NOTE: this is only used for testing IBC related functionality. + // The idea is to deprecate this eventually. + GetIBCChain(t *testing.T, coord *ibctesting.Coordinator) *ibctesting.TestChain + GetEncodingConfig() sdktestutil.TestEncodingConfig +} diff --git a/testutil/integration/ibc/chain/chain.go b/testutil/integration/ibc/chain/chain.go new file mode 100644 index 00000000..1c88773d --- /dev/null +++ b/testutil/integration/ibc/chain/chain.go @@ -0,0 +1,82 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package chain + +import ( + "time" + + cmttypes "github.com/cometbft/cometbft/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + // For now, we'll keep this. Pending to review if we can remove it. + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v8/modules/core/exported" + ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" + ibctesting "github.com/cosmos/ibc-go/v8/testing" + "github.com/cosmos/ibc-go/v8/testing/simapp" +) + +// Chain defines the required methods needed for a testing IBC chain that complies +// with the ibctesting chain struct. +type Chain interface { + // GetContext returns the current context for the application. + GetContext() sdktypes.Context + // GetSimApp returns the SimApp to allow usage of non-interface fields. + GetSimApp() *simapp.SimApp + // QueryProof performs an abci query with the given key and returns the proto encoded merkle proof + // for the query and the height at which the proof will succeed on a tendermint verifier. + QueryProof(key []byte) ([]byte, clienttypes.Height) + // QueryProofAtHeight performs an abci query with the given key and returns the proto encoded merkle proof + // for the query and the height at which the proof will succeed on a tendermint verifier. Only the IBC + // store is supported + QueryProofAtHeight(key []byte, height int64) ([]byte, clienttypes.Height) + // QueryProofForStore performs an abci query with the given key and returns the proto encoded merkle proof + // for the query and the height at which the proof will succeed on a tendermint verifier. + QueryProofForStore(storeKey string, key []byte, height int64) ([]byte, clienttypes.Height) + // QueryUpgradeProof performs an abci query with the given key and returns the proto encoded merkle proof + // for the query and the height at which the proof will succeed on a tendermint verifier. + QueryUpgradeProof(key []byte, height uint64) ([]byte, clienttypes.Height) + // QueryConsensusStateProof performs an abci query for a consensus state + // stored on the given clientID. The proof and consensusHeight are returned. + QueryConsensusStateProof(clientID string) ([]byte, clienttypes.Height) + // NextBlock sets the last header to the current header and increments the current header to be + // at the next block height. It does not update the time as that is handled by the Coordinator. + // It will call Endblock and Commit and apply the validator set changes to the next validators + // of the next block being created. This follows the Tendermint protocol of applying valset changes + // returned on block `n` to the validators of block `n+2`. + // It calls BeginBlock with the new block created before returning. + NextBlock() + // GetClientState retrieves the client state for the provided clientID. The client is + // expected to exist otherwise testing will fail. + GetClientState(clientID string) exported.ClientState + // GetConsensusState retrieves the consensus state for the provided clientID and height. + // It will return a success boolean depending on if consensus state exists or not. + GetConsensusState(clientID string, height exported.Height) (exported.ConsensusState, bool) + // GetValsAtHeight will return the trusted validator set of the chain for the given trusted height. It will return + // a success boolean depending on if the validator set exists or not at that height. + GetValsAtHeight(trustedHeight int64) (*cmttypes.ValidatorSet, bool) + // GetAcknowledgement retrieves an acknowledgement for the provided packet. If the + // acknowledgement does not exist then testing will fail. + GetAcknowledgement(packet exported.PacketI) []byte + // GetPrefix returns the prefix for used by a chain in connection creation + GetPrefix() commitmenttypes.MerklePrefix + // ConstructUpdateTMClientHeader will construct a valid 07-tendermint Header to update the + // light client on the source chain. + ConstructUpdateTMClientHeader(counterparty *ibctesting.TestChain, clientID string) (*ibctm.Header, error) + // ConstructUpdateTMClientHeaderWithTrustedHeight will construct a valid 07-tendermint Header to update the + ConstructUpdateTMClientHeaderWithTrustedHeight(counterparty *ibctesting.TestChain, clientID string, trustedHeight clienttypes.Height) (*ibctm.Header, error) // light client on the source chain. + // ExpireClient fast forwards the chain's block time by the provided amount of time which will + // expire any clients with a trusting period less than or equal to this amount of time. + ExpireClient(amount time.Duration) + // CurrentTMClientHeader creates a TM header using the current header parameters + // on the chain. The trusted fields in the header are set to nil. + CurrentTMClientHeader() *ibctm.Header + // GetChannelCapability returns the channel capability for the given portID and channelID. + // The capability must exist, otherwise testing will fail. + GetChannelCapability(portID, channelID string) *capabilitytypes.Capability + // GetTimeoutHeight is a convenience function which returns an IBC packet timeout height + // to be used for testing. It returns the current IBC height + 100 blocks + GetTimeoutHeight() clienttypes.Height +} diff --git a/testutil/integration/ibc/coordinator/coordinator.go b/testutil/integration/ibc/coordinator/coordinator.go new file mode 100644 index 00000000..7ea68b6d --- /dev/null +++ b/testutil/integration/ibc/coordinator/coordinator.go @@ -0,0 +1,169 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package coordinator + +import ( + "testing" + "time" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + ibctesting "github.com/cosmos/ibc-go/v8/testing" + evmosibc "github.com/evmos/os/ibc/testing" + "github.com/evmos/os/testutil/integration/common/network" + ibcchain "github.com/evmos/os/testutil/integration/ibc/chain" +) + +// Coordinator is the interface that defines the methods that are used to +// coordinate the execution of the IBC relayer. +type Coordinator interface { + // IncrementTime iterates through all the TestChain's and increments their current header time + // by 5 seconds. + IncrementTime() + // UpdateTime updates all clocks for the TestChains to the current global time. + UpdateTime() + // UpdateTimeForChain updates the clock for a specific chain. + UpdateTimeForChain(chainID string) + // GetChain returns the TestChain for a given chainID. + GetChain(chainID string) ibcchain.Chain + // GetDummyChainsIDs returns the chainIDs for all dummy chains. + GetDummyChainsIDs() []string + // GetPath returns the transfer path for the chain ids 'a' and 'b' + GetPath(a, b string) *evmosibc.Path + // GetChainSenderAcc returns the sender account for the specified chain + GetChainSenderAcc(chainID string) sdk.AccountI + // SetDefaultSignerForChain sets the default signer for the chain with the given chainID. + SetDefaultSignerForChain(chainID string, priv cryptotypes.PrivKey, acc sdk.AccountI) + // Setup constructs a TM client, connection, and channel on both chains provided. It will + // fail if any error occurs. The clientID's, TestConnections, and TestChannels are returned + // for both chains. The channels created are connected to the ibc-transfer application. + Setup(src, dst string) IBCConnection + // CommitNBlocks commits n blocks on the chain with the given chainID. + CommitNBlocks(chainID string, n uint64) error + // CommitAll commits 1 blocks on all chains within the coordinator. + CommitAll() error +} + +var AmountOfDummyChains = 2 + +var _ Coordinator = (*IntegrationCoordinator)(nil) + +// IntegrationCoordinator is a testing struct which contains N TestChain's. It handles keeping all chains +// in sync with regards to time. +// NOTE: When using the coordinator, it is important to commit blocks through the coordinator and not +// through the network interface directly. This is because the coordinator does not keep the context in +// sync with the network interface. +type IntegrationCoordinator struct { + coord *ibctesting.Coordinator + dummyChainsIDs []string +} + +// NewIntegrationCoordinator returns a new IntegrationCoordinator with N TestChain's. +func NewIntegrationCoordinator(t *testing.T, preConfiguredChains []network.Network) *IntegrationCoordinator { + coord := &ibctesting.Coordinator{ + T: t, + CurrentTime: time.Now(), + } + ibcChains := getIBCChains(t, coord, preConfiguredChains) + dummyChains, dummyChainsIDs := generateDummyChains(t, coord, AmountOfDummyChains) + totalChains := mergeMaps(ibcChains, dummyChains) + coord.Chains = totalChains + return &IntegrationCoordinator{ + coord: coord, + dummyChainsIDs: dummyChainsIDs, + } +} + +// 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 +} + +// GetPath returns the transfer path for the chain ids 'a' and 'b' +func (c *IntegrationCoordinator) GetPath(a, b string) *evmosibc.Path { + chainA := c.coord.GetChain(a) + chainB := c.coord.GetChain(b) + + return evmosibc.NewTransferPath(chainA, chainB) +} + +// GetChain returns the TestChain for a given chainID. +func (c *IntegrationCoordinator) GetChainSenderAcc(chainID string) sdk.AccountI { + return c.coord.Chains[chainID].SenderAccount +} + +// IncrementTime iterates through all the TestChain's and increments their current header time +// by 5 seconds. +func (c *IntegrationCoordinator) IncrementTime() { + c.coord.IncrementTime() +} + +// UpdateTime updates all clocks for the TestChains to the current global time. +func (c *IntegrationCoordinator) UpdateTime() { + c.coord.UpdateTime() +} + +// UpdateTimeForChain updates the clock for a specific chain. +func (c *IntegrationCoordinator) UpdateTimeForChain(chainID string) { + chain := c.coord.GetChain(chainID) + c.coord.UpdateTimeForChain(chain) +} + +// SetDefaultSignerForChain sets the default signer for the chain with the given chainID. +func (c *IntegrationCoordinator) SetDefaultSignerForChain(chainID string, priv cryptotypes.PrivKey, acc sdk.AccountI) { + chain := c.coord.GetChain(chainID) + chain.SenderPrivKey = priv + chain.SenderAccount = acc + chain.SenderAccounts = []ibctesting.SenderAccount{{SenderPrivKey: priv, SenderAccount: acc}} +} + +// Setup constructs a TM client, connection, and channel on both chains provided. It will +// fail if any error occurs. The clientID's, TestConnections, and TestChannels are returned +// for both chains. The channels created are connected to the ibc-transfer application. +func (c *IntegrationCoordinator) Setup(a, b string) IBCConnection { + path := c.GetPath(a, b) + evmosibc.SetupPath(c.coord, path) + + return IBCConnection{ + EndpointA: Endpoint{ + ChainID: a, + ClientID: path.EndpointA.ClientID, + ConnectionID: path.EndpointA.ConnectionID, + ChannelID: path.EndpointA.ChannelID, + PortID: path.EndpointA.ChannelConfig.PortID, + }, + EndpointB: Endpoint{ + ChainID: b, + ClientID: path.EndpointB.ClientID, + ConnectionID: path.EndpointB.ConnectionID, + ChannelID: path.EndpointB.ChannelID, + PortID: path.EndpointB.ChannelConfig.PortID, + }, + } +} + +// CommitNBlocks commits n blocks on the chain with the given chainID. +func (c *IntegrationCoordinator) CommitNBlocks(chainID string, n uint64) error { + chain := c.coord.GetChain(chainID) + c.coord.CommitNBlocks(chain, n) + return nil +} + +// CommitAll commits n blocks on the chain with the given chainID. +func (c *IntegrationCoordinator) CommitAll() error { + for _, chain := range c.coord.Chains { + c.coord.CommitNBlocks(chain, 1) + } + return nil +} diff --git a/testutil/integration/ibc/coordinator/types.go b/testutil/integration/ibc/coordinator/types.go new file mode 100644 index 00000000..dd54ad17 --- /dev/null +++ b/testutil/integration/ibc/coordinator/types.go @@ -0,0 +1,18 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package coordinator + +// Endpoint defines the identifiers for a chain's client, connection, and channel. +type Endpoint struct { + ChainID string + ClientID string + ConnectionID string + ChannelID string + PortID string +} + +// IBCConnection defines the connection between two chains. +type IBCConnection struct { + EndpointA Endpoint + EndpointB Endpoint +} diff --git a/testutil/integration/ibc/coordinator/utils.go b/testutil/integration/ibc/coordinator/utils.go new file mode 100644 index 00000000..e828bf31 --- /dev/null +++ b/testutil/integration/ibc/coordinator/utils.go @@ -0,0 +1,53 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package coordinator + +import ( + "strconv" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + ibctesting "github.com/cosmos/ibc-go/v8/testing" + "github.com/evmos/os/testutil/integration/common/network" +) + +// getIBCChains returns a map of TestChain's for the given network interface. +func getIBCChains(t *testing.T, coord *ibctesting.Coordinator, chains []network.Network) map[string]*ibctesting.TestChain { + ibcChains := make(map[string]*ibctesting.TestChain) + for _, chain := range chains { + ibcChains[chain.GetChainID()] = chain.GetIBCChain(t, coord) + } + return ibcChains +} + +// generateDummyChains returns a map of dummy chains to complement IBC connections for integration tests. +func generateDummyChains(t *testing.T, coord *ibctesting.Coordinator, numberOfChains int) (map[string]*ibctesting.TestChain, []string) { + ibcChains := make(map[string]*ibctesting.TestChain) + ids := make([]string, numberOfChains) + // dummy chains use the ibc testing chain setup + // that uses the default sdk address prefix ('cosmos') + // Update the prefix configs to use that prefix + cfg := sdk.GetConfig() + cfg.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub) + cfg.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub) + cfg.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) + // Also need to disable address cache to avoid using modules + // accounts with 'evmos' addresses (because Evmos chain setup is first) + sdk.SetAddrCacheEnabled(false) + for i := 1; i <= numberOfChains; i++ { + chainID := "dummychain-" + strconv.Itoa(i) + ids[i-1] = chainID + ibcChains[chainID] = ibctesting.NewTestChain(t, coord, chainID) + } + + return ibcChains, ids +} + +// mergeMaps merges two maps of TestChain's. +func mergeMaps(m1, m2 map[string]*ibctesting.TestChain) map[string]*ibctesting.TestChain { + for k, v := range m2 { + m1[k] = v + } + return m1 +} diff --git a/testutil/integration/os/factory/broadcast.go b/testutil/integration/os/factory/broadcast.go new file mode 100644 index 00000000..81531ea4 --- /dev/null +++ b/testutil/integration/os/factory/broadcast.go @@ -0,0 +1,100 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package factory + +import ( + errorsmod "cosmossdk.io/errors" + abcitypes "github.com/cometbft/cometbft/abci/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/evmos/os/precompiles/testutil" + evmtypes "github.com/evmos/os/x/evm/types" +) + +// ExecuteEthTx executes an Ethereum transaction - contract call with the provided private key and txArgs +// It first builds a MsgEthereumTx and then broadcasts it to the network. +func (tf *IntegrationTxFactory) ExecuteEthTx( + priv cryptotypes.PrivKey, + txArgs evmtypes.EvmTxArgs, +) (abcitypes.ExecTxResult, error) { + signedMsg, err := tf.GenerateSignedEthTx(priv, txArgs) + if err != nil { + return abcitypes.ExecTxResult{}, errorsmod.Wrap(err, "failed to generate signed ethereum tx") + } + + txBytes, err := tf.encodeTx(signedMsg) + if err != nil { + return abcitypes.ExecTxResult{}, errorsmod.Wrap(err, "failed to encode ethereum tx") + } + + res, err := tf.network.BroadcastTxSync(txBytes) + if err != nil { + return abcitypes.ExecTxResult{}, errorsmod.Wrap(err, "failed to broadcast ethereum tx") + } + + if err := tf.checkEthTxResponse(&res); err != nil { + return res, errorsmod.Wrap(err, "failed ETH tx") + } + return res, nil +} + +// ExecuteContractCall executes a contract call with the provided private key. +func (tf *IntegrationTxFactory) ExecuteContractCall(privKey cryptotypes.PrivKey, txArgs evmtypes.EvmTxArgs, callArgs CallArgs) (abcitypes.ExecTxResult, error) { + completeTxArgs, err := tf.GenerateContractCallArgs(txArgs, callArgs) + if err != nil { + return abcitypes.ExecTxResult{}, errorsmod.Wrap(err, "failed to generate contract call args") + } + + return tf.ExecuteEthTx(privKey, completeTxArgs) +} + +// DeployContract deploys a contract with the provided private key, +// compiled contract data and constructor arguments. +// TxArgs Input and Nonce fields are overwritten. +func (tf *IntegrationTxFactory) DeployContract( + priv cryptotypes.PrivKey, + txArgs evmtypes.EvmTxArgs, + deploymentData ContractDeploymentData, +) (common.Address, error) { + // Get account's nonce to create contract hash + from := common.BytesToAddress(priv.PubKey().Address().Bytes()) + completeTxArgs, err := tf.GenerateDeployContractArgs(from, txArgs, deploymentData) + if err != nil { + return common.Address{}, errorsmod.Wrap(err, "failed to generate contract call args") + } + + res, err := tf.ExecuteEthTx(priv, completeTxArgs) + if err != nil || !res.IsOK() { + return common.Address{}, errorsmod.Wrap(err, "failed to execute eth tx") + } + return crypto.CreateAddress(from, completeTxArgs.Nonce), nil +} + +// CallContractAndCheckLogs is a helper function to call a contract and check the logs using +// the integration test utilities. +// +// It returns the Cosmos Tx response, the decoded Ethereum Tx response and an error. This error value +// is nil, if the expected logs are found and the VM error is the expected one, should one be expected. +func (tf *IntegrationTxFactory) CallContractAndCheckLogs( + priv cryptotypes.PrivKey, + txArgs evmtypes.EvmTxArgs, + callArgs CallArgs, + logCheckArgs testutil.LogCheckArgs, +) (abcitypes.ExecTxResult, *evmtypes.MsgEthereumTxResponse, error) { + res, err := tf.ExecuteContractCall(priv, txArgs, callArgs) + logCheckArgs.Res = res + if err != nil { + // NOTE: here we are still passing the response to the log check function, + // because we want to check the logs and expected error in case of a VM error. + return res, nil, CheckError(err, logCheckArgs) + } + + ethRes, err := evmtypes.DecodeTxResponse(res.Data) + if err != nil { + return res, nil, err + } + + return res, ethRes, testutil.CheckLogs(logCheckArgs) +} diff --git a/testutil/integration/os/factory/build.go b/testutil/integration/os/factory/build.go new file mode 100644 index 00000000..7f9fc8e7 --- /dev/null +++ b/testutil/integration/os/factory/build.go @@ -0,0 +1,163 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package factory + +import ( + "encoding/json" + "errors" + "math/big" + + errorsmod "cosmossdk.io/errors" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/x/auth/signing" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" + gethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/evmos/os/server/config" + evmtypes "github.com/evmos/os/x/evm/types" +) + +func (tf *IntegrationTxFactory) GenerateDefaultTxTypeArgs(sender common.Address, txType int) (evmtypes.EvmTxArgs, error) { + defaultArgs := evmtypes.EvmTxArgs{} + switch txType { + case gethtypes.DynamicFeeTxType: + return tf.populateEvmTxArgsWithDefault(sender, defaultArgs) + case gethtypes.AccessListTxType: + defaultArgs.Accesses = &gethtypes.AccessList{{ + Address: sender, + StorageKeys: []common.Hash{{0}}, + }} + defaultArgs.GasPrice = big.NewInt(1e9) + return tf.populateEvmTxArgsWithDefault(sender, defaultArgs) + case gethtypes.LegacyTxType: + defaultArgs.GasPrice = big.NewInt(1e9) + return tf.populateEvmTxArgsWithDefault(sender, defaultArgs) + default: + return evmtypes.EvmTxArgs{}, errors.New("tx type not supported") + } +} + +// EstimateGasLimit estimates the gas limit for a tx with the provided address and txArgs +func (tf *IntegrationTxFactory) EstimateGasLimit(from *common.Address, txArgs *evmtypes.EvmTxArgs) (uint64, error) { + args, err := json.Marshal(evmtypes.TransactionArgs{ + Data: (*hexutil.Bytes)(&txArgs.Input), + From: from, + To: txArgs.To, + AccessList: txArgs.Accesses, + }) + if err != nil { + return 0, errorsmod.Wrap(err, "failed to marshal tx args") + } + + res, err := tf.grpcHandler.EstimateGas(args, config.DefaultGasCap) + if err != nil { + return 0, errorsmod.Wrap(err, "failed to estimate gas") + } + gas := res.Gas + + return gas, nil +} + +// GenerateSignedEthTx generates an Ethereum tx with the provided private key and txArgs but does not broadcast it. +func (tf *IntegrationTxFactory) GenerateSignedEthTx(privKey cryptotypes.PrivKey, txArgs evmtypes.EvmTxArgs) (signing.Tx, error) { + signedMsg, err := tf.GenerateSignedMsgEthereumTx(privKey, txArgs) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to generate signed MsgEthereumTx") + } + + // Validate the transaction to avoid unrealistic behavior + if err = signedMsg.ValidateBasic(); err != nil { + return nil, errorsmod.Wrap(err, "failed to validate transaction") + } + + return tf.buildSignedTx(signedMsg) +} + +// GenerateSignedMsgEthereumTx generates an MsgEthereumTx signed with the provided private key and txArgs. +func (tf *IntegrationTxFactory) GenerateSignedMsgEthereumTx(privKey cryptotypes.PrivKey, txArgs evmtypes.EvmTxArgs) (evmtypes.MsgEthereumTx, error) { + msgEthereumTx, err := tf.GenerateMsgEthereumTx(privKey, txArgs) + if err != nil { + return evmtypes.MsgEthereumTx{}, errorsmod.Wrap(err, "failed to create ethereum tx") + } + + return tf.SignMsgEthereumTx(privKey, msgEthereumTx) +} + +// GenerateMsgEthereumTx creates a new MsgEthereumTx with the provided arguments. +// If any of the arguments are not provided, they will be populated with default values. +func (tf *IntegrationTxFactory) GenerateMsgEthereumTx( + privKey cryptotypes.PrivKey, + txArgs evmtypes.EvmTxArgs, +) (evmtypes.MsgEthereumTx, error) { + fromAddr := common.BytesToAddress(privKey.PubKey().Address().Bytes()) + // Fill TxArgs with default values + txArgs, err := tf.populateEvmTxArgsWithDefault(fromAddr, txArgs) + if err != nil { + return evmtypes.MsgEthereumTx{}, errorsmod.Wrap(err, "failed to populate tx args") + } + msg := buildMsgEthereumTx(txArgs, fromAddr) + + return msg, nil +} + +// GenerateGethCoreMsg creates a new GethCoreMsg with the provided arguments. +func (tf *IntegrationTxFactory) GenerateGethCoreMsg( + privKey cryptotypes.PrivKey, + txArgs evmtypes.EvmTxArgs, +) (core.Message, error) { + msg, err := tf.GenerateMsgEthereumTx(privKey, txArgs) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to generate ethereum tx") + } + + signedMsg, err := tf.SignMsgEthereumTx(privKey, msg) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to sign ethereum tx") + } + + baseFeeResp, err := tf.grpcHandler.GetBaseFee() + if err != nil { + return nil, errorsmod.Wrap(err, "failed to get base fee") + } + signer := gethtypes.LatestSignerForChainID( + tf.network.GetEIP155ChainID(), + ) + return signedMsg.AsMessage(signer, baseFeeResp.BaseFee.BigInt()) +} + +// GenerateContractCallArgs generates the txArgs for a contract call. +func (tf *IntegrationTxFactory) GenerateContractCallArgs( + txArgs evmtypes.EvmTxArgs, + callArgs CallArgs, +) (evmtypes.EvmTxArgs, error) { + input, err := callArgs.ContractABI.Pack(callArgs.MethodName, callArgs.Args...) + if err != nil { + return evmtypes.EvmTxArgs{}, errorsmod.Wrap(err, "failed to pack contract arguments") + } + txArgs.Input = input + return txArgs, nil +} + +// GenerateDeployContractArgs generates the txArgs for a contract deployment. +func (tf *IntegrationTxFactory) GenerateDeployContractArgs( + from common.Address, + txArgs evmtypes.EvmTxArgs, + deploymentData ContractDeploymentData, +) (evmtypes.EvmTxArgs, error) { + account, err := tf.grpcHandler.GetEvmAccount(from) + if err != nil { + return evmtypes.EvmTxArgs{}, errorsmod.Wrapf(err, "failed to get evm account: %s", from.String()) + } + txArgs.Nonce = account.GetNonce() + + ctorArgs, err := deploymentData.Contract.ABI.Pack("", deploymentData.ConstructorArgs...) + if err != nil { + return evmtypes.EvmTxArgs{}, errorsmod.Wrap(err, "failed to pack constructor arguments") + } + data := deploymentData.Contract.Bin + data = append(data, ctorArgs...) + + txArgs.Input = data + return txArgs, nil +} diff --git a/testutil/integration/os/factory/factory.go b/testutil/integration/os/factory/factory.go new file mode 100644 index 00000000..9aedad78 --- /dev/null +++ b/testutil/integration/os/factory/factory.go @@ -0,0 +1,209 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package factory + +import ( + "fmt" + "math/big" + "strings" + + errorsmod "cosmossdk.io/errors" + abcitypes "github.com/cometbft/cometbft/abci/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + testutiltypes "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/auth/signing" + "github.com/cosmos/gogoproto/proto" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/evmos/os/precompiles/testutil" + commonfactory "github.com/evmos/os/testutil/integration/common/factory" + "github.com/evmos/os/testutil/integration/os/grpc" + "github.com/evmos/os/x/evm/core/vm" + evmtypes "github.com/evmos/os/x/evm/types" + "github.com/realiotech/realio-network/testutil/integration/os/network" +) + +// TxFactory defines a struct that can build and broadcast transactions for the Evmos +// network. +// Methods are organized by build sign and broadcast type methods. +type TxFactory interface { + commonfactory.CoreTxFactory + + // GenerateDefaultTxTypeArgs generates a default ETH tx args for the desired tx type + GenerateDefaultTxTypeArgs(sender common.Address, txType int) (evmtypes.EvmTxArgs, error) + // GenerateSignedEthTx generates an Ethereum tx with the provided private key and txArgs but does not broadcast it. + GenerateSignedEthTx(privKey cryptotypes.PrivKey, txArgs evmtypes.EvmTxArgs) (signing.Tx, error) + // GenerateSignedMsgEthereumTx generates an MsgEthereumTx signed with the provided private key and txArgs. + GenerateSignedMsgEthereumTx(privKey cryptotypes.PrivKey, txArgs evmtypes.EvmTxArgs) (evmtypes.MsgEthereumTx, error) + + // SignMsgEthereumTx signs a MsgEthereumTx with the provided private key. + SignMsgEthereumTx(privKey cryptotypes.PrivKey, msgEthereumTx evmtypes.MsgEthereumTx) (evmtypes.MsgEthereumTx, error) + + // ExecuteEthTx builds, signs and broadcasts an Ethereum tx with the provided private key and txArgs. + // If the txArgs are not provided, they will be populated with default values or gas estimations. + ExecuteEthTx(privKey cryptotypes.PrivKey, txArgs evmtypes.EvmTxArgs) (abcitypes.ExecTxResult, error) + // ExecuteContractCall executes a contract call with the provided private key + ExecuteContractCall(privKey cryptotypes.PrivKey, txArgs evmtypes.EvmTxArgs, callArgs CallArgs) (abcitypes.ExecTxResult, error) + // DeployContract deploys a contract with the provided private key, + // compiled contract data and constructor arguments + DeployContract(privKey cryptotypes.PrivKey, txArgs evmtypes.EvmTxArgs, deploymentData ContractDeploymentData) (common.Address, error) + // CallContractAndCheckLogs is a helper function to call a contract and check the logs using + // the integration test utilities. + // + // It returns the Cosmos Tx response, the decoded Ethereum Tx response and an error. This error value + // is nil, if the expected logs are found and the VM error is the expected one, should one be expected. + CallContractAndCheckLogs(privKey cryptotypes.PrivKey, txArgs evmtypes.EvmTxArgs, callArgs CallArgs, logCheckArgs testutil.LogCheckArgs) (abcitypes.ExecTxResult, *evmtypes.MsgEthereumTxResponse, error) + // GenerateDeployContractArgs generates the txArgs for a contract deployment. + GenerateDeployContractArgs(from common.Address, txArgs evmtypes.EvmTxArgs, deploymentData ContractDeploymentData) (evmtypes.EvmTxArgs, error) + // GenerateContractCallArgs generates the txArgs for a contract call. + GenerateContractCallArgs(txArgs evmtypes.EvmTxArgs, callArgs CallArgs) (evmtypes.EvmTxArgs, error) + // GenerateMsgEthereumTx creates a new MsgEthereumTx with the provided arguments. + GenerateMsgEthereumTx(privKey cryptotypes.PrivKey, txArgs evmtypes.EvmTxArgs) (evmtypes.MsgEthereumTx, error) + // GenerateGethCoreMsg creates a new GethCoreMsg with the provided arguments. + GenerateGethCoreMsg(privKey cryptotypes.PrivKey, txArgs evmtypes.EvmTxArgs) (core.Message, error) + // EstimateGasLimit estimates the gas limit for a tx with the provided address and txArgs + EstimateGasLimit(from *common.Address, txArgs *evmtypes.EvmTxArgs) (uint64, error) + // GetEvmTransactionResponseFromTxResult returns the MsgEthereumTxResponse from the provided txResult + GetEvmTransactionResponseFromTxResult(txResult abcitypes.ExecTxResult) (*evmtypes.MsgEthereumTxResponse, error) +} + +var _ TxFactory = (*IntegrationTxFactory)(nil) + +// IntegrationTxFactory is a helper struct to build and broadcast transactions +// to the network on integration tests. This is to simulate the behavior of a real user. +type IntegrationTxFactory struct { + commonfactory.CoreTxFactory + + grpcHandler grpc.Handler + network network.Network + ec testutiltypes.TestEncodingConfig +} + +// New creates a new IntegrationTxFactory instance +func New( + network network.Network, + grpcHandler grpc.Handler, +) TxFactory { + cf := commonfactory.New(network, grpcHandler) + return &IntegrationTxFactory{ + CoreTxFactory: cf, + grpcHandler: grpcHandler, + network: network, + ec: network.GetEncodingConfig(), + } +} + +// GetEvmTransactionResponseFromTxResult returns the MsgEthereumTxResponse from the provided txResult. +func (tf *IntegrationTxFactory) GetEvmTransactionResponseFromTxResult( + txResult abcitypes.ExecTxResult, +) (*evmtypes.MsgEthereumTxResponse, error) { + var txData sdktypes.TxMsgData + if err := tf.ec.Codec.Unmarshal(txResult.Data, &txData); err != nil { + return nil, errorsmod.Wrap(err, "failed to unmarshal tx data") + } + + if len(txData.MsgResponses) != 1 { + return nil, fmt.Errorf("expected 1 message response, got %d", len(txData.MsgResponses)) + } + + var evmRes evmtypes.MsgEthereumTxResponse + if err := proto.Unmarshal(txData.MsgResponses[0].Value, &evmRes); err != nil { + return nil, errorsmod.Wrap(err, "failed to unmarshal evm tx response") + } + + return &evmRes, nil +} + +// populateEvmTxArgsWithDefault populates the missing fields in the provided EvmTxArgs with default values. +// If no GasLimit is present it will estimate the gas needed for the transaction. +func (tf *IntegrationTxFactory) populateEvmTxArgsWithDefault( + fromAddr common.Address, + txArgs evmtypes.EvmTxArgs, +) (evmtypes.EvmTxArgs, error) { + if txArgs.ChainID == nil { + txArgs.ChainID = tf.network.GetEIP155ChainID() + } + + if txArgs.Nonce == 0 { + accountResp, err := tf.grpcHandler.GetEvmAccount(fromAddr) + if err != nil { + return evmtypes.EvmTxArgs{}, errorsmod.Wrapf(err, "failed to get evm account: %s", fromAddr.String()) + } + txArgs.Nonce = accountResp.GetNonce() + } + + // If there is no GasPrice it is assumed this is a DynamicFeeTx. + // If fields are empty they are populated with current dynamic values. + if txArgs.GasPrice == nil { + if txArgs.GasTipCap == nil { + txArgs.GasTipCap = big.NewInt(1) + } + if txArgs.GasFeeCap == nil { + baseFeeResp, err := tf.grpcHandler.GetEvmBaseFee() + if err != nil { + return evmtypes.EvmTxArgs{}, errorsmod.Wrap(err, "failed to get base fee") + } + txArgs.GasFeeCap = baseFeeResp.BaseFee.BigInt() + } + } + + // If the gas limit is not set, estimate it + // through the /simulate endpoint. + if txArgs.GasLimit == 0 { + gasLimit, err := tf.EstimateGasLimit(&fromAddr, &txArgs) + if err != nil { + return evmtypes.EvmTxArgs{}, errorsmod.Wrap(err, "failed to estimate gas limit") + } + txArgs.GasLimit = gasLimit + } + + return txArgs, nil +} + +func (tf *IntegrationTxFactory) encodeTx(tx sdktypes.Tx) ([]byte, error) { + txConfig := tf.ec.TxConfig + txBytes, err := txConfig.TxEncoder()(tx) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to encode tx") + } + return txBytes, nil +} + +func (tf *IntegrationTxFactory) buildSignedTx(msg evmtypes.MsgEthereumTx) (signing.Tx, error) { + txConfig := tf.ec.TxConfig + txBuilder := txConfig.NewTxBuilder() + return msg.BuildTx(txBuilder, tf.network.GetBaseDenom()) +} + +// checkEthTxResponse checks if the response is valid and returns the MsgEthereumTxResponse +func (tf *IntegrationTxFactory) checkEthTxResponse(res *abcitypes.ExecTxResult) error { + var txData sdktypes.TxMsgData + if !res.IsOK() { + return fmt.Errorf("tx failed with Code: %d, Logs: %s", res.Code, res.Log) + } + + cdc := tf.ec.Codec + if err := cdc.Unmarshal(res.Data, &txData); err != nil { + return errorsmod.Wrap(err, "failed to unmarshal tx data") + } + + if len(txData.MsgResponses) != 1 { + return fmt.Errorf("expected 1 message response, got %d", len(txData.MsgResponses)) + } + + var evmRes evmtypes.MsgEthereumTxResponse + if err := proto.Unmarshal(txData.MsgResponses[0].Value, &evmRes); err != nil { + return errorsmod.Wrap(err, "failed to unmarshal evm tx response") + } + + if strings.Contains(evmRes.VmError, vm.ErrOutOfGas.Error()) { + return fmt.Errorf("eth tx ran out of gas; gas used: %d", evmRes.GasUsed) + } + + if evmRes.Failed() { + return fmt.Errorf("tx failed with VmError: %v, Logs: %s", evmRes.VmError, res.GetLog()) + } + return nil +} diff --git a/testutil/integration/os/factory/helpers.go b/testutil/integration/os/factory/helpers.go new file mode 100644 index 00000000..7ff4ad35 --- /dev/null +++ b/testutil/integration/os/factory/helpers.go @@ -0,0 +1,41 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package factory + +import ( + "strings" + + errorsmod "cosmossdk.io/errors" + "github.com/ethereum/go-ethereum/common" + "github.com/evmos/os/precompiles/testutil" + evmtypes "github.com/evmos/os/x/evm/types" +) + +// buildMsgEthereumTx builds an Ethereum transaction from the given arguments and populates the From field. +func buildMsgEthereumTx(txArgs evmtypes.EvmTxArgs, fromAddr common.Address) evmtypes.MsgEthereumTx { + msgEthereumTx := evmtypes.NewTx(&txArgs) + msgEthereumTx.From = fromAddr.String() + return *msgEthereumTx +} + +// CheckError is a helper function to check if the error is the expected one. +func CheckError(err error, logCheckArgs testutil.LogCheckArgs) error { + switch { + case logCheckArgs.ExpPass && err == nil: + return nil + case !logCheckArgs.ExpPass && err == nil: + return errorsmod.Wrap(err, "expected error but got none") + case logCheckArgs.ExpPass && err != nil: + return errorsmod.Wrap(err, "expected no error but got one") + case logCheckArgs.ErrContains == "": + // NOTE: if err contains is empty, we return the error as it is + return errorsmod.Wrap(err, "ErrContains needs to be filled") + case err == nil: + panic("unexpected state: err is nil; this should not happen") + case !strings.Contains(err.Error(), logCheckArgs.ErrContains): + return errorsmod.Wrapf(err, "expected different error; wanted %q", logCheckArgs.ErrContains) + } + + return nil +} diff --git a/testutil/integration/os/factory/sign.go b/testutil/integration/os/factory/sign.go new file mode 100644 index 00000000..376b5428 --- /dev/null +++ b/testutil/integration/os/factory/sign.go @@ -0,0 +1,22 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package factory + +import ( + errorsmod "cosmossdk.io/errors" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + gethtypes "github.com/ethereum/go-ethereum/core/types" + evmtypes "github.com/evmos/os/x/evm/types" + "github.com/realiotech/realio-network/testutil/tx" +) + +// SignMsgEthereumTx signs a MsgEthereumTx with the provided private key and chainID. +func (tf *IntegrationTxFactory) SignMsgEthereumTx(privKey cryptotypes.PrivKey, msgEthereumTx evmtypes.MsgEthereumTx) (evmtypes.MsgEthereumTx, error) { + ethChainID := tf.network.GetEIP155ChainID() + signer := gethtypes.LatestSignerForChainID(ethChainID) + err := msgEthereumTx.Sign(signer, tx.NewSigner(privKey)) + if err != nil { + return evmtypes.MsgEthereumTx{}, errorsmod.Wrap(err, "failed to sign transaction") + } + return msgEthereumTx, nil +} diff --git a/testutil/integration/os/factory/types.go b/testutil/integration/os/factory/types.go new file mode 100644 index 00000000..390781bf --- /dev/null +++ b/testutil/integration/os/factory/types.go @@ -0,0 +1,44 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package factory + +import ( + sdkmath "cosmossdk.io/math" + sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/accounts/abi" + evmtypes "github.com/evmos/os/x/evm/types" +) + +// CosmosTxArgs contains the params to create a cosmos tx +type CosmosTxArgs struct { + // ChainID is the chain's id in cosmos format, e.g. 'evmos_9000-1' + ChainID string + // Gas to be used on the tx + Gas uint64 + // GasPrice to use on tx + GasPrice *sdkmath.Int + // Fees is the fee to be used on the tx (amount and denom) + Fees sdktypes.Coins + // FeeGranter is the account address of the fee granter + FeeGranter sdktypes.AccAddress + // Msgs slice of messages to include on the tx + Msgs []sdktypes.Msg +} + +// CallArgs is a struct to define all relevant data to call a smart contract. +type CallArgs struct { + // ContractABI is the ABI of the contract to call. + ContractABI abi.ABI + // MethodName is the name of the method to call. + MethodName string + // Args are the arguments to pass to the method. + Args []interface{} +} + +// ContractDeploymentData is a struct to define all relevant data to deploy a smart contract. +type ContractDeploymentData struct { + // Contract is the compiled contract to deploy. + Contract evmtypes.CompiledContract + // ConstructorArgs are the arguments to pass to the constructor. + ConstructorArgs []interface{} +} diff --git a/testutil/integration/os/grpc/evm.go b/testutil/integration/os/grpc/evm.go new file mode 100644 index 00000000..f3ed64ed --- /dev/null +++ b/testutil/integration/os/grpc/evm.go @@ -0,0 +1,65 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package grpc + +import ( + "context" + "errors" + + sdktypes "github.com/cosmos/cosmos-sdk/types" + + "github.com/ethereum/go-ethereum/common" + + "github.com/evmos/os/x/evm/core/vm" + evmtypes "github.com/evmos/os/x/evm/types" +) + +// GetEvmAccount returns the EVM account for the given address. +func (gqh *IntegrationHandler) GetEvmAccount(address common.Address) (*evmtypes.QueryAccountResponse, error) { + evmClient := gqh.network.GetEvmClient() + return evmClient.Account(context.Background(), &evmtypes.QueryAccountRequest{ + Address: address.String(), + }) +} + +// EstimateGas returns the estimated gas for the given call args. +func (gqh *IntegrationHandler) EstimateGas(args []byte, gasCap uint64) (*evmtypes.EstimateGasResponse, error) { + evmClient := gqh.network.GetEvmClient() + res, err := evmClient.EstimateGas(context.Background(), &evmtypes.EthCallRequest{ + Args: args, + GasCap: gasCap, + }) + if err != nil { + return nil, err + } + + // handle case where there's a revert related error + if res.Failed() { + if (res.VmError != vm.ErrExecutionReverted.Error()) || len(res.Ret) == 0 { + return nil, errors.New(res.VmError) + } + return nil, evmtypes.NewExecErrorWithReason(res.Ret) + } + + return res, err +} + +// GetEvmParams returns the EVM module params. +func (gqh *IntegrationHandler) GetEvmParams() (*evmtypes.QueryParamsResponse, error) { + evmClient := gqh.network.GetEvmClient() + return evmClient.Params(context.Background(), &evmtypes.QueryParamsRequest{}) +} + +// GetEvmParams returns the EVM module params. +func (gqh *IntegrationHandler) GetEvmBaseFee() (*evmtypes.QueryBaseFeeResponse, error) { + evmClient := gqh.network.GetEvmClient() + return evmClient.BaseFee(context.Background(), &evmtypes.QueryBaseFeeRequest{}) +} + +// GetBalanceFromEVM returns the balance for the given address. +func (gqh *IntegrationHandler) GetBalanceFromEVM(address sdktypes.AccAddress) (*evmtypes.QueryBalanceResponse, error) { + evmClient := gqh.network.GetEvmClient() + return evmClient.Balance(context.Background(), &evmtypes.QueryBalanceRequest{ + Address: common.BytesToAddress(address).Hex(), + }) +} diff --git a/testutil/integration/os/grpc/feemarket.go b/testutil/integration/os/grpc/feemarket.go new file mode 100644 index 00000000..d5094ba9 --- /dev/null +++ b/testutil/integration/os/grpc/feemarket.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 grpc + +import ( + "context" + + feemarkettypes "github.com/evmos/os/x/feemarket/types" +) + +// GetBaseFee returns the base fee from the feemarket module. +func (gqh *IntegrationHandler) GetBaseFee() (*feemarkettypes.QueryBaseFeeResponse, error) { + feeMarketClient := gqh.network.GetFeeMarketClient() + return feeMarketClient.BaseFee(context.Background(), &feemarkettypes.QueryBaseFeeRequest{}) +} + +// GetBaseFee returns the base fee from the feemarket module. +func (gqh *IntegrationHandler) GetFeeMarketParams() (*feemarkettypes.QueryParamsResponse, error) { + feeMarketClient := gqh.network.GetFeeMarketClient() + return feeMarketClient.Params(context.Background(), &feemarkettypes.QueryParamsRequest{}) +} diff --git a/testutil/integration/os/grpc/gov.go b/testutil/integration/os/grpc/gov.go new file mode 100644 index 00000000..e916c7d8 --- /dev/null +++ b/testutil/integration/os/grpc/gov.go @@ -0,0 +1,28 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package grpc + +import ( + "fmt" + "slices" + + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" +) + +// GetGovParams returns the gov params from the gov module. +func (gqh *IntegrationHandler) GetGovParams(paramsType string) (*govtypes.QueryParamsResponse, error) { + possibleTypes := []string{"deposit", "tallying", "voting"} + if !slices.Contains(possibleTypes, paramsType) { + return nil, fmt.Errorf("invalid params type: %s\npossible types: %s", paramsType, possibleTypes) + } + + govClient := gqh.network.GetGovClient() + return govClient.Params(gqh.network.GetContext(), &govtypes.QueryParamsRequest{ParamsType: paramsType}) +} + +// GetProposal returns the proposal from the gov module. +func (gqh *IntegrationHandler) GetProposal(proposalID uint64) (*govtypes.QueryProposalResponse, error) { + govClient := gqh.network.GetGovClient() + return govClient.Proposal(gqh.network.GetContext(), &govtypes.QueryProposalRequest{ProposalId: proposalID}) +} diff --git a/testutil/integration/os/grpc/grpc.go b/testutil/integration/os/grpc/grpc.go new file mode 100644 index 00000000..659958c1 --- /dev/null +++ b/testutil/integration/os/grpc/grpc.go @@ -0,0 +1,55 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package grpc + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + "github.com/ethereum/go-ethereum/common" + commongrpc "github.com/evmos/os/testutil/integration/common/grpc" + evmtypes "github.com/evmos/os/x/evm/types" + feemarkettypes "github.com/evmos/os/x/feemarket/types" + "github.com/realiotech/realio-network/testutil/integration/os/network" +) + +// Handler is an interface that defines the methods that are used to query +// the network's modules via gRPC. +type Handler interface { + commongrpc.Handler + + // EVM methods + GetEvmAccount(address common.Address) (*evmtypes.QueryAccountResponse, error) + EstimateGas(args []byte, GasCap uint64) (*evmtypes.EstimateGasResponse, error) + GetEvmParams() (*evmtypes.QueryParamsResponse, error) + GetEvmBaseFee() (*evmtypes.QueryBaseFeeResponse, error) + GetBalanceFromEVM(address sdk.AccAddress) (*evmtypes.QueryBalanceResponse, error) + + // FeeMarket methods + GetBaseFee() (*feemarkettypes.QueryBaseFeeResponse, error) + GetFeeMarketParams() (*feemarkettypes.QueryParamsResponse, error) + + // Gov methods + GetProposal(proposalID uint64) (*govtypes.QueryProposalResponse, error) + GetGovParams(paramsType string) (*govtypes.QueryParamsResponse, error) +} + +var _ Handler = (*IntegrationHandler)(nil) + +// IntegrationHandler is a helper struct to query the network's modules +// via gRPC. This is to simulate the behavior of a real user and avoid querying +// the modules directly. +type IntegrationHandler struct { + // We take the IntegrationHandler from common/grpc to get the common methods. + *commongrpc.IntegrationHandler + network network.Network +} + +// NewIntegrationHandler creates a new IntegrationHandler instance. +func NewIntegrationHandler(network network.Network) Handler { + return &IntegrationHandler{ + // Is there a better way to do this? + IntegrationHandler: commongrpc.NewIntegrationHandler(network), + network: network, + } +} diff --git a/testutil/integration/os/keyring/keyring.go b/testutil/integration/os/keyring/keyring.go new file mode 100644 index 00000000..52fbd199 --- /dev/null +++ b/testutil/integration/os/keyring/keyring.go @@ -0,0 +1,120 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package keyring + +import ( + "fmt" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + utiltx "github.com/realiotech/realio-network/testutil/tx" +) + +type Key struct { + Addr common.Address + AccAddr sdktypes.AccAddress + Priv cryptotypes.PrivKey +} + +func NewKey() Key { + addr, privKey := utiltx.NewAddrKey() + return Key{ + Addr: addr, + AccAddr: sdktypes.AccAddress(addr.Bytes()), + Priv: privKey, + } +} + +type Keyring interface { + // GetPrivKey returns the private key of the account at the given keyring index. + GetPrivKey(index int) cryptotypes.PrivKey + // GetAddr returns the address of the account at the given keyring index. + GetAddr(index int) common.Address + // GetAccAddr returns the SDK address of the account at the given keyring index. + GetAccAddr(index int) sdktypes.AccAddress + // GetAllAccAddrs returns all the SDK addresses of the accounts in the keyring. + GetAllAccAddrs() []sdktypes.AccAddress + // GetKey returns the key at the given keyring index + GetKey(index int) Key + // GetKeys returns all the keys + GetKeys() []Key + + // AddKey adds a new account to the keyring + AddKey() int + + // Sign signs message with the specified account. + Sign(index int, msg []byte) ([]byte, error) +} + +// IntegrationKeyring is a keyring designed for integration tests. +type IntegrationKeyring struct { + keys []Key +} + +var _ Keyring = (*IntegrationKeyring)(nil) + +// New returns a new keyring with nAccs accounts. +func New(nAccs int) Keyring { + accs := make([]Key, 0, nAccs) + for i := 0; i < nAccs; i++ { + acc := NewKey() + accs = append(accs, acc) + } + return &IntegrationKeyring{ + keys: accs, + } +} + +// GetPrivKey returns the private key of the specified account. +func (kr *IntegrationKeyring) GetPrivKey(index int) cryptotypes.PrivKey { + return kr.keys[index].Priv +} + +// GetAddr returns the address of the specified account. +func (kr *IntegrationKeyring) GetAddr(index int) common.Address { + return kr.keys[index].Addr +} + +// GetAccAddr returns the sdk address of the specified account. +func (kr *IntegrationKeyring) GetAccAddr(index int) sdktypes.AccAddress { + return kr.keys[index].AccAddr +} + +// GetAllAccAddrs returns all the sdk addresses of the accounts in the keyring. +func (kr *IntegrationKeyring) GetAllAccAddrs() []sdktypes.AccAddress { + accs := make([]sdktypes.AccAddress, 0, len(kr.keys)) + for _, key := range kr.keys { + accs = append(accs, key.AccAddr) + } + return accs +} + +// GetKey returns the key specified by index. +func (kr *IntegrationKeyring) GetKey(index int) Key { + return kr.keys[index] +} + +// GetKeys returns all keys from the keyring. +func (kr *IntegrationKeyring) GetKeys() []Key { + return kr.keys +} + +// AddKey adds a new account to the keyring. It returns the index for the key. +func (kr *IntegrationKeyring) AddKey() int { + acc := NewKey() + index := len(kr.keys) + kr.keys = append(kr.keys, acc) + + return index +} + +// Sign signs message with the specified key. +func (kr *IntegrationKeyring) Sign(index int, msg []byte) ([]byte, error) { + privKey := kr.GetPrivKey(index) + if privKey == nil { + return nil, fmt.Errorf("no private key for account %d", index) + } + return privKey.Sign(msg) +} diff --git a/testutil/integration/os/network/abci.go b/testutil/integration/os/network/abci.go new file mode 100644 index 00000000..b1cd2069 --- /dev/null +++ b/testutil/integration/os/network/abci.go @@ -0,0 +1,101 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package network + +import ( + "time" + + storetypes "cosmossdk.io/store/types" + abcitypes "github.com/cometbft/cometbft/abci/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" +) + +// NextBlock is a private helper function that runs the EndBlocker logic, commits the changes, +// updates the header and runs the BeginBlocker +func (n *IntegrationNetwork) NextBlock() error { + return n.NextBlockAfter(time.Second) +} + +// NextBlockAfter is a private helper function that runs the FinalizeBlock logic, updates the context and +// commits the changes to have a block time after the given duration. +func (n *IntegrationNetwork) NextBlockAfter(duration time.Duration) error { + _, err := n.finalizeBlockAndCommit(duration) + return err +} + +// NextBlockWithTxs is a helper function that runs the FinalizeBlock logic +// with the provided tx bytes, updates the context and +// commits the changes to have a block time after the given duration. +func (n *IntegrationNetwork) NextBlockWithTxs(txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) { + return n.finalizeBlockAndCommit(time.Second, txBytes...) +} + +// finalizeBlockAndCommit is a private helper function that runs the FinalizeBlock logic +// with the provided txBytes, updates the context and +// commits the changes to have a block time after the given duration. +func (n *IntegrationNetwork) finalizeBlockAndCommit(duration time.Duration, txBytes ...[]byte) (*abcitypes.ResponseFinalizeBlock, error) { + header := n.ctx.BlockHeader() + // Update block header and BeginBlock + header.Height++ + header.AppHash = n.app.LastCommitID().Hash + // Calculate new block time after duration + newBlockTime := header.Time.Add(duration) + header.Time = newBlockTime + + // FinalizeBlock to run endBlock, deliverTx & beginBlock logic + req := buildFinalizeBlockReq(header, n.valSet.Validators, txBytes...) + + res, err := n.app.FinalizeBlock(req) + if err != nil { + return nil, err + } + + newCtx := n.app.BaseApp.NewContextLegacy(false, header) + + // Update context header + newCtx = newCtx.WithMinGasPrices(n.ctx.MinGasPrices()) + newCtx = newCtx.WithKVGasConfig(n.ctx.KVGasConfig()) + newCtx = newCtx.WithTransientKVGasConfig(n.ctx.TransientKVGasConfig()) + newCtx = newCtx.WithConsensusParams(n.ctx.ConsensusParams()) + // This might have to be changed with time if we want to test gas limits + newCtx = newCtx.WithBlockGasMeter(storetypes.NewInfiniteGasMeter()) + newCtx = newCtx.WithVoteInfos(req.DecidedLastCommit.GetVotes()) + n.ctx = newCtx + + // commit changes + _, err = n.app.Commit() + + return res, err +} + +// buildFinalizeBlockReq is a helper function to build +// properly the FinalizeBlock request +func buildFinalizeBlockReq(header cmtproto.Header, validators []*cmttypes.Validator, txs ...[]byte) *abcitypes.RequestFinalizeBlock { + // add validator's commit info to allocate corresponding tokens to validators + ci := getCommitInfo(validators) + return &abcitypes.RequestFinalizeBlock{ + Height: header.Height, + DecidedLastCommit: ci, + Hash: header.AppHash, + NextValidatorsHash: header.ValidatorsHash, + ProposerAddress: header.ProposerAddress, + Time: header.Time, + Txs: txs, + } +} + +func getCommitInfo(validators []*cmttypes.Validator) abcitypes.CommitInfo { + voteInfos := make([]abcitypes.VoteInfo, len(validators)) + for i, val := range validators { + voteInfos[i] = abcitypes.VoteInfo{ + Validator: abcitypes.Validator{ + Address: val.Address, + Power: val.VotingPower, + }, + BlockIdFlag: cmtproto.BlockIDFlagCommit, + } + } + return abcitypes.CommitInfo{Votes: voteInfos} +} diff --git a/testutil/integration/os/network/amounts.go b/testutil/integration/os/network/amounts.go new file mode 100644 index 00000000..363db058 --- /dev/null +++ b/testutil/integration/os/network/amounts.go @@ -0,0 +1,75 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +package network + +import ( + "math/big" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + testconstants "github.com/evmos/os/testutil/constants" + "github.com/evmos/os/types" + evmtypes "github.com/evmos/os/x/evm/types" +) + +type InitialAmounts struct { + Base math.Int + Evm math.Int +} + +func DefaultInitialAmounts() InitialAmounts { + baseCoinInfo := testconstants.ExampleChainCoinInfo[defaultChain] + + return InitialAmounts{ + Base: GetInitialAmount(baseCoinInfo.Decimals), + Evm: GetInitialAmount(baseCoinInfo.Decimals), + } +} + +func DefaultInitialBondedAmount() math.Int { + baseCoinInfo := testconstants.ExampleChainCoinInfo[defaultChain] + + return GetInitialBondedAmount(baseCoinInfo.Decimals) +} + +func GetInitialAmount(decimals evmtypes.Decimals) math.Int { + if err := decimals.Validate(); err != nil { + panic("unsupported decimals") + } + + // initialBalance defines the initial balance represented in 18 decimals. + initialBalance, _ := math.NewIntFromString("100_000_000_000_000_000_000_000") + + // 18 decimals is the most precise representation we can have, for this + // reason we have to divide the initial balance by the decimals value to + // have the specific representation. + return initialBalance.Quo(decimals.ConversionFactor()) +} + +func GetInitialBondedAmount(decimals evmtypes.Decimals) math.Int { + if err := decimals.Validate(); err != nil { + panic("unsupported decimals") + } + + // initialBondedAmount represents the amount of tokens that each validator will + // have initially bonded expressed in the 18 decimals representation. + sdk.DefaultPowerReduction = math.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(decimals)), nil)) + initialBondedAmount := sdk.TokensFromConsensusPower(1, types.AttoPowerReduction) + + return initialBondedAmount.Quo(decimals.ConversionFactor()) +} + +func GetInitialBaseFeeAmount(decimals evmtypes.Decimals) math.LegacyDec { + if err := decimals.Validate(); err != nil { + panic("unsupported decimals") + } + + switch decimals { + case evmtypes.EighteenDecimals: + return math.LegacyNewDec(1_000_000_000) + case evmtypes.SixDecimals: + return math.LegacyNewDecWithPrec(1, 3) + default: + panic("base fee not specified") + } +} diff --git a/testutil/integration/os/network/chain_id_modifiers.go b/testutil/integration/os/network/chain_id_modifiers.go new file mode 100644 index 00000000..88d09998 --- /dev/null +++ b/testutil/integration/os/network/chain_id_modifiers.go @@ -0,0 +1,76 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +// +// This files contains handler for the testing suite that has to be run to +// modify the chain configuration depending on the chainID + +package network + +import ( + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + testconstants "github.com/evmos/os/testutil/constants" + erc20types "github.com/evmos/os/x/erc20/types" +) + +// updateErc20GenesisStateForChainID modify the default genesis state for the +// bank module of the testing suite depending on the chainID. +func updateBankGenesisStateForChainID(bankGenesisState banktypes.GenesisState) banktypes.GenesisState { + metadata := generateBankGenesisMetadata() + bankGenesisState.DenomMetadata = []banktypes.Metadata{metadata} + + return bankGenesisState +} + +// generateBankGenesisMetadata generates the metadata +// for the Evm coin depending on the chainID. +func generateBankGenesisMetadata() banktypes.Metadata { + return banktypes.Metadata{ + Description: "The native EVM, governance and staking token of the evmOS example chain", + Base: "aevmos", + DenomUnits: []*banktypes.DenomUnit{ + { + Denom: testconstants.ExampleAttoDenom, + Exponent: 0, + }, + { + Denom: testconstants.ExampleDisplayDenom, + Exponent: 18, + }, + }, + Name: "evmOS", + Symbol: "EVMOS", + Display: testconstants.ExampleDisplayDenom, + } +} + +// updateErc20GenesisStateForChainID modify the default genesis state for the +// erc20 module on the testing suite depending on the chainID. +func updateErc20GenesisStateForChainID(chainID string, erc20GenesisState erc20types.GenesisState) erc20types.GenesisState { + erc20GenesisState.TokenPairs = updateErc20TokenPairs(chainID, erc20GenesisState.TokenPairs) + + return erc20GenesisState +} + +// updateErc20TokenPairs modifies the erc20 token pairs to use the correct +// WEVMOS depending on ChainID +func updateErc20TokenPairs(chainID string, tokenPairs []erc20types.TokenPair) []erc20types.TokenPair { + testnetAddress := GetWEVMOSContractHex(chainID) + coinInfo := testconstants.ExampleChainCoinInfo[chainID] + + mainnetAddress := GetWEVMOSContractHex(testconstants.ExampleChainID) + + updatedTokenPairs := make([]erc20types.TokenPair, len(tokenPairs)) + for i, tokenPair := range tokenPairs { + if tokenPair.Erc20Address == mainnetAddress { + updatedTokenPairs[i] = erc20types.TokenPair{ + Erc20Address: testnetAddress, + Denom: coinInfo.Denom, + Enabled: tokenPair.Enabled, + ContractOwner: tokenPair.ContractOwner, + } + } else { + updatedTokenPairs[i] = tokenPair + } + } + return updatedTokenPairs +} diff --git a/testutil/integration/os/network/clients.go b/testutil/integration/os/network/clients.go new file mode 100644 index 00000000..9aa66893 --- /dev/null +++ b/testutil/integration/os/network/clients.go @@ -0,0 +1,93 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package network + +import ( + "github.com/cosmos/cosmos-sdk/baseapp" + sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module/testutil" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + erc20types "github.com/evmos/os/x/erc20/types" + evmtypes "github.com/evmos/os/x/evm/types" + feemarkettypes "github.com/evmos/os/x/feemarket/types" +) + +func getQueryHelper(ctx sdktypes.Context, encCfg testutil.TestEncodingConfig) *baseapp.QueryServiceTestHelper { + interfaceRegistry := encCfg.InterfaceRegistry + // This is needed so that state changes are not committed in precompiles + // simulations. + cacheCtx, _ := ctx.CacheContext() + return baseapp.NewQueryServerTestHelper(cacheCtx, interfaceRegistry) +} + +func (n *IntegrationNetwork) GetERC20Client() erc20types.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + erc20types.RegisterQueryServer(queryHelper, n.app.Erc20Keeper) + return erc20types.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetEvmClient() evmtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + evmtypes.RegisterQueryServer(queryHelper, n.app.EVMKeeper) + return evmtypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetGovClient() govtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + govtypes.RegisterQueryServer(queryHelper, govkeeper.NewQueryServer(&n.app.GovKeeper)) + return govtypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetBankClient() banktypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + banktypes.RegisterQueryServer(queryHelper, n.app.BankKeeper) + return banktypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetFeeMarketClient() feemarkettypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + feemarkettypes.RegisterQueryServer(queryHelper, n.app.FeeMarketKeeper) + return feemarkettypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetAuthClient() authtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + authtypes.RegisterQueryServer(queryHelper, authkeeper.NewQueryServer(n.app.AccountKeeper)) + return authtypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetAuthzClient() authz.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + authz.RegisterQueryServer(queryHelper, n.app.AuthzKeeper) + return authz.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetStakingClient() stakingtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + stakingtypes.RegisterQueryServer(queryHelper, stakingkeeper.Querier{Keeper: n.app.StakingKeeper}) + return stakingtypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetDistrClient() distrtypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + distrtypes.RegisterQueryServer(queryHelper, distrkeeper.Querier{Keeper: n.app.DistrKeeper}) + return distrtypes.NewQueryClient(queryHelper) +} + +func (n *IntegrationNetwork) GetMintClient() minttypes.QueryClient { + queryHelper := getQueryHelper(n.GetContext(), n.GetEncodingConfig()) + minttypes.RegisterQueryServer(queryHelper, mintkeeper.NewQueryServerImpl(n.app.MintKeeper)) + return minttypes.NewQueryClient(queryHelper) +} diff --git a/testutil/integration/os/network/coins.go b/testutil/integration/os/network/coins.go new file mode 100644 index 00000000..faa8f4ee --- /dev/null +++ b/testutil/integration/os/network/coins.go @@ -0,0 +1,90 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package network + +import ( + testconstants "github.com/evmos/os/testutil/constants" + evmtypes "github.com/evmos/os/x/evm/types" +) + +type CoinInfo struct { + Denom string + Decimals evmtypes.Decimals +} + +// ChainCoins information for the coins required from the chian to operate: +// - baseCoin: represents the base coin used to pay gas fees and staking in the +// Cosmos context. +// - evmCoin: represents the evm coin used to pay Ethereum +// transactions fees. +type ChainCoins struct { + // TODO: not sure if this is an overkill. Do we want to customize the + // decimals of the base denom? Maybe not.. + baseCoin *CoinInfo + evmCoin *CoinInfo +} + +// DefaultChainCoins returns the default values used for the ChainCoins in which +// base and evm denom are the same. +func DefaultChainCoins() ChainCoins { + baseCoinInfo := testconstants.ExampleChainCoinInfo[defaultChain] + + // baseCoin is used for both base and evm coin as default.. + baseCoin := getCoinInfo(baseCoinInfo) + evmCoin := getCoinInfo(baseCoinInfo) + + return ChainCoins{ + baseCoin: &baseCoin, + evmCoin: &evmCoin, + } +} + +func getCoinInfo(coinInfo evmtypes.EvmCoinInfo) CoinInfo { + return CoinInfo{ + Denom: coinInfo.Denom, + Decimals: coinInfo.Decimals, + } +} + +func (cc ChainCoins) BaseCoin() CoinInfo { + return *cc.baseCoin +} + +func (cc ChainCoins) EVMCoin() CoinInfo { + return *cc.evmCoin +} + +func (cc ChainCoins) BaseDenom() string { + return cc.baseCoin.Denom +} + +func (cc ChainCoins) EVMDenom() string { + return cc.evmCoin.Denom +} + +func (cc ChainCoins) BaseDecimals() evmtypes.Decimals { + return cc.baseCoin.Decimals +} + +func (cc ChainCoins) EVMDecimals() evmtypes.Decimals { + return cc.evmCoin.Decimals +} + +func (cc ChainCoins) IsBaseEqualToEVM() bool { + return cc.BaseDenom() == cc.EVMDenom() +} + +// DenomDecimalsMap returns a map of unique Denom -> Decimals for the chain +// coins. +func (cc ChainCoins) DenomDecimalsMap() map[string]evmtypes.Decimals { + chainDenomDecimals := map[string]evmtypes.Decimals{ + cc.BaseDenom(): cc.BaseDecimals(), + } + + // Insert the evm denom if base and evm denom are different. + if !cc.IsBaseEqualToEVM() { + chainDenomDecimals[cc.EVMDenom()] = cc.EVMDecimals() + } + return chainDenomDecimals +} diff --git a/testutil/integration/os/network/config.go b/testutil/integration/os/network/config.go new file mode 100644 index 00000000..e18a4ef8 --- /dev/null +++ b/testutil/integration/os/network/config.go @@ -0,0 +1,206 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package network + +import ( + "fmt" + "math/big" + + "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/baseapp" + sdktypes "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + testconstants "github.com/evmos/os/testutil/constants" + evmostypes "github.com/evmos/os/types" + evmtypes "github.com/evmos/os/x/evm/types" + testtx "github.com/realiotech/realio-network/testutil/tx" +) + +// defaultChain represents the default chain ID used in the suite setup. +var defaultChain = testconstants.ExampleChainID + +// Config defines the configuration for a chain. +// It allows for customization of the network to adjust to +// testing needs. +type Config struct { + chainID string + eip155ChainID *big.Int + + customGenesisState CustomGenesisState + + customBaseAppOpts []func(*baseapp.BaseApp) + + amountOfValidators int + operatorsAddrs []sdktypes.AccAddress + initialBondedAmount math.Int + + chainCoins ChainCoins + initialAmounts InitialAmounts + // otherCoinDenoms represents the other possible coin denominations that can be passed during + // test suite intialization to provide other coins initial balances. + otherCoinDenoms []string + preFundedAccounts []sdktypes.AccAddress + balances []banktypes.Balance +} + +type CustomGenesisState map[string]interface{} + +// DefaultConfig returns the default configuration for a chain. +func DefaultConfig() Config { + account, _ := testtx.NewAccAddressAndKey() + + return Config{ + chainID: testconstants.ExampleChainID, + eip155ChainID: big.NewInt(testconstants.ExampleEIP155ChainID), + chainCoins: DefaultChainCoins(), + initialAmounts: DefaultInitialAmounts(), + initialBondedAmount: DefaultInitialBondedAmount(), + amountOfValidators: 3, + + // Only one account besides the validators + preFundedAccounts: []sdktypes.AccAddress{account}, + + // NOTE: Per default, the balances are left empty, and the pre-funded accounts are used. + balances: nil, + customGenesisState: nil, + } +} + +// getGenAccountsAndBalances takes the network configuration and returns the used +// genesis accounts and balances. +// +// NOTE: If the balances are set, the pre-funded accounts are ignored. +func getGenAccountsAndBalances(cfg Config, validators []stakingtypes.Validator) (genAccounts []authtypes.GenesisAccount, balances []banktypes.Balance) { + if len(cfg.balances) > 0 { + balances = cfg.balances + accounts := getAccAddrsFromBalances(balances) + genAccounts = createGenesisAccounts(accounts) + } else { + genAccounts = createGenesisAccounts(cfg.preFundedAccounts) + + denomDecimals := cfg.chainCoins.DenomDecimalsMap() + + // All extra denom specified are represented with the base coin decimal. + for _, denom := range cfg.otherCoinDenoms { + denomDecimals[denom] = cfg.chainCoins.BaseDecimals() + } + + balances = createBalances(cfg.preFundedAccounts, denomDecimals) + } + + // append validators to genesis accounts and balances + valAccs := make([]sdktypes.AccAddress, len(validators)) + for i, v := range validators { + valAddr, err := sdktypes.ValAddressFromBech32(v.OperatorAddress) + if err != nil { + panic(fmt.Sprintf("failed to derive validator address from %q: %s", v.OperatorAddress, err.Error())) + } + valAccs[i] = sdktypes.AccAddress(valAddr.Bytes()) + } + genAccounts = append(genAccounts, createGenesisAccounts(valAccs)...) + + return +} + +// ConfigOption defines a function that can modify the NetworkConfig. +// The purpose of this is to force to be declarative when the default configuration +// requires to be changed. +type ConfigOption func(*Config) + +// WithChainID sets a custom chainID for the network. Changing the chainID +// change automatically also the EVM coin used. It panics if the chainID is invalid. +func WithChainID(chainID string) ConfigOption { + eip155ChainID, err := evmostypes.ParseChainID(chainID) + if err != nil { + panic(err) + } + + evmCoinInfo, found := testconstants.ExampleChainCoinInfo[chainID] + if !found { + panic(fmt.Sprintf( + "chain id %q not found in chain coin info; available: %v", + chainID, + testconstants.ExampleChainCoinInfo, + )) + } + + return func(cfg *Config) { + cfg.chainID = chainID + cfg.eip155ChainID = eip155ChainID + + if cfg.chainCoins.IsBaseEqualToEVM() { + cfg.chainCoins.baseCoin.Denom = evmCoinInfo.Denom + cfg.chainCoins.baseCoin.Decimals = evmCoinInfo.Decimals + } + cfg.chainCoins.evmCoin.Denom = evmCoinInfo.Denom + cfg.chainCoins.evmCoin.Decimals = evmCoinInfo.Decimals + } +} + +// WithAmountOfValidators sets the amount of validators for the network. +func WithAmountOfValidators(amount int) ConfigOption { + return func(cfg *Config) { + cfg.amountOfValidators = amount + } +} + +// WithPreFundedAccounts sets the pre-funded accounts for the network. +func WithPreFundedAccounts(accounts ...sdktypes.AccAddress) ConfigOption { + return func(cfg *Config) { + cfg.preFundedAccounts = accounts + } +} + +// WithBalances sets the specific balances for the pre-funded accounts, that +// are being set up for the network. +func WithBalances(balances ...banktypes.Balance) ConfigOption { + return func(cfg *Config) { + cfg.balances = append(cfg.balances, balances...) + } +} + +// WithBaseCoin sets the denom and decimals for the base coin in the network. +func WithBaseCoin(denom string, decimals uint8) ConfigOption { + return func(cfg *Config) { + cfg.chainCoins.baseCoin.Denom = denom + cfg.chainCoins.baseCoin.Decimals = evmtypes.Decimals(decimals) + } +} + +// WithEVMCoin sets the denom and decimals for the evm coin in the network. +func WithEVMCoin(_ string, _ uint8) ConfigOption { + // The evm config can be changed only via chain ID because it should be + // handled properly from the configurator. + panic("EVM coin can be changed only via ChainID: se WithChainID method") +} + +// WithCustomGenesis sets the custom genesis of the network for specific modules. +func WithCustomGenesis(customGenesis CustomGenesisState) ConfigOption { + return func(cfg *Config) { + cfg.customGenesisState = customGenesis + } +} + +// WithOtherDenoms sets other possible coin denominations for the network. +func WithOtherDenoms(otherDenoms []string) ConfigOption { + return func(cfg *Config) { + cfg.otherCoinDenoms = otherDenoms + } +} + +// WithValidatorOperators overwrites the used operator address for the network instantiation. +func WithValidatorOperators(keys []sdktypes.AccAddress) ConfigOption { + return func(cfg *Config) { + cfg.operatorsAddrs = keys + } +} + +// WithCustomBaseAppOpts sets custom base app options for the network. +func WithCustomBaseAppOpts(opts ...func(*baseapp.BaseApp)) ConfigOption { + return func(cfg *Config) { + cfg.customBaseAppOpts = opts + } +} diff --git a/testutil/integration/os/network/config_test.go b/testutil/integration/os/network/config_test.go new file mode 100644 index 00000000..4d536337 --- /dev/null +++ b/testutil/integration/os/network/config_test.go @@ -0,0 +1,140 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package network_test + +import ( + "testing" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + testconstants "github.com/evmos/os/testutil/constants" + grpchandler "github.com/evmos/os/testutil/integration/os/grpc" + testkeyring "github.com/evmos/os/testutil/integration/os/keyring" + evmtypes "github.com/evmos/os/x/evm/types" + "github.com/realiotech/realio-network/testutil/integration/os/network" + "github.com/stretchr/testify/require" +) + +func TestWithChainID(t *testing.T) { + testCases := []struct { + name string + chainID string + denom string + decimals evmtypes.Decimals + expBaseFee math.LegacyDec + expCosmosAmount math.Int + }{ + { + name: "18 decimals", + chainID: testconstants.ExampleChainID, + denom: testconstants.ExampleAttoDenom, + decimals: evmtypes.EighteenDecimals, + expBaseFee: math.LegacyNewDec(875_000_000), + expCosmosAmount: network.GetInitialAmount(evmtypes.EighteenDecimals), + }, + { + name: "6 decimals", + chainID: testconstants.SixDecimalsChainID, + denom: testconstants.ExampleMicroDenom, + decimals: evmtypes.SixDecimals, + expBaseFee: math.LegacyNewDecWithPrec(875, 6), + expCosmosAmount: network.GetInitialAmount(evmtypes.SixDecimals), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Create a new network with 2 pre-funded accounts + keyring := testkeyring.New(1) + + opts := []network.ConfigOption{ + network.WithChainID(tc.chainID), + network.WithPreFundedAccounts(keyring.GetAllAccAddrs()...), + } + + nw := network.New(opts...) + + handler := grpchandler.NewIntegrationHandler(nw) //nolint:staticcheck // Somehow the linter marks this as not being used, even though it's used below to get balances + + // reset configuration to use the correct decimals coin info + configurator := evmtypes.NewEVMConfigurator() + configurator.ResetTestConfig() + require.NoError(t, configurator.WithEVMCoinInfo(tc.denom, uint8(tc.decimals)).Configure()) + + // ------------------------------------------------------------------------------------ + // Checks on initial balances. + // ------------------------------------------------------------------------------------ + + // Evm balance should always be in 18 decimals regardless of the + // chain ID. + + // Evm balance should always be in 18 decimals + req, err := handler.GetBalanceFromEVM(keyring.GetAccAddr(0)) + require.NoError(t, err, "error getting balances") + require.Equal(t, + network.GetInitialAmount(evmtypes.EighteenDecimals).String(), + req.Balance, + "expected amount to be in 18 decimals", + ) + + // Bank balance should always be in the original amount. + cReq, err := handler.GetBalanceFromBank(keyring.GetAccAddr(0), tc.denom) + require.NoError(t, err, "error getting balances") + require.Equal(t, + tc.expCosmosAmount.String(), + cReq.Balance.Amount.String(), + "expected amount to be in original decimals", + ) + + // ------------------------------------------------------------------------------------ + // Checks on the base fee. + // ------------------------------------------------------------------------------------ + // Base fee should always be represented with the decimal + // representation of the EVM denom coin. + bfResp, err := handler.GetBaseFee() + require.NoError(t, err, "error getting base fee") + require.Equal(t, + tc.expBaseFee.String(), + bfResp.BaseFee.String(), + "expected amount to be in 18 decimals", + ) + }) + } +} + +func TestWithBalances(t *testing.T) { + key1Balance := sdk.NewCoins(sdk.NewInt64Coin(testconstants.ExampleAttoDenom, 1e18)) + key2Balance := sdk.NewCoins( + sdk.NewInt64Coin(testconstants.ExampleAttoDenom, 2e18), + sdk.NewInt64Coin("other", 3e18), + ) + + // Create a new network with 2 pre-funded accounts + keyring := testkeyring.New(2) + balances := []banktypes.Balance{ + { + Address: keyring.GetAccAddr(0).String(), + Coins: key1Balance, + }, + { + Address: keyring.GetAccAddr(1).String(), + Coins: key2Balance, + }, + } + nw := network.New( + network.WithBalances(balances...), + ) + handler := grpchandler.NewIntegrationHandler(nw) + + req, err := handler.GetAllBalances(keyring.GetAccAddr(0)) + require.NoError(t, err, "error getting balances") + require.Len(t, req.Balances, 1, "wrong number of balances") + require.Equal(t, balances[0].Coins, req.Balances, "wrong balances") + + req, err = handler.GetAllBalances(keyring.GetAccAddr(1)) + require.NoError(t, err, "error getting balances") + require.Len(t, req.Balances, 2, "wrong number of balances") + require.Equal(t, balances[1].Coins, req.Balances, "wrong balances") +} diff --git a/testutil/integration/os/network/example_contracts.go b/testutil/integration/os/network/example_contracts.go new file mode 100644 index 00000000..429034e5 --- /dev/null +++ b/testutil/integration/os/network/example_contracts.go @@ -0,0 +1,30 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package network + +import ( + testconstants "github.com/evmos/os/testutil/constants" +) + +// chainsWEVMOSHex is an utility map used to retrieve the WEVMOS contract +// address in hex format from the chain ID. +// +// TODO: refactor to define this in the example chain initialization and pass as function argument +var chainsWEVMOSHex = map[string]string{ + testconstants.ExampleChainID: testconstants.WEVMOSContractMainnet, +} + +// GetWEVMOSContractHex returns the hex format of address for the WEVMOS contract +// given the chainID. If the chainID is not found, it defaults to the mainnet +// address. +func GetWEVMOSContractHex(chainID string) string { + address, found := chainsWEVMOSHex[chainID] + + // default to mainnet address + if !found { + address = chainsWEVMOSHex[testconstants.ExampleChainID] + } + + return address +} diff --git a/testutil/integration/os/network/ibc.go b/testutil/integration/os/network/ibc.go new file mode 100644 index 00000000..2297be90 --- /dev/null +++ b/testutil/integration/os/network/ibc.go @@ -0,0 +1,29 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package network + +import ( + "testing" + + ibctesting "github.com/cosmos/ibc-go/v8/testing" +) + +// GetIBCChain returns a TestChain instance for the given network. +// Note: the sender accounts are not populated. Do not use this accounts to send transactions during tests. +// The keyring should be used instead. +func (n *IntegrationNetwork) GetIBCChain(t *testing.T, coord *ibctesting.Coordinator) *ibctesting.TestChain { + return &ibctesting.TestChain{ + TB: t, + Coordinator: coord, + ChainID: n.GetChainID(), + App: n.app, + CurrentHeader: n.ctx.BlockHeader(), + QueryServer: n.app.GetIBCKeeper(), + TxConfig: n.app.GetTxConfig(), + Codec: n.app.AppCodec(), + Vals: n.valSet, + NextVals: n.valSet, + Signers: n.valSigners, + } +} diff --git a/testutil/integration/os/network/network.go b/testutil/integration/os/network/network.go new file mode 100644 index 00000000..5ad88585 --- /dev/null +++ b/testutil/integration/os/network/network.go @@ -0,0 +1,358 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package network + +import ( + "fmt" + "math" + "math/big" + "time" + + sdkmath "cosmossdk.io/math" + abcitypes "github.com/cometbft/cometbft/abci/types" + cmtjson "github.com/cometbft/cometbft/libs/json" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + tmversion "github.com/cometbft/cometbft/proto/tendermint/version" + cmttypes "github.com/cometbft/cometbft/types" + "github.com/cometbft/cometbft/version" + sdktypes "github.com/cosmos/cosmos-sdk/types" + sdktestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + gethparams "github.com/ethereum/go-ethereum/params" + chainutil "github.com/evmos/os/example_chain/testutil" + commonnetwork "github.com/evmos/os/testutil/integration/common/network" + "github.com/evmos/os/types" + erc20types "github.com/evmos/os/x/erc20/types" + evmtypes "github.com/evmos/os/x/evm/types" + feemarkettypes "github.com/evmos/os/x/feemarket/types" + app "github.com/realiotech/realio-network/example_chain" +) + +// Network is the interface that wraps the methods to interact with integration test network. +// +// It was designed to avoid users to access module's keepers directly and force integration tests +// to be closer to the real user's behavior. +type Network interface { + commonnetwork.Network + + GetEIP155ChainID() *big.Int + GetEVMChainConfig() *gethparams.ChainConfig + + // Clients + GetERC20Client() erc20types.QueryClient + GetEvmClient() evmtypes.QueryClient + GetGovClient() govtypes.QueryClient + GetFeeMarketClient() feemarkettypes.QueryClient + GetMintClient() minttypes.QueryClient +} + +var _ Network = (*IntegrationNetwork)(nil) + +// IntegrationNetwork is the implementation of the Network interface for integration tests. +type IntegrationNetwork struct { + cfg Config + ctx sdktypes.Context + validators []stakingtypes.Validator + app *app.ExampleChain + + // This is only needed for IBC chain testing setup + valSet *cmttypes.ValidatorSet + valSigners map[string]cmttypes.PrivValidator +} + +// New configures and initializes a new integration Network instance with +// the given configuration options. If no configuration options are provided +// it uses the default configuration. +// +// It panics if an error occurs. +func New(opts ...ConfigOption) *IntegrationNetwork { + cfg := DefaultConfig() + // Modify the default config with the given options + for _, opt := range opts { + opt(&cfg) + } + + ctx := sdktypes.Context{} + network := &IntegrationNetwork{ + cfg: cfg, + ctx: ctx, + validators: []stakingtypes.Validator{}, + } + + err := network.configureAndInitChain() + if err != nil { + panic(err) + } + return network +} + +// PrefundedAccountInitialBalance is the amount of tokens that each +// prefunded account has at genesis. It represents a 100k amount expressed +// in the 18 decimals representation. +var PrefundedAccountInitialBalance, _ = sdkmath.NewIntFromString("100_000_000_000_000_000_000_000") + +// configureAndInitChain initializes the network with the given configuration. +// It creates the genesis state and starts the network. +func (n *IntegrationNetwork) configureAndInitChain() error { + // -------------------------------------------------------------------------------------------- + // Apply changes deriving from possible config options + // FIX: for sure there exists a better way to achieve that. + // -------------------------------------------------------------------------------------------- + + // The bonded amount should be updated to reflect the actual base denom + baseDecimals := n.cfg.chainCoins.BaseDecimals() + bondedAmount := GetInitialBondedAmount(baseDecimals) + + // Create validator set with the amount of validators specified in the config + // with the default power of 1. + valSet, valSigners := createValidatorSetAndSigners(n.cfg.amountOfValidators) + totalBonded := bondedAmount.Mul(sdkmath.NewInt(int64(n.cfg.amountOfValidators))) + + // Build staking type validators and delegations + validators, err := createStakingValidators(valSet.Validators, bondedAmount, n.cfg.operatorsAddrs) + if err != nil { + return err + } + + // Create genesis accounts and funded balances based on the config. + genAccounts, fundedAccountBalances := getGenAccountsAndBalances(n.cfg, validators) + + fundedAccountBalances = addBondedModuleAccountToFundedBalances( + fundedAccountBalances, + sdktypes.NewCoin(n.cfg.chainCoins.BaseDenom(), totalBonded), + ) + + delegations := createDelegations(validators, genAccounts[0].GetAddress()) + + // Create a new testing app with the following params + exampleApp := createTestingApp(n.cfg.chainID, n.cfg.customBaseAppOpts...) + + stakingParams := StakingCustomGenesisState{ + denom: n.cfg.chainCoins.BaseDenom(), + validators: validators, + delegations: delegations, + } + govParams := GovCustomGenesisState{ + denom: n.cfg.chainCoins.BaseDenom(), + } + + fmParams := FeeMarketCustomGenesisState{ + baseFee: GetInitialBaseFeeAmount(n.cfg.chainCoins.BaseDecimals()), + } + + mintParams := MintCustomGenesisState{ + denom: n.cfg.chainCoins.BaseDenom(), + inflationMax: sdkmath.LegacyNewDecWithPrec(0, 1), + inflationMin: sdkmath.LegacyNewDecWithPrec(0, 1), + } + + totalSupply := calculateTotalSupply(fundedAccountBalances) + bankParams := BankCustomGenesisState{ + totalSupply: totalSupply, + balances: fundedAccountBalances, + } + + // Get the corresponding slashing info and missed block info + // for the created validators + slashingParams, err := getValidatorsSlashingGen(validators, exampleApp.StakingKeeper) + if err != nil { + return err + } + + // Configure Genesis state + genesisState := newDefaultGenesisState( + exampleApp, + defaultGenesisParams{ + genAccounts: genAccounts, + staking: stakingParams, + bank: bankParams, + feemarket: fmParams, + slashing: slashingParams, + gov: govParams, + mint: mintParams, + }, + ) + + fmt.Println("genesisState", genesisState["asset"]) + + // modify genesis state if there're any custom genesis state + // for specific modules + genesisState, err = customizeGenesis(exampleApp, n.cfg.customGenesisState, genesisState) + if err != nil { + return err + } + + // Init chain + stateBytes, err := cmtjson.MarshalIndent(genesisState, "", " ") + if err != nil { + return err + } + + consensusParams := chainutil.DefaultConsensusParams + now := time.Now() + + if _, err = exampleApp.InitChain( + &abcitypes.RequestInitChain{ + Time: now, + ChainId: n.cfg.chainID, + Validators: []abcitypes.ValidatorUpdate{}, + ConsensusParams: consensusParams, + AppStateBytes: stateBytes, + }, + ); err != nil { + return err + } + + header := cmtproto.Header{ + ChainID: n.cfg.chainID, + Height: exampleApp.LastBlockHeight() + 1, + AppHash: exampleApp.LastCommitID().Hash, + Time: now, + ValidatorsHash: valSet.Hash(), + NextValidatorsHash: valSet.Hash(), + ProposerAddress: valSet.Proposer.Address, + Version: tmversion.Consensus{ + Block: version.BlockProtocol, + }, + } + + req := buildFinalizeBlockReq(header, valSet.Validators) + if _, err := exampleApp.FinalizeBlock(req); err != nil { + return err + } + + // TODO - this might not be the best way to initilize the context + n.ctx = exampleApp.BaseApp.NewContextLegacy(false, header) + + // Commit genesis changes + if _, err := exampleApp.Commit(); err != nil { + return err + } + + // Set networks global parameters + var blockMaxGas uint64 = math.MaxUint64 + if consensusParams.Block != nil && consensusParams.Block.MaxGas > 0 { + blockMaxGas = uint64(consensusParams.Block.MaxGas) //#nosec G115 -- max gas will not exceed uint64 + } + + n.app = exampleApp + n.ctx = n.ctx.WithConsensusParams(*consensusParams) + n.ctx = n.ctx.WithBlockGasMeter(types.NewInfiniteGasMeterWithLimit(blockMaxGas)) + + n.validators = validators + n.valSet = valSet + n.valSigners = valSigners + + return nil +} + +// GetContext returns the network's context +func (n *IntegrationNetwork) GetContext() sdktypes.Context { + return n.ctx +} + +// WithIsCheckTxCtx switches the network's checkTx property +func (n *IntegrationNetwork) WithIsCheckTxCtx(isCheckTx bool) sdktypes.Context { + n.ctx = n.ctx.WithIsCheckTx(isCheckTx) + return n.ctx +} + +// GetChainID returns the network's chainID +func (n *IntegrationNetwork) GetChainID() string { + return n.cfg.chainID +} + +// GetEIP155ChainID returns the network EIP-155 chainID number +func (n *IntegrationNetwork) GetEIP155ChainID() *big.Int { + return n.cfg.eip155ChainID +} + +// GetEVMChainConfig returns the network's EVM chain config +func (n *IntegrationNetwork) GetEVMChainConfig() *gethparams.ChainConfig { + return evmtypes.GetEthChainConfig() +} + +// GetBaseDenom returns the network's base denom +func (n *IntegrationNetwork) GetBaseDenom() string { + return n.cfg.chainCoins.baseCoin.Denom +} + +// GetEVMDenom returns the network's evm denom +func (n *IntegrationNetwork) GetEVMDenom() string { + return n.cfg.chainCoins.evmCoin.Denom +} + +// GetOtherDenoms returns network's other supported denoms +func (n *IntegrationNetwork) GetOtherDenoms() []string { + return n.cfg.otherCoinDenoms +} + +// GetValidators returns the network's validators +func (n *IntegrationNetwork) GetValidators() []stakingtypes.Validator { + return n.validators +} + +// GetEncodingConfig returns the network's encoding configuration +func (n *IntegrationNetwork) GetEncodingConfig() sdktestutil.TestEncodingConfig { + return sdktestutil.TestEncodingConfig{ + InterfaceRegistry: n.app.InterfaceRegistry(), + Codec: n.app.AppCodec(), + TxConfig: n.app.GetTxConfig(), + Amino: n.app.LegacyAmino(), + } +} + +// BroadcastTxSync broadcasts the given txBytes to the network and returns the response. +// TODO - this should be change to gRPC +func (n *IntegrationNetwork) BroadcastTxSync(txBytes []byte) (abcitypes.ExecTxResult, error) { + header := n.ctx.BlockHeader() + // Update block header and BeginBlock + header.Height++ + header.AppHash = n.app.LastCommitID().Hash + // Calculate new block time after duration + newBlockTime := header.Time.Add(time.Second) + header.Time = newBlockTime + + req := buildFinalizeBlockReq(header, n.valSet.Validators, txBytes) + + // dont include the DecidedLastCommit because we're not committing the changes + // here, is just for broadcasting the tx. To persist the changes, use the + // NextBlock or NextBlockAfter functions + req.DecidedLastCommit = abcitypes.CommitInfo{} + + blockRes, err := n.app.BaseApp.FinalizeBlock(req) + if err != nil { + return abcitypes.ExecTxResult{}, err + } + if len(blockRes.TxResults) != 1 { + return abcitypes.ExecTxResult{}, fmt.Errorf("unexpected number of tx results. Expected 1, got: %d", len(blockRes.TxResults)) + } + return *blockRes.TxResults[0], nil +} + +// Simulate simulates the given txBytes to the network and returns the simulated response. +// TODO - this should be change to gRPC +func (n *IntegrationNetwork) Simulate(txBytes []byte) (*txtypes.SimulateResponse, error) { + gas, result, err := n.app.BaseApp.Simulate(txBytes) + if err != nil { + return nil, err + } + return &txtypes.SimulateResponse{ + GasInfo: &gas, + Result: result, + }, nil +} + +// CheckTx calls the BaseApp's CheckTx method with the given txBytes to the network and returns the response. +func (n *IntegrationNetwork) CheckTx(txBytes []byte) (*abcitypes.ResponseCheckTx, error) { + req := &abcitypes.RequestCheckTx{Tx: txBytes} + res, err := n.app.BaseApp.CheckTx(req) + if err != nil { + return nil, err + } + return res, nil +} diff --git a/testutil/integration/os/network/setup.go b/testutil/integration/os/network/setup.go new file mode 100644 index 00000000..0b51c9ca --- /dev/null +++ b/testutil/integration/os/network/setup.go @@ -0,0 +1,540 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package network + +import ( + "fmt" + "slices" + "time" + + "golang.org/x/exp/maps" + + "cosmossdk.io/log" + sdkmath "cosmossdk.io/math" + cmttypes "github.com/cometbft/cometbft/types" + dbm "github.com/cosmos/cosmos-db" + "github.com/cosmos/cosmos-sdk/baseapp" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/testutil/mock" + simutils "github.com/cosmos/cosmos-sdk/testutil/sims" + sdktypes "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + consensustypes "github.com/cosmos/cosmos-sdk/x/consensus/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/gogoproto/proto" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + exampleapp "github.com/realiotech/realio-network/example_chain" + evmostypes "github.com/evmos/os/types" + erc20types "github.com/evmos/os/x/erc20/types" + evmtypes "github.com/evmos/os/x/evm/types" + feemarkettypes "github.com/evmos/os/x/feemarket/types" + assettypes "github.com/realiotech/realio-network/x/asset/types" +) + +// genSetupFn is the type for the module genesis setup functions +type genSetupFn func(evmosApp *exampleapp.ExampleChain, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) + +// defaultGenesisParams contains the params that are needed to +// setup the default genesis for the testing setup +type defaultGenesisParams struct { + genAccounts []authtypes.GenesisAccount + staking StakingCustomGenesisState + slashing SlashingCustomGenesisState + bank BankCustomGenesisState + gov GovCustomGenesisState + mint MintCustomGenesisState + feemarket FeeMarketCustomGenesisState +} + +// genesisSetupFunctions contains the available genesis setup functions +// that can be used to customize the network genesis +var genesisSetupFunctions = map[string]genSetupFn{ + evmtypes.ModuleName: genStateSetter[*evmtypes.GenesisState](evmtypes.ModuleName), + erc20types.ModuleName: genStateSetter[*erc20types.GenesisState](erc20types.ModuleName), + govtypes.ModuleName: genStateSetter[*govtypesv1.GenesisState](govtypes.ModuleName), + feemarkettypes.ModuleName: genStateSetter[*feemarkettypes.GenesisState](feemarkettypes.ModuleName), + distrtypes.ModuleName: genStateSetter[*distrtypes.GenesisState](distrtypes.ModuleName), + minttypes.ModuleName: genStateSetter[*minttypes.GenesisState](minttypes.ModuleName), + banktypes.ModuleName: setBankGenesisState, + authtypes.ModuleName: setAuthGenesisState, + consensustypes.ModuleName: func(_ *exampleapp.ExampleChain, genesisState evmostypes.GenesisState, _ interface{}) (evmostypes.GenesisState, error) { + // no-op. Consensus does not have a genesis state on the application + // but the params are used on it + // (e.g. block max gas, max bytes). + // This is handled accordingly on chain and context initialization + return genesisState, nil + }, + capabilitytypes.ModuleName: genStateSetter[*capabilitytypes.GenesisState](capabilitytypes.ModuleName), +} + +// genStateSetter is a generic function to set module-specific genesis state +func genStateSetter[T proto.Message](moduleName string) genSetupFn { + return func(evmosApp *exampleapp.ExampleChain, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { + moduleGenesis, ok := customGenesis.(T) + if !ok { + return nil, fmt.Errorf("invalid type %T for %s module genesis state", customGenesis, moduleName) + } + + genesisState[moduleName] = evmosApp.AppCodec().MustMarshalJSON(moduleGenesis) + return genesisState, nil + } +} + +// createValidatorSetAndSigners creates validator set with the amount of validators specified +// with the default power of 1. +func createValidatorSetAndSigners(numberOfValidators int) (*cmttypes.ValidatorSet, map[string]cmttypes.PrivValidator) { + // Create validator set + tmValidators := make([]*cmttypes.Validator, 0, numberOfValidators) + signers := make(map[string]cmttypes.PrivValidator, numberOfValidators) + + for i := 0; i < numberOfValidators; i++ { + privVal := mock.NewPV() + pubKey, _ := privVal.GetPubKey() + validator := cmttypes.NewValidator(pubKey, 1) + tmValidators = append(tmValidators, validator) + signers[pubKey.Address().String()] = privVal + } + + return cmttypes.NewValidatorSet(tmValidators), signers +} + +// createGenesisAccounts returns a slice of genesis accounts from the given +// account addresses. +func createGenesisAccounts(accounts []sdktypes.AccAddress) []authtypes.GenesisAccount { + numberOfAccounts := len(accounts) + genAccounts := make([]authtypes.GenesisAccount, 0, numberOfAccounts) + for _, acc := range accounts { + genAccounts = append(genAccounts, authtypes.NewBaseAccount( + acc, nil, 0, 0), + ) + } + return genAccounts +} + +// getAccAddrsFromBalances returns a slice of genesis accounts from the +// given balances. +func getAccAddrsFromBalances(balances []banktypes.Balance) []sdktypes.AccAddress { + numberOfBalances := len(balances) + genAccounts := make([]sdktypes.AccAddress, 0, numberOfBalances) + for _, balance := range balances { + genAccounts = append(genAccounts, sdktypes.AccAddress(balance.Address)) + } + return genAccounts +} + +// createBalances creates balances for the given accounts and coin. Depending on +// the decimal representation of the denom, the amount is scaled to have the +// same value for all denoms. +func createBalances( + accounts []sdktypes.AccAddress, + denomDecimals map[string]evmtypes.Decimals, +) []banktypes.Balance { + numberOfAccounts := len(accounts) + + denoms := maps.Keys(denomDecimals) + slices.Sort(denoms) + + coins := make([]sdktypes.Coin, len(denoms)) + for i, denom := range denoms { + amount := GetInitialAmount(denomDecimals[denom]) + coins[i] = sdktypes.NewCoin(denom, amount) + } + fundedAccountBalances := make([]banktypes.Balance, 0, numberOfAccounts) + for _, acc := range accounts { + balance := banktypes.Balance{ + Address: acc.String(), + Coins: coins, + } + + fundedAccountBalances = append(fundedAccountBalances, balance) + } + return fundedAccountBalances +} + +// createTestingApp creates an evmos app +func createTestingApp(chainID string, customBaseAppOptions ...func(*baseapp.BaseApp)) *exampleapp.ExampleChain { + // Create evmos app + db := dbm.NewMemDB() + logger := log.NewNopLogger() + loadLatest := true + appOptions := simutils.NewAppOptionsWithFlagHome(exampleapp.DefaultNodeHome) + baseAppOptions := append(customBaseAppOptions, baseapp.SetChainID(chainID)) //nolint:gocritic + + return exampleapp.NewExampleApp( + logger, + db, + nil, + loadLatest, + appOptions, + exampleapp.EvmosAppOptions, + baseAppOptions..., + ) +} + +// createStakingValidator creates a staking validator from the given tm validator and bonded +func createStakingValidator(val *cmttypes.Validator, bondedAmt sdkmath.Int, operatorAddr *sdktypes.AccAddress) (stakingtypes.Validator, error) { + pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) //nolint:staticcheck + if err != nil { + return stakingtypes.Validator{}, err + } + + pkAny, err := codectypes.NewAnyWithValue(pk) + if err != nil { + return stakingtypes.Validator{}, err + } + + opAddr := sdktypes.ValAddress(val.Address).String() + if operatorAddr != nil { + opAddr = sdktypes.ValAddress(operatorAddr.Bytes()).String() + } + + // Default to 5% commission + commission := stakingtypes.NewCommission(sdkmath.LegacyNewDecWithPrec(5, 2), sdkmath.LegacyNewDecWithPrec(2, 1), sdkmath.LegacyNewDecWithPrec(5, 2)) + validator := stakingtypes.Validator{ + OperatorAddress: opAddr, + ConsensusPubkey: pkAny, + Jailed: false, + Status: stakingtypes.Bonded, + Tokens: bondedAmt, + DelegatorShares: sdkmath.LegacyOneDec(), + Description: stakingtypes.Description{}, + UnbondingHeight: int64(0), + UnbondingTime: time.Unix(0, 0).UTC(), + Commission: commission, + MinSelfDelegation: sdkmath.ZeroInt(), + } + return validator, nil +} + +// createStakingValidators creates staking validators from the given tm validators and bonded +// amounts +func createStakingValidators(tmValidators []*cmttypes.Validator, bondedAmt sdkmath.Int, operatorsAddresses []sdktypes.AccAddress) ([]stakingtypes.Validator, error) { + if len(operatorsAddresses) == 0 { + return createStakingValidatorsWithRandomOperator(tmValidators, bondedAmt) + } + return createStakingValidatorsWithSpecificOperator(tmValidators, bondedAmt, operatorsAddresses) +} + +// createStakingValidatorsWithRandomOperator creates staking validators with non-specified operator addresses. +func createStakingValidatorsWithRandomOperator(tmValidators []*cmttypes.Validator, bondedAmt sdkmath.Int) ([]stakingtypes.Validator, error) { + amountOfValidators := len(tmValidators) + stakingValidators := make([]stakingtypes.Validator, 0, amountOfValidators) + for _, val := range tmValidators { + validator, err := createStakingValidator(val, bondedAmt, nil) + if err != nil { + return nil, err + } + stakingValidators = append(stakingValidators, validator) + } + return stakingValidators, nil +} + +// createStakingValidatorsWithSpecificOperator creates staking validators with the given operator addresses. +func createStakingValidatorsWithSpecificOperator(tmValidators []*cmttypes.Validator, bondedAmt sdkmath.Int, operatorsAddresses []sdktypes.AccAddress) ([]stakingtypes.Validator, error) { + amountOfValidators := len(tmValidators) + stakingValidators := make([]stakingtypes.Validator, 0, amountOfValidators) + operatorsCount := len(operatorsAddresses) + if operatorsCount != amountOfValidators { + panic(fmt.Sprintf("provided %d validator operator keys but need %d!", operatorsCount, amountOfValidators)) + } + for i, val := range tmValidators { + validator, err := createStakingValidator(val, bondedAmt, &operatorsAddresses[i]) + if err != nil { + return nil, err + } + stakingValidators = append(stakingValidators, validator) + } + return stakingValidators, nil +} + +// createDelegations creates delegations for the given validators and account +func createDelegations(validators []stakingtypes.Validator, fromAccount sdktypes.AccAddress) []stakingtypes.Delegation { + amountOfValidators := len(validators) + delegations := make([]stakingtypes.Delegation, 0, amountOfValidators) + for _, val := range validators { + delegation := stakingtypes.NewDelegation(fromAccount.String(), val.OperatorAddress, sdkmath.LegacyOneDec()) + delegations = append(delegations, delegation) + } + return delegations +} + +// getValidatorsSlashingGen creates the validators signingInfos and missedBlocks +// records necessary for the slashing module genesis +func getValidatorsSlashingGen(validators []stakingtypes.Validator, sk slashingtypes.StakingKeeper) (SlashingCustomGenesisState, error) { + valCount := len(validators) + signInfo := make([]slashingtypes.SigningInfo, valCount) + missedBlocks := make([]slashingtypes.ValidatorMissedBlocks, valCount) + for i, val := range validators { + consAddrBz, err := val.GetConsAddr() + if err != nil { + return SlashingCustomGenesisState{}, err + } + consAddr, err := sk.ConsensusAddressCodec().BytesToString(consAddrBz) + if err != nil { + return SlashingCustomGenesisState{}, err + } + signInfo[i] = slashingtypes.SigningInfo{ + Address: consAddr, + ValidatorSigningInfo: slashingtypes.ValidatorSigningInfo{ + Address: consAddr, + }, + } + missedBlocks[i] = slashingtypes.ValidatorMissedBlocks{ + Address: consAddr, + } + } + return SlashingCustomGenesisState{ + signingInfo: signInfo, + missedBlocks: missedBlocks, + }, nil +} + +// StakingCustomGenesisState defines the staking genesis state +type StakingCustomGenesisState struct { + denom string + + validators []stakingtypes.Validator + delegations []stakingtypes.Delegation +} + +// setDefaultStakingGenesisState sets the default staking genesis state +func setDefaultStakingGenesisState(evmosApp *exampleapp.ExampleChain, genesisState evmostypes.GenesisState, overwriteParams StakingCustomGenesisState) evmostypes.GenesisState { + // Set staking params + stakingParams := stakingtypes.DefaultParams() + stakingParams.BondDenom = overwriteParams.denom + + stakingGenesis := stakingtypes.NewGenesisState( + stakingParams, + overwriteParams.validators, + overwriteParams.delegations, + ) + genesisState[stakingtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(stakingGenesis) + return genesisState +} + +type BankCustomGenesisState struct { + totalSupply sdktypes.Coins + balances []banktypes.Balance +} + +// setDefaultBankGenesisState sets the default bank genesis state +func setDefaultBankGenesisState(evmosApp *exampleapp.ExampleChain, genesisState evmostypes.GenesisState, overwriteParams BankCustomGenesisState) evmostypes.GenesisState { + bankGenesis := banktypes.NewGenesisState( + banktypes.DefaultGenesisState().Params, + overwriteParams.balances, + overwriteParams.totalSupply, + []banktypes.Metadata{}, + []banktypes.SendEnabled{}, + ) + updatedBankGen := updateBankGenesisStateForChainID(*bankGenesis) + genesisState[banktypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(&updatedBankGen) + return genesisState +} + +// SlashingCustomGenesisState defines the corresponding +// validators signing info and missed blocks for the genesis state +type SlashingCustomGenesisState struct { + signingInfo []slashingtypes.SigningInfo + missedBlocks []slashingtypes.ValidatorMissedBlocks +} + +// setDefaultSlashingGenesisState sets the default slashing genesis state +func setDefaultSlashingGenesisState(evmosApp *exampleapp.ExampleChain, genesisState evmostypes.GenesisState, overwriteParams SlashingCustomGenesisState) evmostypes.GenesisState { + slashingGen := slashingtypes.DefaultGenesisState() + slashingGen.SigningInfos = overwriteParams.signingInfo + slashingGen.MissedBlocks = overwriteParams.missedBlocks + + genesisState[slashingtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(slashingGen) + return genesisState +} + +// setBankGenesisState updates the bank genesis state with custom genesis state +func setBankGenesisState(evmosApp *exampleapp.ExampleChain, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { + customGen, ok := customGenesis.(*banktypes.GenesisState) + if !ok { + return nil, fmt.Errorf("invalid type %T for bank module genesis state", customGenesis) + } + + bankGen := &banktypes.GenesisState{} + evmosApp.AppCodec().MustUnmarshalJSON(genesisState[banktypes.ModuleName], bankGen) + + if len(customGen.Balances) > 0 { + coins := sdktypes.NewCoins() + bankGen.Balances = append(bankGen.Balances, customGen.Balances...) + for _, b := range customGen.Balances { + coins = append(coins, b.Coins...) + } + bankGen.Supply = bankGen.Supply.Add(coins...) + } + if len(customGen.DenomMetadata) > 0 { + bankGen.DenomMetadata = append(bankGen.DenomMetadata, customGen.DenomMetadata...) + } + + if len(customGen.SendEnabled) > 0 { + bankGen.SendEnabled = append(bankGen.SendEnabled, customGen.SendEnabled...) + } + + bankGen.Params = customGen.Params + + genesisState[banktypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(bankGen) + return genesisState, nil +} + +// calculateTotalSupply calculates the total supply from the given balances +func calculateTotalSupply(fundedAccountsBalances []banktypes.Balance) sdktypes.Coins { + totalSupply := sdktypes.NewCoins() + for _, balance := range fundedAccountsBalances { + totalSupply = totalSupply.Add(balance.Coins...) + } + return totalSupply +} + +// addBondedModuleAccountToFundedBalances adds bonded amount to bonded pool module account and include it on funded accounts +func addBondedModuleAccountToFundedBalances( + fundedAccountsBalances []banktypes.Balance, + totalBonded sdktypes.Coin, +) []banktypes.Balance { + return append(fundedAccountsBalances, banktypes.Balance{ + Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), + Coins: sdktypes.Coins{totalBonded}, + }) +} + +// setDefaultAuthGenesisState sets the default auth genesis state +func setDefaultAuthGenesisState(evmosApp *exampleapp.ExampleChain, genesisState evmostypes.GenesisState, genAccs []authtypes.GenesisAccount) evmostypes.GenesisState { + defaultAuthGen := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) + genesisState[authtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(defaultAuthGen) + return genesisState +} + +// setAuthGenesisState updates the bank genesis state with custom genesis state +func setAuthGenesisState(evmosApp *exampleapp.ExampleChain, genesisState evmostypes.GenesisState, customGenesis interface{}) (evmostypes.GenesisState, error) { + customGen, ok := customGenesis.(*authtypes.GenesisState) + if !ok { + return nil, fmt.Errorf("invalid type %T for auth module genesis state", customGenesis) + } + + authGen := &authtypes.GenesisState{} + evmosApp.AppCodec().MustUnmarshalJSON(genesisState[authtypes.ModuleName], authGen) + + if len(customGen.Accounts) > 0 { + authGen.Accounts = append(authGen.Accounts, customGen.Accounts...) + } + + authGen.Params = customGen.Params + + genesisState[authtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(authGen) + return genesisState, nil +} + +// GovCustomGenesisState defines the gov genesis state +type GovCustomGenesisState struct { + denom string +} + +// setDefaultGovGenesisState sets the default gov genesis state +func setDefaultGovGenesisState(evmosApp *exampleapp.ExampleChain, genesisState evmostypes.GenesisState, overwriteParams GovCustomGenesisState) evmostypes.GenesisState { + govGen := govtypesv1.DefaultGenesisState() + updatedParams := govGen.Params + minDepositAmt := sdkmath.NewInt(1e18).Quo(evmtypes.GetEVMCoinDecimals().ConversionFactor()) + updatedParams.MinDeposit = sdktypes.NewCoins(sdktypes.NewCoin(overwriteParams.denom, minDepositAmt)) + updatedParams.ExpeditedMinDeposit = sdktypes.NewCoins(sdktypes.NewCoin(overwriteParams.denom, minDepositAmt)) + govGen.Params = updatedParams + genesisState[govtypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(govGen) + return genesisState +} + +// FeeMarketCustomGenesisState defines the fee market genesis state +type FeeMarketCustomGenesisState struct { + baseFee sdkmath.LegacyDec +} + +// setDefaultFeeMarketGenesisState sets the default fee market genesis state +func setDefaultFeeMarketGenesisState(evmosApp *exampleapp.ExampleChain, genesisState evmostypes.GenesisState, overwriteParams FeeMarketCustomGenesisState) evmostypes.GenesisState { + fmGen := feemarkettypes.DefaultGenesisState() + fmGen.Params.BaseFee = overwriteParams.baseFee + genesisState[feemarkettypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(fmGen) + return genesisState +} + +// MintCustomGenesisState defines the gov genesis state +type MintCustomGenesisState struct { + denom string + inflationMin sdkmath.LegacyDec + inflationMax sdkmath.LegacyDec +} + +// setDefaultGovGenesisState sets the default gov genesis state +// +// NOTE: for the testing network we don't want to have any minting +func setDefaultMintGenesisState(evmosApp *exampleapp.ExampleChain, genesisState evmostypes.GenesisState, overwriteParams MintCustomGenesisState) evmostypes.GenesisState { + mintGen := minttypes.DefaultGenesisState() + updatedParams := mintGen.Params + updatedParams.MintDenom = overwriteParams.denom + updatedParams.InflationMin = overwriteParams.inflationMin + updatedParams.InflationMax = overwriteParams.inflationMax + + mintGen.Params = updatedParams + genesisState[minttypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(mintGen) + return genesisState +} + +func setDefaultErc20GenesisState(evmosApp *exampleapp.ExampleChain, genesisState evmostypes.GenesisState) evmostypes.GenesisState { + // NOTE: here we are using the setup from the example chain + erc20Gen := exampleapp.NewErc20GenesisState() + updatedErc20Gen := updateErc20GenesisStateForChainID(evmosApp.ChainID(), *erc20Gen) + + genesisState[erc20types.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(&updatedErc20Gen) + return genesisState +} + +func setDefaultAssetGenesisState(evmosApp *exampleapp.ExampleChain, genesisState evmostypes.GenesisState) evmostypes.GenesisState { + // NOTE: here we are using the setup from the example chain + assetGen := exampleapp.NewAssetGenesisState() + + genesisState[assettypes.ModuleName] = evmosApp.AppCodec().MustMarshalJSON(assetGen) + return genesisState +} + +// defaultAuthGenesisState sets the default genesis state +// for the testing setup +func newDefaultGenesisState(evmosApp *exampleapp.ExampleChain, params defaultGenesisParams) evmostypes.GenesisState { + genesisState := evmosApp.DefaultGenesis() + + genesisState = setDefaultAuthGenesisState(evmosApp, genesisState, params.genAccounts) + genesisState = setDefaultStakingGenesisState(evmosApp, genesisState, params.staking) + genesisState = setDefaultBankGenesisState(evmosApp, genesisState, params.bank) + genesisState = setDefaultGovGenesisState(evmosApp, genesisState, params.gov) + genesisState = setDefaultFeeMarketGenesisState(evmosApp, genesisState, params.feemarket) + genesisState = setDefaultSlashingGenesisState(evmosApp, genesisState, params.slashing) + genesisState = setDefaultMintGenesisState(evmosApp, genesisState, params.mint) + genesisState = setDefaultErc20GenesisState(evmosApp, genesisState) + genesisState = setDefaultAssetGenesisState(evmosApp, genesisState) + + return genesisState +} + +// customizeGenesis modifies genesis state if there are any custom genesis state +// for specific modules +func customizeGenesis(evmosApp *exampleapp.ExampleChain, customGen CustomGenesisState, genesisState evmostypes.GenesisState) (evmostypes.GenesisState, error) { + var err error + for mod, modGenState := range customGen { + if fn, found := genesisSetupFunctions[mod]; found { + genesisState, err = fn(evmosApp, genesisState, modGenState) + if err != nil { + return genesisState, err + } + } else { + panic(fmt.Sprintf("module %s not found in genesis setup functions", mod)) + } + } + return genesisState, err +} diff --git a/testutil/integration/os/network/unit_network.go b/testutil/integration/os/network/unit_network.go new file mode 100644 index 00000000..fe58a52d --- /dev/null +++ b/testutil/integration/os/network/unit_network.go @@ -0,0 +1,57 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package network + +import ( + sdktypes "github.com/cosmos/cosmos-sdk/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/ethereum/go-ethereum/common" + exampleapp "github.com/realiotech/realio-network/example_chain" + "github.com/evmos/os/x/evm/statedb" +) + +// UnitTestNetwork is the implementation of the Network interface for unit tests. +// It embeds the IntegrationNetwork struct to reuse its methods and +// makes the App public for easier testing. +type UnitTestNetwork struct { + IntegrationNetwork + App *exampleapp.ExampleChain +} + +var _ Network = (*UnitTestNetwork)(nil) + +// NewUnitTestNetwork configures and initializes a new Evmos Network instance with +// the given configuration options. If no configuration options are provided +// it uses the default configuration. +// +// It panics if an error occurs. +// Note: Only uses for Unit Tests +func NewUnitTestNetwork(opts ...ConfigOption) *UnitTestNetwork { + network := New(opts...) + return &UnitTestNetwork{ + IntegrationNetwork: *network, + App: network.app, + } +} + +// GetStateDB returns the state database for the current block. +func (n *UnitTestNetwork) GetStateDB() *statedb.StateDB { + headerHash := n.GetContext().HeaderHash() + return statedb.New( + n.GetContext(), + n.app.EVMKeeper, + statedb.NewEmptyTxConfig(common.BytesToHash(headerHash)), + ) +} + +// FundAccount funds the given account with the given amount of coins. +func (n *UnitTestNetwork) FundAccount(addr sdktypes.AccAddress, coins sdktypes.Coins) error { + ctx := n.GetContext() + + if err := n.app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, coins); err != nil { + return err + } + + return n.app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, coins) +} diff --git a/testutil/integration/os/utils/bank.go b/testutil/integration/os/utils/bank.go new file mode 100644 index 00000000..d9e4c516 --- /dev/null +++ b/testutil/integration/os/utils/bank.go @@ -0,0 +1,45 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package utils + +import ( + "context" + "fmt" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + cmnfactory "github.com/evmos/os/testutil/integration/common/factory" + cmnnet "github.com/evmos/os/testutil/integration/common/network" + "github.com/evmos/os/testutil/integration/os/keyring" +) + +// FundAccountWithBaseDenom funds the given account with the given amount of the network's +// base denomination. +func FundAccountWithBaseDenom(tf cmnfactory.CoreTxFactory, nw cmnnet.Network, sender keyring.Key, receiver sdk.AccAddress, amount math.Int) error { + return tf.FundAccount(sender, receiver, sdk.NewCoins(sdk.NewCoin(nw.GetBaseDenom(), amount))) +} + +// CheckBalances checks that the given accounts have the expected balances and +// returns an error if that is not the case. +func CheckBalances(ctx context.Context, client banktypes.QueryClient, balances []banktypes.Balance) error { + for _, balance := range balances { + addr := balance.GetAddress() + for _, coin := range balance.GetCoins() { + balance, err := client.Balance(ctx, &banktypes.QueryBalanceRequest{Address: addr, Denom: coin.Denom}) + if err != nil { + return err + } + + if !balance.Balance.Equal(coin) { + return fmt.Errorf( + "expected balance %s, got %s for address %s", + coin, balance.Balance, addr, + ) + } + } + } + + return nil +} diff --git a/testutil/integration/os/utils/bank_test.go b/testutil/integration/os/utils/bank_test.go new file mode 100644 index 00000000..171cd58f --- /dev/null +++ b/testutil/integration/os/utils/bank_test.go @@ -0,0 +1,71 @@ +package utils_test + +import ( + "testing" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + testkeyring "github.com/evmos/os/testutil/integration/os/keyring" + "github.com/evmos/os/testutil/integration/os/utils" + evmtypes "github.com/evmos/os/x/evm/types" + "github.com/realiotech/realio-network/testutil/integration/os/network" + "github.com/stretchr/testify/require" +) + +func TestCheckBalances(t *testing.T) { + testDenom := "atest" + keyring := testkeyring.New(1) + address := keyring.GetAccAddr(0).String() + + testcases := []struct { + name string + decimals uint8 + expAmount math.Int + expPass bool + errContains string + }{ + { + name: "pass - eighteen decimals", + decimals: 18, + expAmount: network.GetInitialAmount(evmtypes.EighteenDecimals), + expPass: true, + }, + { + name: "pass - six decimals", + decimals: 6, + expAmount: network.GetInitialAmount(evmtypes.SixDecimals), + expPass: true, + }, + { + name: "fail - wrong amount", + decimals: 18, + expAmount: math.NewInt(1), + errContains: "expected balance", + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + balances := []banktypes.Balance{{ + Address: address, + Coins: sdk.NewCoins( + sdk.NewCoin(testDenom, tc.expAmount), + ), + }} + + nw := network.New( + network.WithBaseCoin(testDenom, tc.decimals), + network.WithPreFundedAccounts(keyring.GetAllAccAddrs()...), + ) + + err := utils.CheckBalances(nw.GetContext(), nw.GetBankClient(), balances) + if tc.expPass { + require.NoError(t, err, "unexpected error checking balances") + } else { + require.Error(t, err, "expected error checking balances") + require.ErrorContains(t, err, tc.errContains, "expected different error checking balances") + } + }) + } +} diff --git a/testutil/integration/os/utils/contracts.go b/testutil/integration/os/utils/contracts.go new file mode 100644 index 00000000..4c314a1e --- /dev/null +++ b/testutil/integration/os/utils/contracts.go @@ -0,0 +1,57 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package utils + +import ( + "fmt" + "slices" + + abcitypes "github.com/cometbft/cometbft/abci/types" + "github.com/evmos/os/testutil/integration/os/factory" + evmtypes "github.com/evmos/os/x/evm/types" +) + +// CheckTxTopics checks if all expected topics are present in the transaction response +func CheckTxTopics(res abcitypes.ExecTxResult, expectedTopics []string) error { + msgEthResponse, err := DecodeExecTxResult(res) + if err != nil { + return err + } + + // Collect all topics within the transaction + availableLogs := make([]string, 0, len(msgEthResponse.Logs)) + for _, log := range msgEthResponse.Logs { + availableLogs = append(availableLogs, log.Topics...) + } + + // Check if all expected topics are present + for _, expectedTopic := range expectedTopics { + if !slices.Contains(availableLogs, expectedTopic) { + return fmt.Errorf("expected topic %s not found in tx response", expectedTopic) + } + } + return nil +} + +// DecodeContractCallResponse decodes the response of a contract call query +func DecodeContractCallResponse(response interface{}, callArgs factory.CallArgs, res abcitypes.ExecTxResult) error { + msgEthResponse, err := DecodeExecTxResult(res) + if err != nil { + return err + } + + err = callArgs.ContractABI.UnpackIntoInterface(response, callArgs.MethodName, msgEthResponse.Ret) + if err != nil { + return err + } + return nil +} + +func DecodeExecTxResult(res abcitypes.ExecTxResult) (*evmtypes.MsgEthereumTxResponse, error) { + msgEthResponse, err := evmtypes.DecodeTxResponse(res.Data) + if err != nil { + return nil, err + } + return msgEthResponse, nil +} diff --git a/testutil/integration/os/utils/erc20.go b/testutil/integration/os/utils/erc20.go new file mode 100644 index 00000000..60f82ecb --- /dev/null +++ b/testutil/integration/os/utils/erc20.go @@ -0,0 +1,115 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package utils + +import ( + "fmt" + + errorsmod "cosmossdk.io/errors" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/ethereum/go-ethereum/common" + "github.com/evmos/os/testutil/integration/os/factory" + erc20types "github.com/evmos/os/x/erc20/types" + "github.com/realiotech/realio-network/testutil/integration/os/network" +) + +// ERC20RegistrationData is the necessary data to provide in order to register an ERC20 token. +type ERC20RegistrationData struct { + // Addresses are the addresses of the ERC20 tokens. + Addresses []string + // ProposerPriv is the private key used to sign the proposal and voting transactions. + ProposerPriv cryptotypes.PrivKey +} + +// ValidateBasic does stateless validation of the data for the ERC20 registration. +func (ed ERC20RegistrationData) ValidateBasic() error { + emptyAddr := common.Address{} + + if len(ed.Addresses) == 0 { + return fmt.Errorf("addresses cannot be empty") + } + + for _, a := range ed.Addresses { + if ok := common.IsHexAddress(a); !ok { + return fmt.Errorf("invalid address %s", a) + } + hexAddr := common.HexToAddress(a) + if hexAddr.Hex() == emptyAddr.Hex() { + return fmt.Errorf("address cannot be empty") + } + } + + if ed.ProposerPriv == nil { + return fmt.Errorf("proposer private key cannot be nil") + } + + return nil +} + +// RegisterERC20 is a helper function to register ERC20 token through +// submitting a governance proposal and having it pass. +// It returns the registered token pair. +func RegisterERC20(tf factory.TxFactory, network network.Network, data ERC20RegistrationData) (res []erc20types.TokenPair, err error) { + err = data.ValidateBasic() + if err != nil { + return nil, errorsmod.Wrap(err, "failed to validate erc20 registration data") + } + + proposal := erc20types.MsgRegisterERC20{ + Authority: authtypes.NewModuleAddress("gov").String(), + Erc20Addresses: data.Addresses, + } + + // Submit the proposal + proposalID, err := SubmitProposal(tf, network, data.ProposerPriv, fmt.Sprintf("Register %d Token", len(data.Addresses)), &proposal) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to submit proposal") + } + + err = network.NextBlock() + if err != nil { + return nil, errorsmod.Wrap(err, "failed to commit block after proposal") + } + + // vote 'yes' and wait till proposal passes + err = ApproveProposal(tf, network, data.ProposerPriv, proposalID) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to approve proposal") + } + + // Check if token pair is registered + eq := network.GetERC20Client() + for _, a := range data.Addresses { + tokenPairRes, err := eq.TokenPair(network.GetContext(), &erc20types.QueryTokenPairRequest{Token: a}) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to query token pair") + } + res = append(res, tokenPairRes.TokenPair) + } + + return res, nil +} + +// ToggleTokenConversion is a helper function to toggle an ERC20 token pair conversion through +// submitting a governance proposal and having it pass. +func ToggleTokenConversion(tf factory.TxFactory, network network.Network, privKey cryptotypes.PrivKey, token string) error { + proposal := erc20types.MsgToggleConversion{ + Authority: authtypes.NewModuleAddress("gov").String(), + Token: token, + } + + // Submit the proposal + proposalID, err := SubmitProposal(tf, network, privKey, fmt.Sprintf("Toggle %s Token", token), &proposal) + if err != nil { + return errorsmod.Wrap(err, "failed to submit proposal") + } + + err = network.NextBlock() + if err != nil { + return errorsmod.Wrap(err, "failed to commit block after proposal") + } + + return ApproveProposal(tf, network, privKey, proposalID) +} diff --git a/testutil/integration/os/utils/events.go b/testutil/integration/os/utils/events.go new file mode 100644 index 00000000..bea28f58 --- /dev/null +++ b/testutil/integration/os/utils/events.go @@ -0,0 +1,55 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package utils + +import ( + "errors" + "fmt" + + abcitypes "github.com/cometbft/cometbft/abci/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" +) + +// ContainsEventType returns true if the given events contain the given eventType. +func ContainsEventType(events []abcitypes.Event, eventType string) bool { + event := GetEventType(events, eventType) + return event != nil +} + +// GetEventType returns the given events if found +// Otherwise returns nil +func GetEventType(events []abcitypes.Event, eventType string) *abcitypes.Event { + for _, event := range events { + if event.Type == eventType { + return &event + } + } + return nil +} + +// GetEventAttributeValue returns the value for the required +// attribute key +func GetEventAttributeValue(event abcitypes.Event, attrKey string) string { + for _, attr := range event.Attributes { + if attr.Key == attrKey { + return attr.Value + } + } + return "" +} + +// GetFeesFromEvents returns the fees value for the +// specified events +func GetFeesFromEvents(events []abcitypes.Event) (sdktypes.DecCoins, error) { + event := GetEventType(events, sdktypes.EventTypeTx) + if event == nil { + return sdktypes.DecCoins{}, errors.New("tx event not found") + } + feeStr := GetEventAttributeValue(*event, sdktypes.AttributeKeyFee) + feeCoins, err := sdktypes.ParseDecCoins(feeStr) + if err != nil { + return sdktypes.DecCoins{}, fmt.Errorf("invalid fees: %v. got %s", err, feeStr) + } + return feeCoins, nil +} diff --git a/testutil/integration/os/utils/evm.go b/testutil/integration/os/utils/evm.go new file mode 100644 index 00000000..24a9af71 --- /dev/null +++ b/testutil/integration/os/utils/evm.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 utils + +import ( + "encoding/json" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/evmos/os/contracts" + evmtypes "github.com/evmos/os/x/evm/types" + "github.com/realiotech/realio-network/testutil/integration/os/network" +) + +// GetERC20Balance returns the token balance of a given account address for +// an ERC-20 token at the given contract address. +func GetERC20Balance(nw network.Network, tokenAddress, accountAddress common.Address) (*big.Int, error) { + input, err := contracts.ERC20MinterBurnerDecimalsContract.ABI.Pack( + "balanceOf", + accountAddress, + ) + if err != nil { + return nil, err + } + + callData, err := json.Marshal(evmtypes.TransactionArgs{ + To: &tokenAddress, + Input: (*hexutil.Bytes)(&input), + }) + if err != nil { + return nil, err + } + + ethRes, err := nw.GetEvmClient().EthCall( + nw.GetContext(), + &evmtypes.EthCallRequest{ + Args: callData, + }, + ) + if err != nil { + return nil, err + } + + fmt.Println("got ret: ", ethRes.Ret) + fmt.Println("got eth call logs: ", ethRes.Logs) + fmt.Println("got eth call error: ", ethRes.VmError) + + var balance *big.Int + err = contracts.ERC20MinterBurnerDecimalsContract.ABI.UnpackIntoInterface(&balance, "balanceOf", ethRes.Ret) + if err != nil { + return nil, err + } + + return balance, nil +} diff --git a/testutil/integration/os/utils/evm_test.go b/testutil/integration/os/utils/evm_test.go new file mode 100644 index 00000000..79e65803 --- /dev/null +++ b/testutil/integration/os/utils/evm_test.go @@ -0,0 +1,64 @@ +package utils_test + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/evmos/os/contracts" + testfactory "github.com/evmos/os/testutil/integration/os/factory" + testhandler "github.com/evmos/os/testutil/integration/os/grpc" + testkeyring "github.com/evmos/os/testutil/integration/os/keyring" + "github.com/evmos/os/testutil/integration/os/utils" + evmtypes "github.com/evmos/os/x/evm/types" + testnetwork "github.com/realiotech/realio-network/testutil/integration/os/network" + "github.com/stretchr/testify/require" +) + +func TestGetERC20Balance(t *testing.T) { + keyring := testkeyring.New(1) + network := testnetwork.NewUnitTestNetwork( + testnetwork.WithPreFundedAccounts(keyring.GetAllAccAddrs()...), + ) + handler := testhandler.NewIntegrationHandler(network) + factory := testfactory.New(network, handler) + + sender := keyring.GetKey(0) + mintAmount := big.NewInt(100) + + // Deploy an ERC-20 contract + erc20Addr, err := factory.DeployContract( + sender.Priv, + evmtypes.EvmTxArgs{}, + testfactory.ContractDeploymentData{ + Contract: contracts.ERC20MinterBurnerDecimalsContract, + ConstructorArgs: []interface{}{"TestToken", "TT", uint8(18)}, + }, + ) + require.NoError(t, err, "failed to deploy contract") + require.NoError(t, network.NextBlock(), "failed to advance block") + + balance, err := utils.GetERC20Balance(network, erc20Addr, sender.Addr) + require.NoError(t, err, "failed to get ERC20 balance") + require.Equal(t, common.Big0.Int64(), balance.Int64(), "expected no balance before minting") + + // Mint some tokens + _, err = factory.ExecuteContractCall( + sender.Priv, + evmtypes.EvmTxArgs{ + To: &erc20Addr, + }, + testfactory.CallArgs{ + ContractABI: contracts.ERC20MinterBurnerDecimalsContract.ABI, + MethodName: "mint", + Args: []interface{}{sender.Addr, mintAmount}, + }, + ) + require.NoError(t, err, "failed to mint tokens") + + require.NoError(t, network.NextBlock(), "failed to advance block") + + balance, err = utils.GetERC20Balance(network, erc20Addr, sender.Addr) + require.NoError(t, err, "failed to get ERC20 balance") + require.Equal(t, mintAmount.Int64(), balance.Int64(), "expected different balance after minting") +} diff --git a/testutil/integration/os/utils/genesis.go b/testutil/integration/os/utils/genesis.go new file mode 100644 index 00000000..15727700 --- /dev/null +++ b/testutil/integration/os/utils/genesis.go @@ -0,0 +1,84 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package utils + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/evmos/os/testutil/constants" + testkeyring "github.com/evmos/os/testutil/integration/os/keyring" + erc20types "github.com/evmos/os/x/erc20/types" + exampleapp "github.com/realiotech/realio-network/example_chain" + "github.com/realiotech/realio-network/testutil/integration/os/network" + utiltx "github.com/realiotech/realio-network/testutil/tx" +) + +// CreateGenesisWithTokenPairs creates a genesis that includes +// the WEVMOS and the provided denoms. +// If no denoms provided, creates only one dynamic precompile with the 'xmpl' denom. +func CreateGenesisWithTokenPairs(keyring testkeyring.Keyring, denoms ...string) network.CustomGenesisState { + // Add all keys from the keyring to the genesis accounts as well. + // + // NOTE: This is necessary to enable the account to send EVM transactions, + // because the Mono ante handler checks the account balance by querying the + // account from the account keeper first. If these accounts are not in the genesis + // state, the ante handler finds a zero balance because of the missing account. + + // if denom not provided, defaults to create only one dynamic erc20 + // precompile with the 'xmpl' denom + if len(denoms) == 0 { + denoms = []string{"xmpl"} + } + + accs := keyring.GetAllAccAddrs() + genesisAccounts := make([]*authtypes.BaseAccount, len(accs)) + for i, addr := range accs { + genesisAccounts[i] = &authtypes.BaseAccount{ + Address: addr.String(), + PubKey: nil, + AccountNumber: uint64(i + 1), //nolint:gosec // G115 + Sequence: 1, + } + } + + accGenesisState := authtypes.DefaultGenesisState() + for _, genesisAccount := range genesisAccounts { + // NOTE: This type requires to be packed into a *types.Any as seen on SDK tests, + // e.g. https://github.com/evmos/cosmos-sdk/blob/v0.47.5-evmos.2/x/auth/keeper/keeper_test.go#L193-L223 + accGenesisState.Accounts = append(accGenesisState.Accounts, codectypes.UnsafePackAny(genesisAccount)) + } + + // Add token pairs to genesis + tokenPairs := make([]erc20types.TokenPair, 0, len(denoms)+1) + tokenPairs = append(tokenPairs, + // NOTE: the example token pairs are being added in the integration test utils + exampleapp.ExampleTokenPairs..., + ) + + dynPrecAddr := make([]string, 0, len(denoms)) + for _, denom := range denoms { + addr := utiltx.GenerateAddress().Hex() + tp := erc20types.TokenPair{ + Erc20Address: addr, + Denom: denom, + Enabled: true, + ContractOwner: erc20types.OWNER_MODULE, // NOTE: Owner is the module account since it's a native token and was registered through governance + } + tokenPairs = append(tokenPairs, tp) + dynPrecAddr = append(dynPrecAddr, addr) + } + + // STR v2: update the NativePrecompiles and DynamicPrecompiles + // with the WEVMOS (default is mainnet) and 'xmpl' tokens in the erc20 params + erc20GenesisState := exampleapp.NewErc20GenesisState() + erc20GenesisState.TokenPairs = tokenPairs + erc20GenesisState.Params.NativePrecompiles = []string{constants.WEVMOSContractMainnet} + erc20GenesisState.Params.DynamicPrecompiles = dynPrecAddr + + // Combine module genesis states + return network.CustomGenesisState{ + authtypes.ModuleName: accGenesisState, + erc20types.ModuleName: erc20GenesisState, + } +} diff --git a/testutil/integration/os/utils/gov.go b/testutil/integration/os/utils/gov.go new file mode 100644 index 00000000..66d7725c --- /dev/null +++ b/testutil/integration/os/utils/gov.go @@ -0,0 +1,197 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package utils + +import ( + "errors" + "fmt" + "strconv" + + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/math" + abcitypes "github.com/cometbft/cometbft/abci/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + commonfactory "github.com/evmos/os/testutil/integration/common/factory" + "github.com/evmos/os/testutil/integration/os/factory" + evmtypes "github.com/evmos/os/x/evm/types" + "github.com/realiotech/realio-network/testutil/integration/os/network" +) + +// SubmitProposal is a helper function to submit a governance proposal and +// return the proposal ID. +func SubmitProposal(tf factory.TxFactory, network network.Network, proposerPriv cryptotypes.PrivKey, title string, msgs ...sdk.Msg) (uint64, error) { + proposerAccAddr := sdk.AccAddress(proposerPriv.PubKey().Address()).String() + // Tokens send to submit the proposal has to be adjusted to take into account the EVM coin decimals. + proposal, err := govv1.NewMsgSubmitProposal( + msgs, + sdk.NewCoins(sdk.NewCoin(network.GetBaseDenom(), math.NewInt(1e18).Quo(evmtypes.GetEVMCoinDecimals().ConversionFactor()))), + proposerAccAddr, + "", + title, + title, + false, + ) + if err != nil { + return 0, err + } + + txArgs := commonfactory.CosmosTxArgs{ + Msgs: []sdk.Msg{proposal}, + } + + return submitProposal(tf, network, proposerPriv, txArgs) +} + +// SubmitLegacyProposal is a helper function to submit a governance proposal and +// return the proposal ID. +func SubmitLegacyProposal(tf factory.TxFactory, network network.Network, proposerPriv cryptotypes.PrivKey, proposal govv1beta1.Content) (uint64, error) { + proposerAccAddr := sdk.AccAddress(proposerPriv.PubKey().Address()) + + msgSubmitProposal, err := govv1beta1.NewMsgSubmitProposal( + proposal, + sdk.NewCoins(sdk.NewCoin(network.GetBaseDenom(), math.NewInt(1e18))), + proposerAccAddr, + ) + if err != nil { + return 0, err + } + + txArgs := commonfactory.CosmosTxArgs{ + Msgs: []sdk.Msg{msgSubmitProposal}, + } + + return submitProposal(tf, network, proposerPriv, txArgs) +} + +// VoteOnProposal is a helper function to vote on a governance proposal given the private key of the voter and +// the option to vote. +func VoteOnProposal(tf factory.TxFactory, voterPriv cryptotypes.PrivKey, proposalID uint64, option govv1.VoteOption) (abcitypes.ExecTxResult, error) { + voterAccAddr := sdk.AccAddress(voterPriv.PubKey().Address()) + + msgVote := govv1.NewMsgVote( + voterAccAddr, + proposalID, + option, + "", + ) + + res, err := tf.CommitCosmosTx(voterPriv, commonfactory.CosmosTxArgs{ + Msgs: []sdk.Msg{msgVote}, + }) + + return res, err +} + +// ApproveProposal is a helper function to vote 'yes' +// for it and wait till it passes. +func ApproveProposal(tf factory.TxFactory, network network.Network, proposerPriv cryptotypes.PrivKey, proposalID uint64) error { + // Vote on proposal + if _, err := VoteOnProposal(tf, proposerPriv, proposalID, govv1.OptionYes); err != nil { + return errorsmod.Wrap(err, "failed to vote on proposal") + } + + if err := waitVotingPeriod(network); err != nil { + return errorsmod.Wrap(err, "failed to wait for voting period to pass") + } + + return checkProposalStatus(network, proposalID, govv1.StatusPassed) +} + +// getProposalIDFromEvents returns the proposal ID from the events in +// the ResponseDeliverTx. +func getProposalIDFromEvents(events []abcitypes.Event) (uint64, error) { + var ( + err error + found bool + proposalID uint64 + ) + + for _, event := range events { + if event.Type != govtypes.EventTypeProposalDeposit { + continue + } + + for _, attr := range event.Attributes { + if attr.Key != govtypes.AttributeKeyProposalID { + continue + } + + proposalID, err = strconv.ParseUint(attr.Value, 10, 64) + if err != nil { + return 0, errorsmod.Wrap(err, "failed to parse proposal ID") + } + + found = true + break + } + + if found { + break + } + } + + if !found { + return 0, errors.New("proposal deposit not found") + } + + return proposalID, nil +} + +func submitProposal(tf factory.TxFactory, network network.Network, proposerPriv cryptotypes.PrivKey, txArgs commonfactory.CosmosTxArgs) (uint64, error) { + res, err := tf.CommitCosmosTx(proposerPriv, txArgs) + if err != nil { + return 0, err + } + + proposalID, err := getProposalIDFromEvents(res.Events) + if err != nil { + return 0, errorsmod.Wrap(err, "failed to get proposal ID from events") + } + + err = network.NextBlock() + if err != nil { + return 0, errorsmod.Wrap(err, "failed to commit block after proposal") + } + + if err := checkProposalStatus(network, proposalID, govv1.StatusVotingPeriod); err != nil { + return 0, errorsmod.Wrap(err, "error while checking proposal") + } + + return proposalID, nil +} + +// waitVotingPeriod is a helper function that waits for the current voting period +// defined in the gov module params to pass +func waitVotingPeriod(n network.Network) error { + gq := n.GetGovClient() + params, err := gq.Params(n.GetContext(), &govv1.QueryParamsRequest{ParamsType: "voting"}) + if err != nil { + return errorsmod.Wrap(err, "failed to query voting params") + } + + err = n.NextBlockAfter(*params.Params.VotingPeriod) // commit after voting period is over + if err != nil { + return errorsmod.Wrap(err, "failed to commit block after voting period ends") + } + + return n.NextBlock() +} + +// checkProposalStatus is a helper function to check for a specific proposal status +func checkProposalStatus(n network.Network, proposalID uint64, expStatus govv1.ProposalStatus) error { + gq := n.GetGovClient() + proposalRes, err := gq.Proposal(n.GetContext(), &govv1.QueryProposalRequest{ProposalId: proposalID}) + if err != nil { + return errorsmod.Wrap(err, "failed to query proposal") + } + + if proposalRes.Proposal.Status != expStatus { + return fmt.Errorf("proposal status different than expected. Expected %s; got: %s", expStatus.String(), proposalRes.Proposal.Status.String()) + } + return nil +} diff --git a/testutil/integration/os/utils/params.go b/testutil/integration/os/utils/params.go new file mode 100644 index 00000000..6b2f9ec0 --- /dev/null +++ b/testutil/integration/os/utils/params.go @@ -0,0 +1,87 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package utils + +import ( + "fmt" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govv1types "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + "github.com/evmos/os/testutil/integration/os/factory" + erc20types "github.com/evmos/os/x/erc20/types" + evmtypes "github.com/evmos/os/x/evm/types" + feemarkettypes "github.com/evmos/os/x/feemarket/types" + "github.com/realiotech/realio-network/testutil/integration/os/network" +) + +type UpdateParamsInput struct { + Tf factory.TxFactory + Network network.Network + Pk cryptotypes.PrivKey + Params interface{} +} + +var authority = authtypes.NewModuleAddress(govtypes.ModuleName).String() + +// UpdateEvmParams helper function to update the EVM module parameters +// It submits an update params proposal, votes for it, and waits till it passes +func UpdateEvmParams(input UpdateParamsInput) error { + return updateModuleParams[evmtypes.Params](input, evmtypes.ModuleName) +} + +// UpdateGovParams helper function to update the governance module parameters +// It submits an update params proposal, votes for it, and waits till it passes +func UpdateGovParams(input UpdateParamsInput) error { + return updateModuleParams[govv1types.Params](input, govtypes.ModuleName) +} + +// UpdateFeeMarketParams helper function to update the feemarket module parameters +// It submits an update params proposal, votes for it, and waits till it passes +func UpdateFeeMarketParams(input UpdateParamsInput) error { + return updateModuleParams[feemarkettypes.Params](input, feemarkettypes.ModuleName) +} + +// UpdateERC20Params helper function to update the erc20 module parameters +// It submits an update params proposal, votes for it, and waits till it passes +func UpdateERC20Params(input UpdateParamsInput) error { + return updateModuleParams[erc20types.Params](input, erc20types.ModuleName) +} + +// updateModuleParams helper function to update module parameters +// It submits an update params proposal, votes for it, and waits till it passes +func updateModuleParams[T interface{}](input UpdateParamsInput, moduleName string) error { + newParams, ok := input.Params.(T) + if !ok { + return fmt.Errorf("invalid params type %T for module %s", input.Params, moduleName) + } + + proposalMsg := createProposalMsg(newParams, moduleName) + + title := fmt.Sprintf("Update %s params", moduleName) + proposalID, err := SubmitProposal(input.Tf, input.Network, input.Pk, title, proposalMsg) + if err != nil { + return err + } + + return ApproveProposal(input.Tf, input.Network, input.Pk, proposalID) +} + +// createProposalMsg creates the module-specific update params message +func createProposalMsg(params interface{}, name string) sdk.Msg { + switch name { + case evmtypes.ModuleName: + return &evmtypes.MsgUpdateParams{Authority: authority, Params: params.(evmtypes.Params)} + case govtypes.ModuleName: + return &govv1types.MsgUpdateParams{Authority: authority, Params: params.(govv1types.Params)} + case feemarkettypes.ModuleName: + return &feemarkettypes.MsgUpdateParams{Authority: authority, Params: params.(feemarkettypes.Params)} + case erc20types.ModuleName: + return &erc20types.MsgUpdateParams{Authority: authority, Params: params.(erc20types.Params)} + default: + return nil + } +} diff --git a/testutil/integration/os/utils/staking.go b/testutil/integration/os/utils/staking.go new file mode 100644 index 00000000..33ccf71f --- /dev/null +++ b/testutil/integration/os/utils/staking.go @@ -0,0 +1,111 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package utils + +import ( + "errors" + "time" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/evmos/os/testutil/integration/os/grpc" + "github.com/realiotech/realio-network/testutil/integration/os/network" +) + +// WaitToAccrueRewards is a helper function that waits for rewards to +// accumulate up to a specified expected amount +func WaitToAccrueRewards(n network.Network, gh grpc.Handler, delegatorAddr string, expRewards sdk.DecCoins) (sdk.DecCoins, error) { + var ( + err error + lapse = time.Hour * 24 * 7 // one week + rewards = sdk.DecCoins{} + ) + + if err = checkNonZeroInflation(n); err != nil { + return nil, err + } + + expAmt := expRewards.AmountOf(n.GetBaseDenom()) + for rewards.AmountOf(n.GetBaseDenom()).LT(expAmt) { + rewards, err = checkRewardsAfter(n, gh, delegatorAddr, lapse) + if err != nil { + return nil, errorsmod.Wrap(err, "error checking rewards") + } + } + + return rewards, err +} + +// checkRewardsAfter is a helper function that checks the accrued rewards +// after the provided timelapse +func checkRewardsAfter(n network.Network, gh grpc.Handler, delegatorAddr string, lapse time.Duration) (sdk.DecCoins, error) { + err := n.NextBlockAfter(lapse) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to commit block after voting period ends") + } + + res, err := gh.GetDelegationTotalRewards(delegatorAddr) + if err != nil { + return nil, errorsmod.Wrapf(err, "error while querying for delegation rewards") + } + + return res.Total, nil +} + +// WaitToAccrueCommission is a helper function that waits for commission to +// accumulate up to a specified expected amount +func WaitToAccrueCommission(n network.Network, gh grpc.Handler, validatorAddr string, expCommission sdk.DecCoins) (sdk.DecCoins, error) { + var ( + err error + lapse = time.Hour * 24 * 7 // one week + commission = sdk.DecCoins{} + ) + + if err := checkNonZeroInflation(n); err != nil { + return nil, err + } + + expAmt := expCommission.AmountOf(n.GetBaseDenom()) + for commission.AmountOf(n.GetBaseDenom()).LT(expAmt) { + commission, err = checkCommissionAfter(n, gh, validatorAddr, lapse) + if err != nil { + return nil, errorsmod.Wrap(err, "error checking commission") + } + } + + return commission, err +} + +// checkCommissionAfter is a helper function that checks the accrued commission +// after the provided time lapse +func checkCommissionAfter(n network.Network, gh grpc.Handler, valAddr string, lapse time.Duration) (sdk.DecCoins, error) { + err := n.NextBlockAfter(lapse) + if err != nil { + return nil, errorsmod.Wrap(err, "failed to commit block after voting period ends") + } + + res, err := gh.GetValidatorCommission(valAddr) + if err != nil { + return nil, errorsmod.Wrapf(err, "error while querying for delegation rewards") + } + + return res.Commission.Commission, nil +} + +// checkNonZeroInflation is a helper function that checks if the network's +// inflation is non-zero. +// This is required to ensure that rewards and commission are accrued. +func checkNonZeroInflation(n network.Network) error { + res, err := n.GetMintClient().Inflation(n.GetContext(), &minttypes.QueryInflationRequest{}) + if err != nil { + return errorsmod.Wrap(err, "failed to get inflation") + } + + if res.Inflation.IsZero() { + return errors.New("inflation is zero; must be non-zero for rewards or commission to be distributed") + } + + return nil +} diff --git a/testutil/integration/os/utils/types.go b/testutil/integration/os/utils/types.go new file mode 100644 index 00000000..96f17deb --- /dev/null +++ b/testutil/integration/os/utils/types.go @@ -0,0 +1,15 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package utils + +import ( + "github.com/ethereum/go-ethereum/common" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func ValidatorConsAddressToHex(valAddress string) common.Address { + coinbaseAddressBytes := sdk.ConsAddress(valAddress).Bytes() + return common.BytesToAddress(coinbaseAddressBytes) +} diff --git a/testutil/integration/os/utils/unit.go b/testutil/integration/os/utils/unit.go new file mode 100644 index 00000000..e00c82f0 --- /dev/null +++ b/testutil/integration/os/utils/unit.go @@ -0,0 +1,144 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) +// +// This file contains all utility function that require the access to the unit +// test network and should only be used in unit tests. + +package utils + +import ( + "fmt" + + "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + erc20types "github.com/evmos/os/x/erc20/types" + "github.com/realiotech/realio-network/testutil/integration/os/network" +) + +const ( + TokenToMint = 1e18 +) + +// RegisterEvmosERC20Coins uses the UnitNetwork to register the evmos token as an +// ERC20 token. The function performs all the required steps for the registration +// like registering the denom trace in the transfer keeper and minting the token +// with the bank. Returns the TokenPair or an error. +func RegisterEvmosERC20Coins( + network network.UnitTestNetwork, + tokenReceiver sdk.AccAddress, +) (erc20types.TokenPair, error) { + bondDenom, err := network.App.StakingKeeper.BondDenom(network.GetContext()) + if err != nil { + return erc20types.TokenPair{}, err + } + + coin := sdk.NewCoin(bondDenom, math.NewInt(TokenToMint)) + err = network.App.BankKeeper.MintCoins( + network.GetContext(), + minttypes.ModuleName, + sdk.NewCoins(coin), + ) + if err != nil { + return erc20types.TokenPair{}, err + } + err = network.App.BankKeeper.SendCoinsFromModuleToAccount( + network.GetContext(), + minttypes.ModuleName, + tokenReceiver, + sdk.NewCoins(coin), + ) + if err != nil { + return erc20types.TokenPair{}, err + } + + evmosMetadata, found := network.App.BankKeeper.GetDenomMetaData(network.GetContext(), bondDenom) + if !found { + return erc20types.TokenPair{}, fmt.Errorf("expected evmos denom metadata") + } + + _, err = network.App.Erc20Keeper.RegisterERC20Extension(network.GetContext(), evmosMetadata.Base) + if err != nil { + return erc20types.TokenPair{}, err + } + + evmosDenomID := network.App.Erc20Keeper.GetDenomMap(network.GetContext(), bondDenom) + tokenPair, ok := network.App.Erc20Keeper.GetTokenPair(network.GetContext(), evmosDenomID) + if !ok { + return erc20types.TokenPair{}, fmt.Errorf("expected evmos erc20 token pair") + } + + return tokenPair, nil +} + +// RegisterIBCERC20Coins uses the UnitNetwork to register the denomTrace as an +// ERC20 token. The function performs all the required steps for the registration +// like registering the denom trace in the transfer keeper and minting the token +// with the bank. Returns the TokenPair or an error. +// +// TODO: why is this not yet used +func RegisterIBCERC20Coins( + network *network.UnitTestNetwork, + tokenReceiver sdk.AccAddress, + denomTrace transfertypes.DenomTrace, +) (erc20types.TokenPair, error) { + ibcDenom := denomTrace.IBCDenom() + network.App.TransferKeeper.SetDenomTrace(network.GetContext(), denomTrace) + ibcMetadata := banktypes.Metadata{ + Name: "Generic IBC name", + Symbol: "IBC", + Description: "Generic IBC token description", + DenomUnits: []*banktypes.DenomUnit{ + { + Denom: ibcDenom, + Exponent: 0, + Aliases: []string{ibcDenom}, + }, + { + Denom: ibcDenom, + Exponent: 18, + }, + }, + Display: ibcDenom, + Base: ibcDenom, + } + + coin := sdk.NewCoin(ibcMetadata.Base, math.NewInt(TokenToMint)) + err := network.App.BankKeeper.MintCoins( + network.GetContext(), + minttypes.ModuleName, + sdk.NewCoins(coin), + ) + if err != nil { + return erc20types.TokenPair{}, err + } + + err = network.App.BankKeeper.SendCoinsFromModuleToAccount( + network.GetContext(), + minttypes.ModuleName, + tokenReceiver, + sdk.NewCoins(coin), + ) + if err != nil { + return erc20types.TokenPair{}, err + } + + _, err = network.App.Erc20Keeper.RegisterERC20Extension(network.GetContext(), ibcMetadata.Base) + if err != nil { + return erc20types.TokenPair{}, err + } + + ibcDenomID := network.App.Erc20Keeper.GetDenomMap( + network.GetContext(), + denomTrace.IBCDenom(), + ) + tokenPair, ok := network.App.Erc20Keeper.GetTokenPair(network.GetContext(), ibcDenomID) + if !ok { + return erc20types.TokenPair{}, fmt.Errorf("expected %s erc20 token pair", ibcDenom) + } + + return tokenPair, nil +} diff --git a/testutil/tx/cosmos.go b/testutil/tx/cosmos.go new file mode 100644 index 00000000..06756551 --- /dev/null +++ b/testutil/tx/cosmos.go @@ -0,0 +1,146 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package tx + +import ( + sdkmath "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + "github.com/evmos/os/testutil/constants" + exampleapp "github.com/realiotech/realio-network/example_chain" + protov2 "google.golang.org/protobuf/proto" +) + +var DefaultFee = sdk.NewCoin(constants.ExampleAttoDenom, sdkmath.NewInt(1e16)) // 0.01 AEVMOS + +// CosmosTxArgs contains the params to create a cosmos tx +type CosmosTxArgs struct { + // TxCfg is the client transaction config + TxCfg client.TxConfig + // Priv is the private key that will be used to sign the tx + Priv cryptotypes.PrivKey + // ChainID is the chain's id on cosmos format, e.g. 'evmos_9000-1' + ChainID string + // Gas to be used on the tx + Gas uint64 + // GasPrice to use on tx + GasPrice *sdkmath.Int + // Fees is the fee to be used on the tx (amount and denom) + Fees sdk.Coins + // FeeGranter is the account address of the fee granter + FeeGranter sdk.AccAddress + // Msgs slice of messages to include on the tx + Msgs []sdk.Msg +} + +// PrepareCosmosTx creates a cosmos tx and signs it with the provided messages and private key. +// It returns the signed transaction and an error +func PrepareCosmosTx( + ctx sdk.Context, + exampleApp *exampleapp.ExampleChain, + args CosmosTxArgs, +) (authsigning.Tx, error) { + txBuilder := args.TxCfg.NewTxBuilder() + + txBuilder.SetGasLimit(args.Gas) + + var fees sdk.Coins + if args.GasPrice != nil { + fees = sdk.Coins{{Denom: constants.ExampleAttoDenom, Amount: args.GasPrice.MulRaw(int64(args.Gas))}} //#nosec G115 + } else { + fees = sdk.Coins{DefaultFee} + } + + txBuilder.SetFeeAmount(fees) + if err := txBuilder.SetMsgs(args.Msgs...); err != nil { + return nil, err + } + + txBuilder.SetFeeGranter(args.FeeGranter) + + return signCosmosTx( + ctx, + exampleApp, + args, + txBuilder, + ) +} + +// signCosmosTx signs the cosmos transaction on the txBuilder provided using +// the provided private key +func signCosmosTx( + ctx sdk.Context, + exampleApp *exampleapp.ExampleChain, + args CosmosTxArgs, + txBuilder client.TxBuilder, +) (authsigning.Tx, error) { + addr := sdk.AccAddress(args.Priv.PubKey().Address().Bytes()) + seq, err := exampleApp.AccountKeeper.GetSequence(ctx, addr) + if err != nil { + return nil, err + } + + signMode, err := authsigning.APISignModeToInternal(args.TxCfg.SignModeHandler().DefaultMode()) + if err != nil { + return nil, err + } + + // First round: we gather all the signer infos. We use the "set empty + // signature" hack to do that. + sigV2 := signing.SignatureV2{ + PubKey: args.Priv.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: signMode, + Signature: nil, + }, + Sequence: seq, + } + + sigsV2 := []signing.SignatureV2{sigV2} + + if err := txBuilder.SetSignatures(sigsV2...); err != nil { + return nil, err + } + + // Second round: all signer infos are set, so each signer can sign. + accNumber := exampleApp.AccountKeeper.GetAccount(ctx, addr).GetAccountNumber() + signerData := authsigning.SignerData{ + ChainID: args.ChainID, + AccountNumber: accNumber, + Sequence: seq, + } + sigV2, err = tx.SignWithPrivKey( + ctx, + signMode, + signerData, + txBuilder, args.Priv, args.TxCfg, + seq, + ) + if err != nil { + return nil, err + } + + sigsV2 = []signing.SignatureV2{sigV2} + if err = txBuilder.SetSignatures(sigsV2...); err != nil { + return nil, err + } + return txBuilder.GetTx(), nil +} + +var _ sdk.Tx = &InvalidTx{} + +// InvalidTx defines a type, which satisfies the sdk.Tx interface, but +// holds no valid transaction information. +// +// NOTE: This is used for testing purposes, to serve the edge case of invalid data being passed to functions. +type InvalidTx struct{} + +func (InvalidTx) GetMsgs() []sdk.Msg { return nil } +func (InvalidTx) GetMsgsV2() ([]protov2.Message, error) { return nil, nil } + +func (InvalidTx) ValidateBasic() error { return nil } diff --git a/testutil/tx/eip712.go b/testutil/tx/eip712.go new file mode 100644 index 00000000..c66aadf7 --- /dev/null +++ b/testutil/tx/eip712.go @@ -0,0 +1,206 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package tx + +import ( + "errors" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/signer/core/apitypes" + cryptocodec "github.com/evmos/os/crypto/codec" + "github.com/evmos/os/ethereum/eip712" + "github.com/evmos/os/types" + exampleapp "github.com/realiotech/realio-network/example_chain" +) + +type EIP712TxArgs struct { + CosmosTxArgs CosmosTxArgs + UseLegacyTypedData bool +} + +type typedDataArgs struct { + chainID uint64 + data []byte + legacyFeePayer sdk.AccAddress + legacyMsg sdk.Msg +} + +type signatureV2Args struct { + pubKey cryptotypes.PubKey + signature []byte + nonce uint64 +} + +// CreateEIP712CosmosTx creates a cosmos tx for typed data according to EIP712. +// Also, signs the tx with the provided messages and private key. +// It returns the signed transaction and an error +func CreateEIP712CosmosTx( + ctx sdk.Context, + exampleApp *exampleapp.ExampleChain, + args EIP712TxArgs, +) (sdk.Tx, error) { + builder, err := PrepareEIP712CosmosTx( + ctx, + exampleApp, + args, + ) + return builder.GetTx(), err +} + +// PrepareEIP712CosmosTx creates a cosmos tx for typed data according to EIP712. +// Also, signs the tx with the provided messages and private key. +// It returns the tx builder with the signed transaction and an error +func PrepareEIP712CosmosTx( + ctx sdk.Context, + exampleApp *exampleapp.ExampleChain, + args EIP712TxArgs, +) (client.TxBuilder, error) { + txArgs := args.CosmosTxArgs + + pc, err := types.ParseChainID(txArgs.ChainID) + if err != nil { + return nil, err + } + chainIDNum := pc.Uint64() + + from := sdk.AccAddress(txArgs.Priv.PubKey().Address().Bytes()) + accNumber := exampleApp.AccountKeeper.GetAccount(ctx, from).GetAccountNumber() + + nonce, err := exampleApp.AccountKeeper.GetSequence(ctx, from) + if err != nil { + return nil, err + } + + // using nolint:all because the staticcheck nolint is not working as expected + fee := legacytx.NewStdFee(txArgs.Gas, txArgs.Fees) //nolint:all + + msgs := txArgs.Msgs + data := legacytx.StdSignBytes(ctx.ChainID(), accNumber, nonce, 0, fee, msgs, "") + + typedDataArgs := typedDataArgs{ + chainID: chainIDNum, + data: data, + legacyFeePayer: from, + legacyMsg: msgs[0], + } + typedData, err := createTypedData(typedDataArgs, args.UseLegacyTypedData) + if err != nil { + return nil, err + } + + txBuilder := txArgs.TxCfg.NewTxBuilder() + builder, ok := txBuilder.(authtx.ExtensionOptionsTxBuilder) + if !ok { + return nil, errors.New("txBuilder could not be casted to authtx.ExtensionOptionsTxBuilder type") + } + + builder.SetFeeAmount(fee.Amount) + builder.SetGasLimit(txArgs.Gas) + + err = builder.SetMsgs(txArgs.Msgs...) + if err != nil { + return nil, err + } + + return signCosmosEIP712Tx( + ctx, + exampleApp, + args, + builder, + typedData, + ) +} + +// signCosmosEIP712Tx signs the cosmos transaction on the txBuilder provided using +// the provided private key and the typed data +func signCosmosEIP712Tx( + ctx sdk.Context, + exampleApp *exampleapp.ExampleChain, + args EIP712TxArgs, + builder authtx.ExtensionOptionsTxBuilder, + data apitypes.TypedData, +) (client.TxBuilder, error) { + priv := args.CosmosTxArgs.Priv + + from := sdk.AccAddress(priv.PubKey().Address().Bytes()) + nonce, err := exampleApp.AccountKeeper.GetSequence(ctx, from) + if err != nil { + return nil, err + } + + sigHash, _, err := apitypes.TypedDataAndHash(data) + if err != nil { + return nil, err + } + + keyringSigner := NewSigner(priv) + signature, pubKey, err := keyringSigner.SignByAddress(from, sigHash, signingtypes.SignMode_SIGN_MODE_DIRECT) + if err != nil { + return nil, err + } + signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper + + sigsV2 := getTxSignatureV2( + signatureV2Args{ + pubKey: pubKey, + signature: signature, + nonce: nonce, + }, + ) + + err = builder.SetSignatures(sigsV2) + if err != nil { + return nil, err + } + + return builder, nil +} + +// createTypedData creates the TypedData object corresponding to +// the arguments, using the legacy implementation as specified. +func createTypedData(args typedDataArgs, useLegacy bool) (apitypes.TypedData, error) { + if useLegacy { + registry := codectypes.NewInterfaceRegistry() + types.RegisterInterfaces(registry) + cryptocodec.RegisterInterfaces(registry) + evmosCodec := codec.NewProtoCodec(registry) + + feeDelegation := &eip712.FeeDelegationOptions{ + FeePayer: args.legacyFeePayer, + } + + return eip712.LegacyWrapTxToTypedData( + evmosCodec, + args.chainID, + args.legacyMsg, + args.data, + feeDelegation, + ) + } + + return eip712.WrapTxToTypedData(args.chainID, args.data) +} + +// getTxSignatureV2 returns the SignatureV2 object corresponding to +// the arguments, using the legacy implementation as needed. +func getTxSignatureV2(args signatureV2Args) signingtypes.SignatureV2 { + // Must use SIGN_MODE_DIRECT, since Amino has some trouble parsing certain Any values from a SignDoc + // with the Legacy EIP-712 TypedData encodings. This is not an issue with the latest encoding. + return signingtypes.SignatureV2{ + PubKey: args.pubKey, + Data: &signingtypes.SingleSignatureData{ + SignMode: signingtypes.SignMode_SIGN_MODE_DIRECT, + Signature: args.signature, + }, + Sequence: args.nonce, + } +} diff --git a/testutil/tx/eth.go b/testutil/tx/eth.go new file mode 100644 index 00000000..fb205836 --- /dev/null +++ b/testutil/tx/eth.go @@ -0,0 +1,165 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package tx + +import ( + "context" + "encoding/json" + "math/big" + + errorsmod "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/client" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/evmos/os/server/config" + evmtypes "github.com/evmos/os/x/evm/types" + exampleapp "github.com/realiotech/realio-network/example_chain" +) + +// PrepareEthTx creates an ethereum tx and signs it with the provided messages and private key. +// It returns the signed transaction and an error +func PrepareEthTx( + txCfg client.TxConfig, + priv cryptotypes.PrivKey, + msgs ...sdk.Msg, +) (authsigning.Tx, error) { + txBuilder := txCfg.NewTxBuilder() + + signer := ethtypes.LatestSignerForChainID(evmtypes.GetEthChainConfig().ChainID) + txFee := sdk.Coins{} + txGasLimit := uint64(0) + + baseDenom := evmtypes.GetEVMCoinDenom() + + // Sign messages and compute gas/fees. + for _, m := range msgs { + msg, ok := m.(*evmtypes.MsgEthereumTx) + if !ok { + return nil, errorsmod.Wrapf(errorsmod.Error{}, "cannot mix Ethereum and Cosmos messages in one Tx") + } + + if priv != nil { + err := msg.Sign(signer, NewSigner(priv)) + if err != nil { + return nil, err + } + } + + msg.From = "" + + txGasLimit += msg.GetGas() + txFee = txFee.Add(sdk.Coin{Denom: baseDenom, Amount: sdkmath.NewIntFromBigInt(msg.GetFee())}) + } + + if err := txBuilder.SetMsgs(msgs...); err != nil { + return nil, err + } + + // Set the extension + var option *codectypes.Any + option, err := codectypes.NewAnyWithValue(&evmtypes.ExtensionOptionsEthereumTx{}) + if err != nil { + return nil, err + } + + builder, ok := txBuilder.(authtx.ExtensionOptionsTxBuilder) + if !ok { + return nil, errorsmod.Wrapf(errorsmod.Error{}, "could not set extensions for Ethereum tx") + } + + builder.SetExtensionOptions(option) + + txBuilder.SetGasLimit(txGasLimit) + txBuilder.SetFeeAmount(txFee) + + return txBuilder.GetTx(), nil +} + +// CreateEthTx is a helper function to create and sign an Ethereum transaction. +// +// If the given private key is not nil, it will be used to sign the transaction. +// +// It offers the ability to increment the nonce by a given amount in case one wants to set up +// multiple transactions that are supposed to be executed one after another. +// Should this not be the case, just pass in zero. +func CreateEthTx( + ctx sdk.Context, + exampleApp *exampleapp.ExampleChain, + privKey cryptotypes.PrivKey, + from sdk.AccAddress, + dest sdk.AccAddress, + amount *big.Int, + nonceIncrement int, +) (*evmtypes.MsgEthereumTx, error) { + toAddr := common.BytesToAddress(dest.Bytes()) + fromAddr := common.BytesToAddress(from.Bytes()) + chainID := evmtypes.GetEthChainConfig().ChainID + + baseFeeRes, err := exampleApp.EVMKeeper.BaseFee(ctx, &evmtypes.QueryBaseFeeRequest{}) + if err != nil { + return nil, err + } + baseFee := baseFeeRes.BaseFee.BigInt() + + // When we send multiple Ethereum Tx's in one Cosmos Tx, we need to increment the nonce for each one. + nonce := exampleApp.EVMKeeper.GetNonce(ctx, fromAddr) + uint64(nonceIncrement) //#nosec G115 -- will not exceed uint64 + evmTxParams := &evmtypes.EvmTxArgs{ + ChainID: chainID, + Nonce: nonce, + To: &toAddr, + Amount: amount, + GasLimit: 100000, + GasFeeCap: baseFee, + GasTipCap: big.NewInt(1), + Accesses: ðtypes.AccessList{}, + } + msgEthereumTx := evmtypes.NewTx(evmTxParams) + msgEthereumTx.From = fromAddr.String() + + // If we are creating multiple eth Tx's with different senders, we need to sign here rather than later. + if privKey != nil { + signer := ethtypes.LatestSignerForChainID(evmtypes.GetEthChainConfig().ChainID) + err := msgEthereumTx.Sign(signer, NewSigner(privKey)) + if err != nil { + return nil, err + } + } + + return msgEthereumTx, nil +} + +// GasLimit estimates the gas limit for the provided parameters. To achieve +// this, need to provide the corresponding QueryClient to call the +// `eth_estimateGas` rpc method. If not provided, returns a default value +func GasLimit(ctx context.Context, from common.Address, data evmtypes.HexString, queryClientEvm evmtypes.QueryClient) (uint64, error) { + // default gas limit (used if no queryClientEvm is provided) + gas := uint64(100000000000) + + if queryClientEvm != nil { + args, err := json.Marshal(&evmtypes.TransactionArgs{ + From: &from, + Data: (*hexutil.Bytes)(&data), + }) + if err != nil { + return gas, err + } + + res, err := queryClientEvm.EstimateGas(ctx, &evmtypes.EthCallRequest{ + Args: args, + GasCap: config.DefaultGasCap, + }) + if err != nil { + return gas, err + } + gas = res.Gas + } + return gas, nil +} diff --git a/testutil/tx/signer.go b/testutil/tx/signer.go new file mode 100644 index 00000000..b6217f4c --- /dev/null +++ b/testutil/tx/signer.go @@ -0,0 +1,83 @@ +// Copyright Tharsis Labs Ltd.(Evmos) +// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) + +package tx + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/evmos/os/crypto/ethsecp256k1" +) + +// NewAddrKey generates an Ethereum address and its corresponding private key. +func NewAddrKey() (common.Address, *ethsecp256k1.PrivKey) { + privkey, _ := ethsecp256k1.GenerateKey() + key, err := privkey.ToECDSA() + if err != nil { + return common.Address{}, nil + } + + addr := crypto.PubkeyToAddress(key.PublicKey) + + return addr, privkey +} + +// NewAccAddressAndKey generates a private key and its corresponding +// Cosmos SDK address. +func NewAccAddressAndKey() (sdk.AccAddress, *ethsecp256k1.PrivKey) { + addr, privKey := NewAddrKey() + return sdk.AccAddress(addr.Bytes()), privKey +} + +// GenerateAddress generates an Ethereum address. +func GenerateAddress() common.Address { + addr, _ := NewAddrKey() + return addr +} + +var _ keyring.Signer = &Signer{} + +// Signer defines a type that is used on testing for signing MsgEthereumTx +type Signer struct { + privKey cryptotypes.PrivKey +} + +func NewSigner(sk cryptotypes.PrivKey) keyring.Signer { + return &Signer{ + privKey: sk, + } +} + +// Sign signs the message using the underlying private key +func (s Signer) Sign(_ string, msg []byte, _ signing.SignMode) ([]byte, cryptotypes.PubKey, error) { + if s.privKey.Type() != ethsecp256k1.KeyType { + return nil, nil, fmt.Errorf( + "invalid private key type for signing ethereum tx; expected %s, got %s", + ethsecp256k1.KeyType, + s.privKey.Type(), + ) + } + + sig, err := s.privKey.Sign(msg) + if err != nil { + return nil, nil, err + } + + return sig, s.privKey.PubKey(), nil +} + +// SignByAddress sign byte messages with a user key providing the address. +func (s Signer) SignByAddress(address sdk.Address, msg []byte, signMode signing.SignMode) ([]byte, cryptotypes.PubKey, error) { + signer := sdk.AccAddress(s.privKey.PubKey().Address()) + if !signer.Equals(address) { + return nil, nil, fmt.Errorf("address mismatch: signer %s ≠ given address %s", signer, address) + } + + return s.Sign("", msg, signMode) +} diff --git a/x/asset/client/cli/query.go b/x/asset/client/cli/query.go deleted file mode 100644 index cac5ffbe..00000000 --- a/x/asset/client/cli/query.go +++ /dev/null @@ -1,28 +0,0 @@ -package cli - -import ( - "fmt" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/spf13/cobra" - - "github.com/realiotech/realio-network/x/asset/types" -) - -// GetQueryCmd returns the cli query commands for this module -func GetQueryCmd() *cobra.Command { - // Group asset queries under a subcommand - cmd := &cobra.Command{ - Use: types.ModuleName, - Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), - DisableFlagParsing: true, - SuggestionsMinimumDistance: 2, - RunE: client.ValidateCmd, - } - - cmd.AddCommand(CmdQueryParams()) - cmd.AddCommand(CmdQueryTokens()) - cmd.AddCommand(CmdQueryToken()) - - return cmd -} diff --git a/x/asset/client/cli/query_params.go b/x/asset/client/cli/query_params.go deleted file mode 100644 index abf28d8e..00000000 --- a/x/asset/client/cli/query_params.go +++ /dev/null @@ -1,35 +0,0 @@ -package cli - -import ( - "context" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/spf13/cobra" - - "github.com/realiotech/realio-network/x/asset/types" -) - -func CmdQueryParams() *cobra.Command { - cmd := &cobra.Command{ - Use: "params", - Short: "shows the parameters of the module", - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, _ []string) error { - clientCtx := client.GetClientContextFromCmd(cmd) - - queryClient := types.NewQueryClient(clientCtx) - - res, err := queryClient.Params(context.Background(), &types.QueryParamsRequest{}) - if err != nil { - return err - } - - return clientCtx.PrintProto(res) - }, - } - - flags.AddQueryFlagsToCmd(cmd) - - return cmd -} diff --git a/x/asset/client/cli/query_tokens.go b/x/asset/client/cli/query_tokens.go deleted file mode 100644 index c2e36b2a..00000000 --- a/x/asset/client/cli/query_tokens.go +++ /dev/null @@ -1,59 +0,0 @@ -package cli - -import ( - "context" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/spf13/cobra" - - "github.com/realiotech/realio-network/x/asset/types" -) - -func CmdQueryTokens() *cobra.Command { - cmd := &cobra.Command{ - Use: "tokens", - Short: "shows the tokens of the module", - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, _ []string) error { - clientCtx := client.GetClientContextFromCmd(cmd) - - queryClient := types.NewQueryClient(clientCtx) - - res, err := queryClient.Tokens(context.Background(), &types.QueryTokensRequest{}) - if err != nil { - return err - } - - return clientCtx.PrintProto(res) - }, - } - - flags.AddQueryFlagsToCmd(cmd) - - return cmd -} - -func CmdQueryToken() *cobra.Command { - cmd := &cobra.Command{ - Use: "token [symbol]", - Short: "query token by symbol", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) (err error) { - argSymbol := args[0] - clientCtx := client.GetClientContextFromCmd(cmd) - queryClient := types.NewQueryClient(clientCtx) - params := types.NewQueryTokenRequest(argSymbol) - res, err := queryClient.Token(context.Background(), params) - if err != nil { - return err - } - - return clientCtx.PrintProto(res) - }, - } - - flags.AddQueryFlagsToCmd(cmd) - - return cmd -} diff --git a/x/asset/client/cli/tx.go b/x/asset/client/cli/tx.go deleted file mode 100644 index eecd9b1c..00000000 --- a/x/asset/client/cli/tx.go +++ /dev/null @@ -1,36 +0,0 @@ -package cli - -import ( - "fmt" - "time" - - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/client" - // "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/realiotech/realio-network/x/asset/types" -) - -var DefaultRelativePacketTimeoutTimestamp = uint64((time.Duration(10) * time.Minute).Nanoseconds()) - -// GetTxCmd returns the transaction commands for this module -func GetTxCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: types.ModuleName, - Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName), - DisableFlagParsing: true, - SuggestionsMinimumDistance: 2, - RunE: client.ValidateCmd, - } - - cmd.AddCommand(CmdMsgCreateToken()) - cmd.AddCommand(CmdMsgUpdateToken()) - cmd.AddCommand(CmdCreateToken()) - cmd.AddCommand(CmdUpdateToken()) - cmd.AddCommand(CmdAuthorizeAddress()) - cmd.AddCommand(CmdUnAuthorizeAddress()) - cmd.AddCommand(CmdTransferToken()) - // this line is used by starport scaffolding # 1 - - return cmd -} diff --git a/x/asset/client/cli/tx_authorize_address.go b/x/asset/client/cli/tx_authorize_address.go deleted file mode 100644 index 3843c613..00000000 --- a/x/asset/client/cli/tx_authorize_address.go +++ /dev/null @@ -1,44 +0,0 @@ -package cli - -import ( - "strconv" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/realiotech/realio-network/x/asset/types" - "github.com/spf13/cobra" -) - -var _ = strconv.Itoa(0) - -func CmdAuthorizeAddress() *cobra.Command { - cmd := &cobra.Command{ - Use: "authorize-address [symbol] [address]", - Short: "Broadcast message AuthorizeAddress", - Args: cobra.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) (err error) { - argSymbol := args[0] - argAddress := args[1] - - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - msg := types.NewMsgAuthorizeAddress( - clientCtx.GetFromAddress().String(), - argSymbol, - argAddress, - ) - if err := msg.ValidateBasic(); err != nil { - return err - } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - - flags.AddTxFlagsToCmd(cmd) - - return cmd -} diff --git a/x/asset/client/cli/tx_create_token.go b/x/asset/client/cli/tx_create_token.go deleted file mode 100644 index 5837afdc..00000000 --- a/x/asset/client/cli/tx_create_token.go +++ /dev/null @@ -1,53 +0,0 @@ -package cli - -import ( - "strconv" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/spf13/cast" - "github.com/spf13/cobra" - - "github.com/realiotech/realio-network/x/asset/types" -) - -var _ = strconv.Itoa(0) - -func CmdCreateToken() *cobra.Command { - cmd := &cobra.Command{ - Use: "create-token [name] [symbol] [total] [authorization-required]", - Short: "Broadcast message CreateToken", - Args: cobra.ExactArgs(4), - RunE: func(cmd *cobra.Command, args []string) (err error) { - argName := args[0] - argSymbol := args[1] - argTotal := args[2] - argAuthorizationRequired, err := cast.ToBoolE(args[3]) - if err != nil { - return err - } - - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - msg := types.NewMsgCreateToken( - clientCtx.GetFromAddress().String(), - argName, - argSymbol, - argTotal, - argAuthorizationRequired, - ) - if err := msg.ValidateBasic(); err != nil { - return err - } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - - flags.AddTxFlagsToCmd(cmd) - - return cmd -} diff --git a/x/asset/client/cli/tx_msg_create_token.go b/x/asset/client/cli/tx_msg_create_token.go deleted file mode 100644 index 8ae443eb..00000000 --- a/x/asset/client/cli/tx_msg_create_token.go +++ /dev/null @@ -1,53 +0,0 @@ -package cli - -import ( - "strconv" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/spf13/cast" - "github.com/spf13/cobra" - - "github.com/realiotech/realio-network/x/asset/types" -) - -var _ = strconv.Itoa(0) - -func CmdMsgCreateToken() *cobra.Command { - cmd := &cobra.Command{ - Use: "msg-create-token [name] [symbol] [total] [authorization-required]", - Short: "Broadcast message MsgCreateToken", - Args: cobra.ExactArgs(4), - RunE: func(cmd *cobra.Command, args []string) (err error) { - argName := args[0] - argSymbol := args[1] - argTotal := args[2] - argAuthorizationRequired, err := cast.ToBoolE(args[3]) - if err != nil { - return err - } - - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - msg := types.NewMsgCreateToken( - clientCtx.GetFromAddress().String(), - argName, - argSymbol, - argTotal, - argAuthorizationRequired, - ) - if err := msg.ValidateBasic(); err != nil { - return err - } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - - flags.AddTxFlagsToCmd(cmd) - - return cmd -} diff --git a/x/asset/client/cli/tx_msg_update_token.go b/x/asset/client/cli/tx_msg_update_token.go deleted file mode 100644 index 5304ae06..00000000 --- a/x/asset/client/cli/tx_msg_update_token.go +++ /dev/null @@ -1,48 +0,0 @@ -package cli - -import ( - "strconv" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/realiotech/realio-network/x/asset/types" - "github.com/spf13/cast" - "github.com/spf13/cobra" -) - -var _ = strconv.Itoa(0) - -func CmdMsgUpdateToken() *cobra.Command { - cmd := &cobra.Command{ - Use: "msg-update-token [symbol] [authorization-required]", - Short: "Broadcast message MsgUpdateToken", - Args: cobra.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) (err error) { - argSymbol := args[0] - argAuthorizationRequired, err := cast.ToBoolE(args[1]) - if err != nil { - return err - } - - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - msg := types.NewMsgUpdateToken( - clientCtx.GetFromAddress().String(), - argSymbol, - argAuthorizationRequired, - ) - if err := msg.ValidateBasic(); err != nil { - return err - } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - - flags.AddTxFlagsToCmd(cmd) - - return cmd -} diff --git a/x/asset/client/cli/tx_transfer_token.go b/x/asset/client/cli/tx_transfer_token.go deleted file mode 100644 index 77b26114..00000000 --- a/x/asset/client/cli/tx_transfer_token.go +++ /dev/null @@ -1,51 +0,0 @@ -package cli - -import ( - "strconv" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/spf13/cobra" - - "github.com/realiotech/realio-network/x/asset/types" -) - -var _ = strconv.Itoa(0) - -func CmdTransferToken() *cobra.Command { - cmd := &cobra.Command{ - Use: "transfer-token [symbol] [from] [to] [amount]", - Short: "Broadcast message TransferToken", - Args: cobra.ExactArgs(4), - RunE: func(cmd *cobra.Command, args []string) (err error) { - argSymbol := args[0] - argFrom := args[1] - argTo := args[2] - argAmount := args[3] - if err != nil { - return err - } - - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - msg := types.NewMsgTransferToken( - argSymbol, - argFrom, - argTo, - argAmount, - ) - if err := msg.ValidateBasic(); err != nil { - return err - } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - - flags.AddTxFlagsToCmd(cmd) - - return cmd -} diff --git a/x/asset/client/cli/tx_un_authorize_address.go b/x/asset/client/cli/tx_un_authorize_address.go deleted file mode 100644 index 80e2ad01..00000000 --- a/x/asset/client/cli/tx_un_authorize_address.go +++ /dev/null @@ -1,44 +0,0 @@ -package cli - -import ( - "strconv" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/realiotech/realio-network/x/asset/types" - "github.com/spf13/cobra" -) - -var _ = strconv.Itoa(0) - -func CmdUnAuthorizeAddress() *cobra.Command { - cmd := &cobra.Command{ - Use: "un-authorize-address [symbol] [address]", - Short: "Broadcast message UnAuthorizeAddress", - Args: cobra.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) (err error) { - argSymbol := args[0] - argAddress := args[1] - - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - msg := types.NewMsgUnAuthorizeAddress( - clientCtx.GetFromAddress().String(), - argSymbol, - argAddress, - ) - if err := msg.ValidateBasic(); err != nil { - return err - } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - - flags.AddTxFlagsToCmd(cmd) - - return cmd -} diff --git a/x/asset/client/cli/tx_update_token.go b/x/asset/client/cli/tx_update_token.go deleted file mode 100644 index 13182aaf..00000000 --- a/x/asset/client/cli/tx_update_token.go +++ /dev/null @@ -1,48 +0,0 @@ -package cli - -import ( - "strconv" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/realiotech/realio-network/x/asset/types" - "github.com/spf13/cast" - "github.com/spf13/cobra" -) - -var _ = strconv.Itoa(0) - -func CmdUpdateToken() *cobra.Command { - cmd := &cobra.Command{ - Use: "update-token [symbol] [authorization-required]", - Short: "Broadcast message UpdateToken", - Args: cobra.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) (err error) { - argSymbol := args[0] - argAuthorizationRequired, err := cast.ToBoolE(args[1]) - if err != nil { - return err - } - - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - msg := types.NewMsgUpdateToken( - clientCtx.GetFromAddress().String(), - argSymbol, - argAuthorizationRequired, - ) - if err := msg.ValidateBasic(); err != nil { - return err - } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - - flags.AddTxFlagsToCmd(cmd) - - return cmd -} diff --git a/x/asset/exported/exported.go b/x/asset/exported/exported.go index 000114e6..2eb9e7dd 100644 --- a/x/asset/exported/exported.go +++ b/x/asset/exported/exported.go @@ -15,4 +15,4 @@ type ( Subspace interface { GetParamSet(ctx sdk.Context, ps ParamSet) } -) +) \ No newline at end of file diff --git a/x/asset/genesis.go b/x/asset/genesis.go index 02d51567..729c97b0 100644 --- a/x/asset/genesis.go +++ b/x/asset/genesis.go @@ -2,6 +2,7 @@ package asset import ( "context" + "fmt" "github.com/realiotech/realio-network/x/asset/keeper" "github.com/realiotech/realio-network/x/asset/types" @@ -10,16 +11,19 @@ import ( // InitGenesis initializes the assets module's state from a provided genesis // state. func InitGenesis(ctx context.Context, k keeper.Keeper, genState types.GenesisState) { + fmt.Println("Go here") err := k.Params.Set(ctx, genState.Params) if err != nil { panic(err) } for _, token := range genState.Tokens { - err := k.Token.Set(ctx, types.TokenKey(token.Symbol), token) + err := k.Token.Set(ctx, token.Symbol, token) if err != nil { panic(err) } } + ma := k.Ak.GetModuleAccount(ctx, types.ModuleName) + fmt.Println("asset module account", ma) } // ExportGenesis returns the capability module's exported genesis. @@ -42,4 +46,4 @@ func ExportGenesis(ctx context.Context, k keeper.Keeper) *types.GenesisState { // this line is used by starport scaffolding # genesis/module/export return genesis -} +} \ No newline at end of file diff --git a/x/asset/genesis_test.go b/x/asset/genesis_test.go deleted file mode 100644 index 87fed1fd..00000000 --- a/x/asset/genesis_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package asset_test - -import ( - "testing" - "time" - - feemarkettypes "github.com/evmos/os/x/feemarket/types" - - "github.com/cometbft/cometbft/crypto/tmhash" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - tmversion "github.com/cometbft/cometbft/proto/tendermint/version" - "github.com/cometbft/cometbft/version" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/suite" - - "github.com/realiotech/realio-network/app" - "github.com/realiotech/realio-network/testutil" - realiotypes "github.com/realiotech/realio-network/types" - "github.com/realiotech/realio-network/x/asset" - "github.com/realiotech/realio-network/x/asset/types" -) - -type GenesisTestSuite struct { - suite.Suite - - ctx sdk.Context - - app *app.RealioNetwork - genesis types.GenesisState -} - -func (suite *GenesisTestSuite) SetupTest() { - // consensus key - consAddress := sdk.ConsAddress(testutil.GenAddress().Bytes()) - - suite.app = app.Setup(false, feemarkettypes.DefaultGenesisState(), 1) - suite.ctx = suite.app.BaseApp.NewContextLegacy(false, tmproto.Header{ - Height: 1, - ChainID: realiotypes.MainnetChainID + "-1", - Time: time.Now().UTC(), - ProposerAddress: consAddress.Bytes(), - - Version: tmversion.Consensus{ - Block: version.BlockProtocol, - }, - LastBlockId: tmproto.BlockID{ - Hash: tmhash.Sum([]byte("block_id")), - PartSetHeader: tmproto.PartSetHeader{ - Total: 11, - Hash: tmhash.Sum([]byte("partset_header")), - }, - }, - AppHash: tmhash.Sum([]byte("app")), - DataHash: tmhash.Sum([]byte("data")), - EvidenceHash: tmhash.Sum([]byte("evidence")), - ValidatorsHash: tmhash.Sum([]byte("validators")), - NextValidatorsHash: tmhash.Sum([]byte("next_validators")), - ConsensusHash: tmhash.Sum([]byte("consensus")), - LastResultsHash: tmhash.Sum([]byte("last_result")), - }) - - suite.genesis = *types.DefaultGenesis() -} - -func TestGenesisTestSuite(t *testing.T) { - suite.Run(t, new(GenesisTestSuite)) -} - -func (suite *GenesisTestSuite) TestGenesis() { - asset.InitGenesis(suite.ctx, suite.app.AssetKeeper, suite.genesis) - got := asset.ExportGenesis(suite.ctx, suite.app.AssetKeeper) - suite.Require().NotNil(got) - - suite.Require().Equal(len(suite.genesis.Tokens), len(got.Tokens)) -} diff --git a/x/asset/keeper/access_control.go b/x/asset/keeper/access_control.go new file mode 100644 index 00000000..cfa25c6d --- /dev/null +++ b/x/asset/keeper/access_control.go @@ -0,0 +1,53 @@ +package keeper + +import ( + "bytes" + "context" + + errorsmod "cosmossdk.io/errors" + "github.com/realiotech/realio-network/x/asset/types" +) + +func (k Keeper) GrantRole(ctx context.Context, tokenId string, issuer []byte, manager []byte) error { + token, err := k.Token.Get(ctx, tokenId) + if err != nil { + return errorsmod.Wrapf(types.ErrTokenGet, err.Error()) + } + + if !bytes.Equal(issuer, token.Issuer) { + return errorsmod.Wrapf(types.ErrUnauthorize, "issuer not the creator of the token") + } + + tokenManagement, err := k.TokenManagement.Get(ctx, tokenId) + if err != nil { + return errorsmod.Wrapf(types.ErrTokenManagementGet, err.Error()) + } + newManagers := append(tokenManagement.Managers, manager) + tokenManagement.Managers = newManagers + + return k.TokenManagement.Set(ctx, tokenId, tokenManagement) +} + +func (k Keeper) RevokeRole(ctx context.Context, tokenId string, issuer []byte, manager []byte) error { + token, err := k.Token.Get(ctx, tokenId) + if err != nil { + return errorsmod.Wrapf(types.ErrTokenGet, err.Error()) + } + + if !bytes.Equal(issuer, token.Issuer) { + return errorsmod.Wrapf(types.ErrUnauthorize, "issuer not the creator of the token") + } + + tokenManagement, err := k.TokenManagement.Get(ctx, tokenId) + if err != nil { + return errorsmod.Wrapf(types.ErrTokenManagementGet, err.Error()) + } + managers := tokenManagement.Managers + for i, b := range managers { + if bytes.Equal(b, manager) { + tokenManagement.Managers = append(managers[:i], managers[i+1:]...) + } + } + + return k.TokenManagement.Set(ctx, tokenId, tokenManagement) +} diff --git a/x/asset/keeper/grpc_query.go b/x/asset/keeper/grpc_query.go deleted file mode 100644 index e34fbaea..00000000 --- a/x/asset/keeper/grpc_query.go +++ /dev/null @@ -1,80 +0,0 @@ -package keeper - -import ( - "context" - - errorsmod "cosmossdk.io/errors" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - sdk "github.com/cosmos/cosmos-sdk/types" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - "github.com/realiotech/realio-network/x/asset/types" -) - -var _ types.QueryServer = queryServer{} - -func NewQueryServerImpl(k Keeper) types.QueryServer { - return queryServer{k} -} - -type queryServer struct { - k Keeper -} - -func (q queryServer) Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { - if req == nil { - return nil, status.Error(codes.InvalidArgument, "invalid request") - } - params, err := q.k.Params.Get(c) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - - return &types.QueryParamsResponse{Params: params}, nil -} - -func (q queryServer) Tokens(c context.Context, req *types.QueryTokensRequest) (*types.QueryTokensResponse, error) { - if req == nil { - return nil, status.Error(codes.InvalidArgument, "invalid request") - } - - tokens := []types.Token{} - err := q.k.Token.Walk(c, nil, func(_ string, token types.Token) (stop bool, err error) { - tokens = append(tokens, token) - return false, nil - }) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - return &types.QueryTokensResponse{Tokens: tokens}, nil -} - -func (q queryServer) Token(c context.Context, req *types.QueryTokenRequest) (*types.QueryTokenResponse, error) { - if req == nil { - return nil, status.Error(codes.InvalidArgument, "invalid request") - } - ctx := sdk.UnwrapSDKContext(c) - - if t, err := q.k.Token.Get(ctx, types.TokenKey(req.Symbol)); err != nil { - return nil, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, "not found") - } else { //nolint:revive // fixing this causes t to be inaccessible, so let's leave all as is. - return &types.QueryTokenResponse{Token: t}, nil - } -} - -func (q queryServer) IsAuthorized(c context.Context, req *types.QueryIsAuthorizedRequest) (*types.QueryIsAuthorizedResponse, error) { - if req == nil { - return nil, status.Error(codes.InvalidArgument, "invalid request") - } - ctx := sdk.UnwrapSDKContext(c) - - if t, err := q.k.Token.Get(ctx, types.TokenKey(req.Symbol)); err != nil { - return nil, errorsmod.Wrap(sdkerrors.ErrKeyNotFound, "not found") - } else { //nolint:revive // fixing this causes t to be inaccessible, so let's leave all as is. - accAddress, _ := sdk.AccAddressFromBech32(req.Address) - isAuthorized := t.AddressIsAuthorized(accAddress) - return &types.QueryIsAuthorizedResponse{IsAuthorized: isAuthorized}, nil - } -} diff --git a/x/asset/keeper/grpc_query_test.go b/x/asset/keeper/grpc_query_test.go deleted file mode 100644 index 329aed9c..00000000 --- a/x/asset/keeper/grpc_query_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package keeper_test - -import ( - "fmt" - - "github.com/realiotech/realio-network/x/asset/keeper" - "github.com/realiotech/realio-network/x/asset/types" -) - -func (suite *KeeperTestSuite) TestParamsQuery() { - suite.SetupTest() - - k := suite.app.AssetKeeper - - params := types.DefaultParams() - err := k.Params.Set(suite.ctx, params) - suite.Require().NoError(err) - - queryServer := keeper.NewQueryServerImpl(k) - response, err := queryServer.Params(suite.ctx, &types.QueryParamsRequest{}) - suite.Require().NoError(err) - suite.Require().Equal(&types.QueryParamsResponse{Params: params}, response) -} - -func (suite *KeeperTestSuite) TestTokensQuery() { - var ( - req *types.QueryTokensRequest - expRes *types.QueryTokensResponse - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "no tokens", - func() { - req = &types.QueryTokensRequest{} - expRes = &types.QueryTokensResponse{Tokens: []types.Token(nil)} - }, - true, - }, - { - "1 token exists", - func() { - req = &types.QueryTokensRequest{} - token := types.Token{ - Manager: suite.testUser1Address, - Name: "rst", - Symbol: "rst", - Total: "1000", - AuthorizationRequired: false, - } - err := suite.app.AssetKeeper.Token.Set(suite.ctx, types.TokenKey("rst"), token) - suite.Require().NoError(err) - - expRes = &types.QueryTokensResponse{ - Tokens: []types.Token{token}, - } - }, - true, - }, - { - "2 tokens exists", - func() { - req = &types.QueryTokensRequest{} - token1 := types.Token{ - Manager: suite.testUser1Address, - Name: "rst", - Symbol: "rst", - Total: "1000", - AuthorizationRequired: false, - } - err := suite.app.AssetKeeper.Token.Set(suite.ctx, types.TokenKey("rst"), token1) - suite.Require().NoError(err) - - token2 := types.Token{ - Manager: suite.testUser1Address, - Name: "bitcoinEtf", - Symbol: "btf", - Total: "1000", - AuthorizationRequired: false, - } - err = suite.app.AssetKeeper.Token.Set(suite.ctx, types.TokenKey("btf"), token2) - suite.Require().NoError(err) - - expRes = &types.QueryTokensResponse{ - Tokens: []types.Token{token2, token1}, - } - }, - true, - }, - } - for _, tc := range testCases { - suite.Run(fmt.Sprintf("Case %s", tc.name), func() { - suite.SetupTest() // reset - - ctx := suite.ctx - tc.malleate() - - res, err := suite.queryClient.Tokens(ctx, req) - if tc.expPass { - suite.Require().NoError(err) - suite.Require().Equal(expRes.Tokens, res.Tokens) - suite.Require().ElementsMatch(expRes.Tokens, res.Tokens) - } else { - suite.Require().Error(err) - } - }) - } -} diff --git a/x/asset/keeper/keeper.go b/x/asset/keeper/keeper.go index 74b812e0..495c5237 100644 --- a/x/asset/keeper/keeper.go +++ b/x/asset/keeper/keeper.go @@ -1,6 +1,7 @@ package keeper import ( + "bytes" "context" "fmt" @@ -10,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" "github.com/realiotech/realio-network/x/asset/types" ) @@ -17,17 +19,19 @@ import ( type Keeper struct { cdc codec.Codec storeService store.KVStoreService - bankKeeper types.BankKeeper - ak types.AccountKeeper - allowAddrs map[string]bool + bk types.BankKeeper + Ak types.AccountKeeper // the address capable of executing a MsgUpdateParams message. Typically, this // should be the x/gov module account. authority string - Schema collections.Schema - Params collections.Item[types.Params] - Token collections.Map[string, types.Token] + Schema collections.Schema + Params collections.Item[types.Params] + Token collections.Map[string, types.Token] + TokenManagement collections.Map[string, types.TokenManagement] + WhitelistAddresses collections.Map[[]byte, bool] + FreezeAddresses collections.Map[[]byte, bool] } // NewKeeper returns a new Keeper object with a given codec, dedicated @@ -36,21 +40,22 @@ type Keeper struct { func NewKeeper( cdc codec.Codec, storeService store.KVStoreService, - bankKeeper types.BankKeeper, + bk types.BankKeeper, ak types.AccountKeeper, - allowAddrs map[string]bool, authority string, ) *Keeper { sb := collections.NewSchemaBuilder(storeService) k := Keeper{ - cdc: cdc, - storeService: storeService, - authority: authority, - bankKeeper: bankKeeper, - ak: ak, - allowAddrs: allowAddrs, - Params: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[types.Params](cdc)), - Token: collections.NewMap(sb, types.TokenKeyPrefix, "token", collections.StringKey, codec.CollValue[types.Token](cdc)), + cdc: cdc, + storeService: storeService, + authority: authority, + bk: bk, + Ak: ak, + Params: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[types.Params](cdc)), + Token: collections.NewMap(sb, types.TokenKey, "token", collections.StringKey, codec.CollValue[types.Token](cdc)), + TokenManagement: collections.NewMap(sb, types.TokenManagementKey, "token_management", collections.StringKey, codec.CollValue[types.TokenManagement](cdc)), + WhitelistAddresses: collections.NewMap(sb, types.WhitelistAddressesKey, "whitelist_addresses", collections.BytesKey, collections.BoolValue), + FreezeAddresses: collections.NewMap(sb, types.FreezeAddressesKey, "freeze_addresses", collections.BytesKey, collections.BoolValue), } schema, err := sb.Build() @@ -66,11 +71,85 @@ func (k Keeper) Logger(ctx context.Context) log.Logger { return sdkCtx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } -func (k Keeper) IsAddressAuthorizedToSend(ctx context.Context, symbol string, address sdk.AccAddress) (authorized bool) { - token, err := k.Token.Get(ctx, types.TokenKey(symbol)) +func (k Keeper) GetWhitelistAddress(ctx context.Context, address []byte) bool { + found, err := k.WhitelistAddresses.Get(ctx, address) if err != nil { return false } - return token.AddressIsAuthorized(address) + return found +} + +func (k Keeper) EVMContractExist(ctx context.Context, address common.Address) (bool, string, error) { + exist := false + tokenId := "" + err := k.Token.Walk(ctx, nil, func(key string, token types.Token) (stop bool, err error) { + if token.EvmAddress == address.String() { + exist = true + tokenId = key + return true, nil + } + return false, nil + }) + + if err != nil { + return false, "", err + } + + return exist, tokenId, nil +} + +func (k Keeper) GetParams(ctx context.Context) (types.Params, error) { + return k.Params.Get(ctx) +} + +func (k Keeper) IsTokenManager(ctx context.Context, tokenId string, addr []byte) (bool, error) { + exist := false + tm, err := k.TokenManagement.Get(ctx, tokenId) + if err != nil { + return false, err + } + for _, manager := range tm.Managers { + if bytes.Equal(addr, manager) { + exist = true + break + } + } + return exist, nil +} + +func (k Keeper) IsTokenOwner(ctx context.Context, tokenId string, addr []byte) (bool, error) { + token, err := k.Token.Get(ctx, tokenId) + if err != nil { + return false, err + } + + if bytes.Equal(token.Issuer, addr) { + return true, nil + } + return false, nil +} + +func (k Keeper) IsFreezed(ctx context.Context, addr common.Address) bool { + exist, err := k.FreezeAddresses.Get(ctx, addr.Bytes()) + if err != nil { + return false + } + return exist +} + +func (k Keeper) SetFreezeAddress(ctx context.Context, addr common.Address) error { + err := k.FreezeAddresses.Set(ctx, addr.Bytes(), true) + if err != nil { + return err + } + return nil +} + +func (k Keeper) GetToken(ctx context.Context, tokenId string) (types.Token, error) { + return k.Token.Get(ctx, tokenId) +} + +func (k Keeper) GetTokenManager(ctx context.Context, tokenId string) (types.TokenManagement, error) { + return k.TokenManagement.Get(ctx, tokenId) } diff --git a/x/asset/keeper/keeper_test.go b/x/asset/keeper/keeper_test.go deleted file mode 100644 index 33ab9127..00000000 --- a/x/asset/keeper/keeper_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package keeper_test - -import ( - "testing" - "time" - - "github.com/cometbft/cometbft/crypto/tmhash" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - tmversion "github.com/cometbft/cometbft/proto/tendermint/version" - "github.com/cometbft/cometbft/version" - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/evmos/os/crypto/ethsecp256k1" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - - "github.com/realiotech/realio-network/app" - realiotypes "github.com/realiotech/realio-network/types" - "github.com/realiotech/realio-network/x/asset/keeper" - "github.com/realiotech/realio-network/x/asset/types" - - "github.com/realiotech/realio-network/testutil" -) - -type KeeperTestSuite struct { - suite.Suite - app *app.RealioNetwork - ctx sdk.Context - queryClient types.QueryClient - testUser1Acc sdk.AccAddress - testUser1Address string - testUser2Acc sdk.AccAddress - testUser2Address string - testUser3Acc sdk.AccAddress - testUser3Address string -} - -func (suite *KeeperTestSuite) SetupTest() { - suite.DoSetupTest(suite.T()) -} - -func (suite *KeeperTestSuite) DoSetupTest(t *testing.T) { - checkTx := false - - // user 1 key - suite.testUser1Acc = testutil.GenAddress() - suite.testUser1Address = suite.testUser1Acc.String() - - // user 2 key - suite.testUser2Acc = testutil.GenAddress() - suite.testUser2Address = suite.testUser2Acc.String() - - // user 3 key - suite.testUser3Acc = testutil.GenAddress() - suite.testUser3Address = suite.testUser3Acc.String() - - // consensus key - priv, err := ethsecp256k1.GenerateKey() - require.NoError(t, err) - consAddress := sdk.ConsAddress(priv.PubKey().Address()) - - // init app - suite.app = app.Setup(checkTx, nil, 1) - - // Set Context - suite.ctx = suite.app.BaseApp.NewContextLegacy(checkTx, tmproto.Header{ - Height: 1, - ChainID: realiotypes.TestnetChainID, - Time: time.Now().UTC(), - ProposerAddress: consAddress.Bytes(), - - Version: tmversion.Consensus{ - Block: version.BlockProtocol, - }, - LastBlockId: tmproto.BlockID{ - Hash: tmhash.Sum([]byte("block_id")), - PartSetHeader: tmproto.PartSetHeader{ - Total: 11, - Hash: tmhash.Sum([]byte("partset_header")), - }, - }, - AppHash: tmhash.Sum([]byte("app")), - DataHash: tmhash.Sum([]byte("data")), - EvidenceHash: tmhash.Sum([]byte("evidence")), - ValidatorsHash: tmhash.Sum([]byte("validators")), - NextValidatorsHash: tmhash.Sum([]byte("next_validators")), - ConsensusHash: tmhash.Sum([]byte("consensus")), - LastResultsHash: tmhash.Sum([]byte("last_result")), - }) - - queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry()) - types.RegisterQueryServer(queryHelper, keeper.NewQueryServerImpl(suite.app.AssetKeeper)) - suite.queryClient = types.NewQueryClient(queryHelper) -} - -func TestKeeperTestSuite(t *testing.T) { - suite.Run(t, new(KeeperTestSuite)) -} diff --git a/x/asset/keeper/migrator.go b/x/asset/keeper/migrator.go deleted file mode 100644 index 1d994f6c..00000000 --- a/x/asset/keeper/migrator.go +++ /dev/null @@ -1,29 +0,0 @@ -package keeper - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/realiotech/realio-network/x/asset/exported" - v2 "github.com/realiotech/realio-network/x/asset/migrations/v2" -) - -// Migrator is a struct for handling in-place state migrations. -type Migrator struct { - keeper Keeper - legacySubspace exported.Subspace -} - -// NewMigrator returns Migrator instance for the state migration. -func NewMigrator(k Keeper, ss exported.Subspace) Migrator { - return Migrator{ - keeper: k, - legacySubspace: ss, - } -} - -// Migrate1to2 migrates the x/mint module state from the consensus version 1 to -// version 2. Specifically, it takes the parameters that are currently stored -// and managed by the x/params modules and stores them directly into the x/mint -// module state. -func (m Migrator) Migrate1to2(ctx sdk.Context) error { - return v2.Migrate(ctx, m.keeper.storeService.OpenKVStore(ctx), m.legacySubspace, m.keeper.cdc) -} diff --git a/x/asset/keeper/msg_server.go b/x/asset/keeper/msg_server.go index 4cb7f0eb..6a0fcdb2 100644 --- a/x/asset/keeper/msg_server.go +++ b/x/asset/keeper/msg_server.go @@ -2,9 +2,16 @@ package keeper import ( "context" + "fmt" + "strings" - "cosmossdk.io/errors" + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + "github.com/ethereum/go-ethereum/common" "github.com/realiotech/realio-network/x/asset/types" ) @@ -20,10 +27,96 @@ func NewMsgServerImpl(keeper Keeper) types.MsgServer { var _ types.MsgServer = msgServer{} +// CreateToken allow issuer to define new token. +func (ms msgServer) CreateToken(ctx context.Context, msg *types.MsgCreateToken) (*types.MsgCreateTokenResponse, error) { + sdkCtx := sdk.UnwrapSDKContext(ctx) + + if !ms.GetWhitelistAddress(ctx, msg.Issuer) { + return nil, errorsmod.Wrapf(types.ErrUnauthorize, "issuer not in whitelisted addresses") + } + + lowerCaseName := strings.ToLower(msg.Name) + lowerCaseSymbol := strings.ToLower(msg.Symbol) + tokenId := fmt.Sprintf("%s/%s/%s", types.ModuleName, msg.Issuer, lowerCaseSymbol) + + isFound := ms.bk.HasSupply(ctx, tokenId) + if isFound { + return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "token with id %s already exists", tokenId) + } + + // Create a evm addr from tokenId + evmAddr := common.BytesToAddress([]byte(tokenId)) + + token := types.NewToken(tokenId, msg.Name, msg.Decimal, msg.Description, msg.Symbol, msg.Issuer, evmAddr.String()) + err := ms.Token.Set(ctx, tokenId, token) + if err != nil { + return nil, errorsmod.Wrap(types.ErrTokenSet, err.Error()) + } + + tokenManagement := types.NewTokenManagement(msg.Managers, msg.AllowNewExtensions, msg.ExtensionsList, msg.MaxSupply) + err = ms.TokenManagement.Set(ctx, tokenId, tokenManagement) + if err != nil { + return nil, errorsmod.Wrap(types.ErrTokenManagementSet, err.Error()) + } + + ms.bk.SetDenomMetaData(ctx, banktypes.Metadata{ + Base: tokenId, Symbol: lowerCaseSymbol, Name: lowerCaseName, + DenomUnits: []*banktypes.DenomUnit{{Denom: lowerCaseSymbol, Exponent: msg.Decimal}, {Denom: tokenId, Exponent: 0}}, + }) + + sdkCtx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeTokenCreated, + sdk.NewAttribute(types.AttributeKeyTokenId, tokenId), + sdk.NewAttribute(types.AttributeKeyAddress, sdk.AccAddress(msg.Issuer).String()), + ), + ) + + return &types.MsgCreateTokenResponse{ + TokenId: tokenId, + }, nil +} + +func (ms msgServer) AssignRoles(ctx context.Context, msg *types.MsgAssignRoles) (*types.MsgAssignRolesResponse, error) { + sdkCtx := sdk.UnwrapSDKContext(ctx) + + err := ms.Keeper.GrantRole(ctx, msg.TokenId, msg.Issuer, msg.Managers) + if err != nil { + return nil, err + } + + sdkCtx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeTokenAuthorizeUpdated, + sdk.NewAttribute(types.AttributeKeyTokenId, msg.TokenId), + ), + ) + + return &types.MsgAssignRolesResponse{}, nil +} + +func (ms msgServer) UnassignRoles(ctx context.Context, msg *types.MsgUnassignRoles) (*types.MsgUnassignRolesResponse, error) { + sdkCtx := sdk.UnwrapSDKContext(ctx) + + err := ms.Keeper.RevokeRole(ctx, msg.TokenId, msg.Issuer, msg.Managers) + if err != nil { + return nil, err + } + + sdkCtx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeTokenAuthorizeUpdated, + sdk.NewAttribute(types.AttributeKeyTokenId, msg.TokenId), + ), + ) + + return &types.MsgUnassignRolesResponse{}, nil +} + // UpdateParams updates the params. func (ms msgServer) UpdateParams(ctx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) { if ms.authority != msg.Authority { - return nil, errors.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", ms.authority, msg.Authority) + return nil, errorsmod.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", ms.authority, msg.Authority) } if err := msg.Params.Validate(); err != nil { diff --git a/x/asset/keeper/msg_server_authorize_address.go b/x/asset/keeper/msg_server_authorize_address.go deleted file mode 100644 index f9ccc251..00000000 --- a/x/asset/keeper/msg_server_authorize_address.go +++ /dev/null @@ -1,46 +0,0 @@ -package keeper - -import ( - "context" - - errorsmod "cosmossdk.io/errors" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/realiotech/realio-network/x/asset/types" -) - -func (ms msgServer) AuthorizeAddress(goCtx context.Context, msg *types.MsgAuthorizeAddress) (*types.MsgAuthorizeAddressResponse, error) { - // Check if the value exists - token, err := ms.Token.Get(goCtx, types.TokenKey(msg.Symbol)) - if err != nil { - return nil, errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "symbol %s does not exists : %s", msg.Symbol, err.Error()) - } - - // assert that the manager account is the only signer of the message - if msg.Manager != token.Manager { - return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "caller not authorized") - } - - accAddress, err := sdk.AccAddressFromBech32(msg.Address) - if err != nil { - return nil, errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "invalid address") - } - - token.AuthorizeAddress(accAddress) - err = ms.Token.Set(goCtx, types.TokenKey(msg.Symbol), token) - if err != nil { - return nil, types.ErrSetTokenUnable - } - - ctx := sdk.UnwrapSDKContext(goCtx) - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeTokenAuthorized, - sdk.NewAttribute(types.AttributeKeySymbol, msg.Symbol), - sdk.NewAttribute(types.AttributeKeyAddress, msg.Address), - ), - ) - - return &types.MsgAuthorizeAddressResponse{}, nil -} diff --git a/x/asset/keeper/msg_server_create_token.go b/x/asset/keeper/msg_server_create_token.go deleted file mode 100644 index fb3ab977..00000000 --- a/x/asset/keeper/msg_server_create_token.go +++ /dev/null @@ -1,83 +0,0 @@ -package keeper - -import ( - "context" - "fmt" - "strings" - - errorsmod "cosmossdk.io/errors" - bank "github.com/cosmos/cosmos-sdk/x/bank/types" - - realionetworktypes "github.com/realiotech/realio-network/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - "cosmossdk.io/math" - "github.com/realiotech/realio-network/x/asset/types" -) - -func (ms msgServer) CreateToken(goCtx context.Context, msg *types.MsgCreateToken) (*types.MsgCreateTokenResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - // Check if the value already exists - lowerCaseSymbol := strings.ToLower(msg.Symbol) - lowerCaseName := strings.ToLower(msg.Name) - baseDenom := fmt.Sprintf("a%s", lowerCaseSymbol) - - isFound := ms.bankKeeper.HasSupply(ctx, baseDenom) - if isFound { - return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "token with denom %s already exists", baseDenom) - } - - managerAccAddress, err := sdk.AccAddressFromBech32(msg.Manager) - if err != nil { - return nil, errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "invalid manager address") - } - - token := types.NewToken(lowerCaseName, lowerCaseSymbol, msg.Total, msg.Manager, msg.AuthorizationRequired) - - if msg.AuthorizationRequired { - // create authorization for module account and manager - assetModuleAddress := ms.ak.GetModuleAddress(types.ModuleName) - moduleAuthorization := types.NewAuthorization(assetModuleAddress) - newAuthorizationManager := types.NewAuthorization(managerAccAddress) - token.Authorized = append(token.Authorized, moduleAuthorization, newAuthorizationManager) - } - - err = ms.Token.Set(goCtx, types.TokenKey(lowerCaseSymbol), token) - if err != nil { - return nil, types.ErrSetTokenUnable - } - - ms.bankKeeper.SetDenomMetaData(ctx, bank.Metadata{ - Base: baseDenom, Symbol: lowerCaseSymbol, Name: lowerCaseName, - DenomUnits: []*bank.DenomUnit{{Denom: lowerCaseSymbol, Exponent: 18}, {Denom: baseDenom, Exponent: 0}}, - }) - - // mint coins for the current module - // normalize into chains 10^18 denomination - totalInt, _ := math.NewIntFromString(msg.Total) - canonicalAmount := totalInt.Mul(realionetworktypes.PowerReduction) - coin := sdk.Coins{{Denom: baseDenom, Amount: canonicalAmount}} - - err = ms.bankKeeper.MintCoins(ctx, types.ModuleName, coin) - if err != nil { - panic(err) - } - - err = ms.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, managerAccAddress, coin) - if err != nil { - panic(err) - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeTokenCreated, - sdk.NewAttribute(sdk.AttributeKeyAmount, fmt.Sprint(msg.Total)), - sdk.NewAttribute(types.AttributeKeySymbol, msg.Symbol), - ), - ) - - return &types.MsgCreateTokenResponse{}, nil -} diff --git a/x/asset/keeper/msg_server_token_test.go b/x/asset/keeper/msg_server_token_test.go deleted file mode 100644 index 8d2d4335..00000000 --- a/x/asset/keeper/msg_server_token_test.go +++ /dev/null @@ -1,408 +0,0 @@ -package keeper_test - -import ( - "strconv" - "strings" - - "cosmossdk.io/math" - realionetworktypes "github.com/realiotech/realio-network/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - "github.com/realiotech/realio-network/x/asset/keeper" - "github.com/realiotech/realio-network/x/asset/types" -) - -// Prevent strconv unused error -var _ = strconv.IntSize - -func (suite *KeeperTestSuite) TestTokenMsgServerCreate() { - testCases := []struct { - name string - msg *types.MsgCreateToken - expectErr bool - errString string - }{ - { - name: "valid MsgCreateToken", - msg: &types.MsgCreateToken{ - Manager: suite.testUser1Address, - Symbol: "FOO", Total: "1000", - }, - expectErr: false, - }, - { - name: "invalid MsgCreateToken; duplicated denom ario", - msg: &types.MsgCreateToken{ - Manager: suite.testUser1Address, - Symbol: "RIO", Total: "100", - }, - expectErr: true, - errString: "token with denom ario already exists: invalid request", - }, - { - name: "invalid MsgCreateToken; duplicated denom abar", - msg: &types.MsgCreateToken{ - Manager: suite.testUser1Address, - Symbol: "BAR", Total: "100", - }, - expectErr: true, - errString: "token with denom abar already exists: invalid request", - }, - { - name: "invalid MsgCreateToken; invalid manager address", - msg: &types.MsgCreateToken{ - Manager: "invalid address", - Symbol: "FOO", Total: "100", - }, - expectErr: true, - errString: "invalid manager address: invalid address", - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() - - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - - // we created a token here with symbol "BAR" - // there's a test case to make sure we CANNOT create tokens with the same symbol - _, err := srv.CreateToken(wctx, &types.MsgCreateToken{ - Manager: suite.testUser1Address, - Symbol: "BAR", Total: "999999", - }) - suite.Require().NoError(err) - - _, err = srv.CreateToken(wctx, tc.msg) - if tc.expectErr { - suite.Require().EqualError(err, tc.errString) - } else { - suite.Require().NoError(err) - - token, err := suite.app.AssetKeeper.Token.Get(suite.ctx, - types.TokenKey(tc.msg.Symbol), - ) - suite.Require().NoError(err) - suite.Require().Equal(token.Manager, tc.msg.Manager) - suite.Require().Equal(token.Symbol, strings.ToLower(tc.msg.Symbol)) - suite.Require().Equal(token.Total, tc.msg.Total) - suite.Require().Equal(token.AuthorizationRequired, tc.msg.AuthorizationRequired) - } - }) - } -} - -func (suite *KeeperTestSuite) TestTokenMsgServerCreateInvalidSender() { - suite.SetupTest() - - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - manager := "invalid" - expected := &types.MsgCreateToken{ - Manager: manager, - Symbol: "BTC", Total: "1000", - } - _, err := srv.CreateToken(wctx, expected) - suite.Require().ErrorIs(err, sdkerrors.ErrInvalidAddress) -} - -func (suite *KeeperTestSuite) TestTokenMsgServerCreateAuthorizationDefaultFalse() { - suite.SetupTest() - - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - manager := suite.testUser1Address - expected := &types.MsgCreateToken{ - Manager: manager, - Symbol: "BTC", Total: "1000", - } - _, err := srv.CreateToken(wctx, expected) - suite.Require().NoError(err) - token, _ := suite.app.AssetKeeper.Token.Get(suite.ctx, - types.TokenKey(expected.Symbol), - ) - suite.Require().False(token.AuthorizationRequired) -} - -func (suite *KeeperTestSuite) TestTokenMsgServerCreateErrorDupIndex() { - suite.SetupTest() - - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - manager := suite.testUser1Address - t1 := &types.MsgCreateToken{ - Manager: manager, - Symbol: "BTC", Total: "1000", - } - _, err := srv.CreateToken(wctx, t1) - suite.Require().NoError(err) - - t2 := &types.MsgCreateToken{ - Manager: manager, - Symbol: "BTC", Total: "1000", - } - _, err2 := srv.CreateToken(wctx, t2) - suite.Require().Error(err2) - suite.Require().ErrorIs(err2, sdkerrors.ErrInvalidRequest) -} - -func (suite *KeeperTestSuite) TestTokenMsgServerCreateVerifyDistribution() { - suite.SetupTest() - - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - manager := suite.testUser1Address - t1 := &types.MsgCreateToken{ - Manager: manager, - Symbol: "RST", Total: "1000", - } - _, err := srv.CreateToken(wctx, t1) - suite.Require().NoError(err) - - totalInt, _ := math.NewIntFromString("1000") - canonicalAmount := totalInt.Mul(realionetworktypes.PowerReduction) - - account, _ := sdk.AccAddressFromBech32(manager) - managerBalance := suite.app.BankKeeper.GetBalance(suite.ctx, account, "arst") - suite.Require().Equal(managerBalance.Amount, canonicalAmount) - - totalbalance := suite.app.BankKeeper.GetSupply(suite.ctx, "arst") - suite.Require().Equal(totalbalance.Amount, canonicalAmount) -} - -func (suite *KeeperTestSuite) TestTokenMsgServerUpdate() { - suite.SetupTest() - - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - manager := suite.testUser1Address - t1 := &types.MsgCreateToken{ - Manager: manager, - Symbol: "BTC", Total: "1000", - } - _, err := srv.CreateToken(wctx, t1) - suite.Require().NoError(err) - - token, _ := suite.app.AssetKeeper.Token.Get(suite.ctx, - types.TokenKey(t1.Symbol), - ) - suite.Require().False(token.AuthorizationRequired) - - updateMsg := &types.MsgUpdateToken{ - Manager: manager, - Symbol: "BTC", AuthorizationRequired: true, - } - - _, err = srv.UpdateToken(wctx, updateMsg) - - token, _ = suite.app.AssetKeeper.Token.Get(suite.ctx, - types.TokenKey(t1.Symbol), - ) - suite.Require().NoError(err) - suite.Require().True(token.AuthorizationRequired) - suite.Require().Equal(token.Total, "1000") -} - -func (suite *KeeperTestSuite) TestTokenMsgServerUpdateNotFound() { - suite.SetupTest() - - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - manager := suite.testUser1Address - t1 := &types.MsgCreateToken{ - Manager: manager, - Symbol: "BTC", Total: "1000", - } - _, err := srv.CreateToken(wctx, t1) - suite.Require().NoError(err) - - updateMsg := &types.MsgUpdateToken{ - Manager: manager, - Symbol: "RST", AuthorizationRequired: true, - } - - _, err = srv.UpdateToken(wctx, updateMsg) - suite.Require().ErrorIs(err, sdkerrors.ErrKeyNotFound) -} - -func (suite *KeeperTestSuite) TestTokenMsgServerAuthorizeAddress() { - suite.SetupTest() - - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - manager := suite.testUser1Address - testUser := suite.testUser2Address - t1 := &types.MsgCreateToken{ - Manager: manager, - Symbol: "BTC", Total: "1000", AuthorizationRequired: true, - } - _, err := srv.CreateToken(wctx, t1) - suite.Require().NoError(err) - - token, _ := suite.app.AssetKeeper.Token.Get(suite.ctx, - types.TokenKey(t1.Symbol), - ) - suite.Require().True(token.AddressIsAuthorized(suite.testUser1Acc)) - - authUserMsg := &types.MsgAuthorizeAddress{ - Manager: manager, - Symbol: "BTC", Address: testUser, - } - - _, err = srv.AuthorizeAddress(wctx, authUserMsg) - suite.Require().NoError(err) - - token, _ = suite.app.AssetKeeper.Token.Get(suite.ctx, - types.TokenKey(t1.Symbol), - ) - suite.Require().NotNil(token.Authorized) - suite.Require().True(token.AddressIsAuthorized(suite.testUser1Acc)) -} - -func (suite *KeeperTestSuite) TestTokenMsgServerAuthorizeTokenNotFound() { - suite.SetupTest() - - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - manager := suite.testUser1Address - testUser := suite.testUser2Address - t1 := &types.MsgCreateToken{ - Manager: manager, - Symbol: "BTC", Total: "1000", AuthorizationRequired: true, - } - _, err := srv.CreateToken(wctx, t1) - suite.Require().NoError(err) - - authUserMsg := &types.MsgAuthorizeAddress{ - Manager: manager, - Symbol: "RST", Address: testUser, - } - - _, err = srv.AuthorizeAddress(wctx, authUserMsg) - - suite.Require().ErrorIs(err, sdkerrors.ErrKeyNotFound) -} - -func (suite *KeeperTestSuite) TestTokenMsgServerAuthorizeAddressSenderUnauthorized() { - suite.SetupTest() - - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - manager := suite.testUser1Address - manager2 := suite.testUser2Address - testUser := suite.testUser3Address - - t1 := &types.MsgCreateToken{ - Manager: manager, - Symbol: "BTC", Total: "1000", AuthorizationRequired: true, - } - _, err := srv.CreateToken(wctx, t1) - suite.Require().NoError(err) - - authUserMsg := &types.MsgAuthorizeAddress{ - Manager: manager2, - Symbol: "BTC", Address: testUser, - } - - _, err = srv.AuthorizeAddress(wctx, authUserMsg) - - suite.Require().ErrorIs(err, sdkerrors.ErrUnauthorized) -} - -func (suite *KeeperTestSuite) TestTokenMsgServerUnAuthorizeAddress() { - suite.SetupTest() - - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - manager := suite.testUser1Address - testUser := suite.testUser2Address - t1 := &types.MsgCreateToken{ - Manager: manager, - Symbol: "BTC", Total: "1000", AuthorizationRequired: true, - } - _, err := srv.CreateToken(wctx, t1) - suite.Require().NoError(err) - - token, _ := suite.app.AssetKeeper.Token.Get(suite.ctx, - types.TokenKey(t1.Symbol), - ) - suite.Require().True(token.AddressIsAuthorized(suite.testUser1Acc)) - - authUserMsg := &types.MsgAuthorizeAddress{ - Manager: manager, - Symbol: "BTC", Address: testUser, - } - - _, err = srv.AuthorizeAddress(wctx, authUserMsg) - suite.Require().NoError(err) - - token, _ = suite.app.AssetKeeper.Token.Get(suite.ctx, - types.TokenKey(t1.Symbol), - ) - suite.Require().True(token.AddressIsAuthorized(suite.testUser2Acc)) - - unAuthUserMsg := &types.MsgUnAuthorizeAddress{ - Manager: manager, - Symbol: "BTC", Address: testUser, - } - - _, err = srv.UnAuthorizeAddress(wctx, unAuthUserMsg) - suite.Require().NoError(err) - - token, _ = suite.app.AssetKeeper.Token.Get(suite.ctx, - types.TokenKey(t1.Symbol), - ) - suite.Require().False(token.AddressIsAuthorized(suite.testUser2Acc)) -} - -func (suite *KeeperTestSuite) TestTokenMsgServerUnAuthorizeTokenNotFound() { - suite.SetupTest() - - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - manager := suite.testUser1Address - testUser := suite.testUser2Address - t1 := &types.MsgCreateToken{ - Manager: manager, - Symbol: "BTC", Total: "1000", AuthorizationRequired: true, - } - _, err := srv.CreateToken(wctx, t1) - suite.Require().NoError(err) - - unAuthUserMsg := &types.MsgUnAuthorizeAddress{ - Manager: manager, - Symbol: "RST", Address: testUser, - } - - _, err = srv.UnAuthorizeAddress(wctx, unAuthUserMsg) - - suite.Require().ErrorIs(err, sdkerrors.ErrKeyNotFound) -} - -func (suite *KeeperTestSuite) TestTokenMsgServerUnAuthorizeAddressSenderUnauthorized() { - suite.SetupTest() - - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - manager := suite.testUser1Address - manager2 := suite.testUser2Address - testUser := suite.testUser3Address - t1 := &types.MsgCreateToken{ - Manager: manager, - Symbol: "BTC", Total: "1000", AuthorizationRequired: true, - } - _, err := srv.CreateToken(wctx, t1) - suite.Require().NoError(err) - - unAuthUserMsg := &types.MsgUnAuthorizeAddress{ - Manager: manager2, - Symbol: "BTC", Address: testUser, - } - - _, err = srv.UnAuthorizeAddress(wctx, unAuthUserMsg) - - suite.Require().ErrorIs(err, sdkerrors.ErrUnauthorized) -} diff --git a/x/asset/keeper/msg_server_transfer_token.go b/x/asset/keeper/msg_server_transfer_token.go deleted file mode 100644 index d1d01642..00000000 --- a/x/asset/keeper/msg_server_transfer_token.go +++ /dev/null @@ -1,63 +0,0 @@ -package keeper - -import ( - "context" - "fmt" - "strings" - - errorsmod "cosmossdk.io/errors" - "cosmossdk.io/math" - - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/realiotech/realio-network/x/asset/types" -) - -func (ms msgServer) TransferToken(goCtx context.Context, msg *types.MsgTransferToken) (*types.MsgTransferTokenResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - var fromAddress, toAddress sdk.AccAddress - isAuthorizedFrom, isAuthorizedTo := true, true - - lowerCaseSymbol := strings.ToLower(msg.Symbol) - - fromAddress, _ = sdk.AccAddressFromBech32(msg.From) - toAddress, _ = sdk.AccAddressFromBech32(msg.To) - // Check if the value already exists - token, err := ms.Token.Get( - ctx, - types.TokenKey(msg.Symbol), - ) - if err != nil { - return nil, errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "token %s not found: %s", lowerCaseSymbol, err.Error()) - } - - if ms.bankKeeper.BlockedAddr(toAddress) { - return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", msg.To) - } - - if token.AuthorizationRequired { - isAuthorizedFrom = ms.IsAddressAuthorizedToSend(ctx, lowerCaseSymbol, fromAddress) - isAuthorizedTo = ms.IsAddressAuthorizedToSend(ctx, lowerCaseSymbol, toAddress) - } - - if isAuthorizedFrom && isAuthorizedTo { - totalInt, totalIsValid := math.NewIntFromString(msg.Amount) - if !totalIsValid { - return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidCoins, "invalid coin amount %s", msg.Amount) - } - - baseDenom := fmt.Sprintf("a%s", lowerCaseSymbol) - coin := sdk.Coins{{Denom: baseDenom, Amount: totalInt}} - err := ms.bankKeeper.SendCoins(ctx, fromAddress, toAddress, coin) - if err != nil { - return nil, err - } - } else { - return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "%s transfer not authorized", lowerCaseSymbol) - } - - return &types.MsgTransferTokenResponse{}, nil -} diff --git a/x/asset/keeper/msg_server_transfer_token_test.go b/x/asset/keeper/msg_server_transfer_token_test.go deleted file mode 100644 index 260182ad..00000000 --- a/x/asset/keeper/msg_server_transfer_token_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package keeper_test - -import ( - "fmt" - "strconv" - - "github.com/realiotech/realio-network/x/asset/keeper" - "github.com/realiotech/realio-network/x/asset/types" -) - -// Prevent strconv unused error -var _ = strconv.IntSize - -func (suite *KeeperTestSuite) TestTransferToken() { - suite.SetupTest() - - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - - manager := suite.testUser1Address - testUser := suite.testUser2Address - - t1 := &types.MsgCreateToken{ - Manager: manager, - Symbol: "rst", Total: "1000", AuthorizationRequired: true, - } - _, err := srv.CreateToken(wctx, t1) - suite.Require().NoError(err) - - authUserMsg := &types.MsgAuthorizeAddress{ - Manager: manager, - Symbol: "rst", Address: manager, - } - - _, err = srv.AuthorizeAddress(wctx, authUserMsg) - suite.Require().NoError(err) - - authUser2Msg := &types.MsgAuthorizeAddress{ - Manager: manager, - Symbol: "RST", Address: testUser, - } - - _, err = srv.AuthorizeAddress(wctx, authUser2Msg) - suite.Require().NoError(err) - - amount := "50000000000000000000" - expected := &types.MsgTransferToken{Symbol: "RST", From: manager, To: testUser, Amount: amount} - - _, err = srv.TransferToken(wctx, expected) - suite.Require().NoError(err) - - balance := suite.app.BankKeeper.GetBalance(suite.ctx, suite.testUser2Acc, "arst") - suite.Require().Equal(balance.String(), fmt.Sprintf("%s%s", amount, "arst")) -} - -func (suite *KeeperTestSuite) TestTransferTokenInvalidAmount() { - suite.SetupTest() - - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - - manager := suite.testUser1Address - testUser := suite.testUser2Address - - t1 := &types.MsgCreateToken{ - Manager: manager, - Symbol: "rst", Total: "1000", AuthorizationRequired: true, - } - _, err := srv.CreateToken(wctx, t1) - suite.Require().NoError(err) - - authUserMsg := &types.MsgAuthorizeAddress{ - Manager: manager, - Symbol: "rst", Address: manager, - } - - _, err = srv.AuthorizeAddress(wctx, authUserMsg) - suite.Require().NoError(err) - - authUser2Msg := &types.MsgAuthorizeAddress{ - Manager: manager, - Symbol: "rst", Address: testUser, - } - - _, err = srv.AuthorizeAddress(wctx, authUser2Msg) - suite.Require().NoError(err) - - // amount is invalid, all amounts should be in base 10^18 amount - amount := "50000000000000000000.00" - expected := &types.MsgTransferToken{Symbol: "rst", From: manager, To: testUser, Amount: amount} - - _, err = srv.TransferToken(wctx, expected) - suite.Require().Error(err) -} - -func (suite *KeeperTestSuite) TestTransferTokenSenderBalanceToSmall() { - suite.SetupTest() - - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - - manager := suite.testUser1Address - testUser := suite.testUser2Address - - t1 := &types.MsgCreateToken{ - Manager: manager, - Symbol: "RST", Total: "1000", AuthorizationRequired: true, - } - _, err := srv.CreateToken(wctx, t1) - suite.Require().NoError(err) - - authUserMsg := &types.MsgAuthorizeAddress{ - Manager: manager, - Symbol: "RST", Address: manager, - } - - _, err = srv.AuthorizeAddress(wctx, authUserMsg) - suite.Require().NoError(err) - - authUser2Msg := &types.MsgAuthorizeAddress{ - Manager: manager, - Symbol: "RST", Address: testUser, - } - - _, err = srv.AuthorizeAddress(wctx, authUser2Msg) - suite.Require().NoError(err) - - // amount is invalid, all amounts should be in base 10^18 amount - amount := "1001000000000000000000" - expected := &types.MsgTransferToken{Symbol: "RST", From: manager, To: testUser, Amount: amount} - - _, err = srv.TransferToken(wctx, expected) - suite.Require().Error(err) - suite.Require().Equal(err.Error(), "spendable balance 1000000000000000000000arst is smaller than 1001000000000000000000arst: insufficient funds") -} diff --git a/x/asset/keeper/msg_server_un_authorize_address.go b/x/asset/keeper/msg_server_un_authorize_address.go deleted file mode 100644 index 0b9e17c5..00000000 --- a/x/asset/keeper/msg_server_un_authorize_address.go +++ /dev/null @@ -1,46 +0,0 @@ -package keeper - -import ( - "context" - - errorsmod "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/realiotech/realio-network/x/asset/types" -) - -func (ms msgServer) UnAuthorizeAddress(goCtx context.Context, msg *types.MsgUnAuthorizeAddress) (*types.MsgUnAuthorizeAddressResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - // Check if the value exists - token, err := ms.Token.Get(ctx, types.TokenKey(msg.Symbol)) - if err != nil { - return nil, errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "symbol %s does not exists: %s", msg.Symbol, err.Error()) - } - - // assert that the manager account is the only signer of the message - if msg.Manager != token.Manager { - return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "caller not authorized") - } - - accAddress, err := sdk.AccAddressFromBech32(msg.Address) - if err != nil { - return nil, errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "invalid address") - } - - token.UnAuthorizeAddress(accAddress) - err = ms.Token.Set(goCtx, types.TokenKey(msg.Symbol), token) - if err != nil { - return nil, types.ErrSetTokenUnable - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeTokenUnAuthorized, - sdk.NewAttribute(types.AttributeKeySymbol, msg.Symbol), - sdk.NewAttribute(types.AttributeKeyAddress, msg.Address), - ), - ) - - return &types.MsgUnAuthorizeAddressResponse{}, nil -} diff --git a/x/asset/keeper/msg_server_update_token.go b/x/asset/keeper/msg_server_update_token.go deleted file mode 100644 index 4d9c85b4..00000000 --- a/x/asset/keeper/msg_server_update_token.go +++ /dev/null @@ -1,48 +0,0 @@ -package keeper - -import ( - "context" - - errorsmod "cosmossdk.io/errors" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/realiotech/realio-network/x/asset/types" -) - -func (ms msgServer) UpdateToken(goCtx context.Context, msg *types.MsgUpdateToken) (*types.MsgUpdateTokenResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - existing, err := ms.Token.Get(ctx, types.TokenKey(msg.Symbol)) - if err != nil { - return nil, errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "symbol %s does not exists: %s", msg.Symbol, err.Error()) - } - - // assert that the manager account is the only signer of the message - if msg.Manager != existing.Manager { - return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "caller not authorized") - } - - // only Authorization Flag is updatable at this time - token := types.Token{ - Name: existing.Name, - Symbol: existing.Symbol, - Total: existing.Total, - Manager: existing.Manager, - AuthorizationRequired: msg.AuthorizationRequired, - } - - err = ms.Token.Set(goCtx, types.TokenKey(msg.Symbol), token) - if err != nil { - return nil, types.ErrSetTokenUnable - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeTokenUpdated, - sdk.NewAttribute(types.AttributeKeySymbol, msg.Symbol), - ), - ) - - return &types.MsgUpdateTokenResponse{}, nil -} diff --git a/x/asset/keeper/restrictions.go b/x/asset/keeper/restrictions.go deleted file mode 100644 index fd70bc60..00000000 --- a/x/asset/keeper/restrictions.go +++ /dev/null @@ -1,56 +0,0 @@ -package keeper - -import ( - "context" - - errorsmod "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/realiotech/realio-network/x/asset/types" -) - -func (k Keeper) AssetSendRestriction(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) (newToAddr sdk.AccAddress, err error) { - newToAddr = toAddr - - // module whitelisted addresses can send coins without restrictions - if allow := k.AllowAddr(fromAddr) || k.AllowAddr(toAddr); allow { - return newToAddr, nil - } - - for _, coin := range amt { - // Check if the value already exists - // fetch bank metadata to get symbol from denom - symbol := coin.Denom - tokenMetadata, found := k.bankKeeper.GetDenomMetaData(ctx, coin.Denom) - if found { - symbol = tokenMetadata.Symbol - } - token, err := k.Token.Get( - ctx, - types.TokenKey(symbol), - ) - if err != nil { - continue - } - - var isAuthorizedFrom, isAuthorizedTo bool - if token.AuthorizationRequired { - isAuthorizedFrom = k.IsAddressAuthorizedToSend(ctx, symbol, fromAddr) - isAuthorizedTo = k.IsAddressAuthorizedToSend(ctx, symbol, toAddr) - } else { - continue - } - - if isAuthorizedFrom && isAuthorizedTo { - continue - } else { //nolint:revive // superfluous else, could fix, but not worth it? - err := errorsmod.Wrapf(types.ErrNotAuthorized, "%s is not authorized to transact with %s", fromAddr, coin.Denom) - return nil, err - } - } - return newToAddr, nil -} - -// AllowAddr addr checks if a given address is in the list of allowAddrs to skip restrictions -func (k Keeper) AllowAddr(addr sdk.AccAddress) bool { - return k.allowAddrs[addr.String()] -} diff --git a/x/asset/keeper/restrictions_test.go b/x/asset/keeper/restrictions_test.go deleted file mode 100644 index 51501f1f..00000000 --- a/x/asset/keeper/restrictions_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package keeper_test - -import ( - "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/realiotech/realio-network/x/asset/keeper" - "github.com/realiotech/realio-network/x/asset/types" -) - -func (suite *KeeperTestSuite) TestRestrictions() { - srv := keeper.NewMsgServerImpl(suite.app.AssetKeeper) - wctx := suite.ctx - manager := suite.testUser1Address - testUser := suite.testUser2Address - t1 := &types.MsgCreateToken{ - Manager: manager, - Symbol: "RST", Total: "1000", AuthorizationRequired: true, - } - _, err := srv.CreateToken(wctx, t1) - suite.Require().NoError(err) - - authUserMsg := &types.MsgAuthorizeAddress{ - Manager: manager, - Symbol: "RST", Address: testUser, - } - - _, err = srv.AuthorizeAddress(wctx, authUserMsg) - suite.Require().NoError(err) - - cases := []struct { - name string - from sdk.AccAddress - to sdk.AccAddress - amount sdk.Coins - expPass bool - }{ - { - "module accounts can send to any account", - suite.app.AccountKeeper.GetModuleAddress(stakingtypes.BondedPoolName), - suite.testUser1Acc, - sdk.NewCoins(sdk.NewCoin("arst", math.NewInt(100))), - true, - }, - { - "module accounts can send to any account", - suite.app.AccountKeeper.GetModuleAddress(stakingtypes.NotBondedPoolName), - suite.testUser1Acc, - sdk.NewCoins(sdk.NewCoin("arst", math.NewInt(100))), - true, - }, - { - "module accounts can send to any account", - suite.app.AccountKeeper.GetModuleAddress(stakingtypes.BondedPoolName), - suite.testUser1Acc, - sdk.NewCoins(sdk.NewCoin("arst", math.NewInt(100))), - true, - }, - { - "unauthorized accounts cannot send", - suite.testUser3Acc, - suite.testUser1Acc, - sdk.NewCoins(sdk.NewCoin("arst", math.NewInt(100))), - false, - }, - } - - for _, tc := range cases { - toAddr, err := suite.app.AssetKeeper.AssetSendRestriction(suite.ctx, tc.from, tc.to, tc.amount) - if tc.expPass { - suite.Require().NoError(err, tc.name) - suite.Require().Equal(toAddr, tc.to) - } else { - suite.Require().Error(err) - } - } -} diff --git a/x/asset/migrations/v2/migrate.go b/x/asset/migrations/v2/migrate.go deleted file mode 100644 index ca7d57fb..00000000 --- a/x/asset/migrations/v2/migrate.go +++ /dev/null @@ -1,38 +0,0 @@ -package v2 - -import ( - storetypes "cosmossdk.io/core/store" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/realiotech/realio-network/x/asset/exported" - "github.com/realiotech/realio-network/x/asset/types" -) - -const ( - ModuleName = "asset" -) - -var ParamsKey = []byte{0x00} - -// Migrate migrates the x/mint module state from the consensus version 1 to -// version 2. Specifically, it takes the parameters that are currently stored -// and managed by the x/params modules and stores them directly into the x/mint -// module state. -func Migrate( - ctx sdk.Context, - store storetypes.KVStore, - legacySubspace exported.Subspace, - cdc codec.BinaryCodec, -) error { - var currParams types.Params - legacySubspace.GetParamSet(ctx, &currParams) - - if err := currParams.Validate(); err != nil { - return err - } - - bz := cdc.MustMarshal(&currParams) - return store.Set(ParamsKey, bz) -} diff --git a/x/asset/migrations/v2/migrator_test.go b/x/asset/migrations/v2/migrator_test.go deleted file mode 100644 index 85f8b2ae..00000000 --- a/x/asset/migrations/v2/migrator_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package v2_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - storetypes "cosmossdk.io/store/types" - - "github.com/cosmos/cosmos-sdk/runtime" - "github.com/cosmos/cosmos-sdk/testutil" - sdk "github.com/cosmos/cosmos-sdk/types" - moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" - - "github.com/realiotech/realio-network/x/asset" - "github.com/realiotech/realio-network/x/asset/exported" - v2 "github.com/realiotech/realio-network/x/asset/migrations/v2" - "github.com/realiotech/realio-network/x/asset/types" -) - -type mockSubspace struct { - ps types.Params -} - -func newMockSubspace(ps types.Params) mockSubspace { - return mockSubspace{ps: ps} -} - -func (ms mockSubspace) GetParamSet(_ sdk.Context, ps exported.ParamSet) { - *ps.(*types.Params) = ms.ps -} - -func TestMigrate(t *testing.T) { - encCfg := moduletestutil.MakeTestEncodingConfig(asset.AppModuleBasic{}) - cdc := encCfg.Codec - - storeKey := storetypes.NewKVStoreKey(v2.ModuleName) - tKey := storetypes.NewTransientStoreKey("transient_test") - ctx := testutil.DefaultContext(storeKey, tKey) - kvStoreService := runtime.NewKVStoreService(storeKey) - store := kvStoreService.OpenKVStore(ctx) - - legacySubspace := newMockSubspace(types.DefaultParams()) - require.NoError(t, v2.Migrate(ctx, store, legacySubspace, cdc)) - - var res types.Params - bz, err := store.Get(v2.ParamsKey) - require.NoError(t, err) - require.NoError(t, cdc.Unmarshal(bz, &res)) - require.Equal(t, legacySubspace.ps, res) -} diff --git a/x/asset/module.go b/x/asset/module.go index eeb832d5..5b098f15 100644 --- a/x/asset/module.go +++ b/x/asset/module.go @@ -8,7 +8,6 @@ import ( // this line is used by starport scaffolding # 1 "github.com/grpc-ecosystem/grpc-gateway/runtime" - "github.com/spf13/cobra" "cosmossdk.io/core/appmodule" "github.com/cosmos/cosmos-sdk/client" @@ -18,7 +17,6 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/realiotech/realio-network/x/asset/client/cli" "github.com/realiotech/realio-network/x/asset/exported" "github.com/realiotech/realio-network/x/asset/keeper" "github.com/realiotech/realio-network/x/asset/types" @@ -28,7 +26,7 @@ var ( _ module.AppModuleBasic = AppModule{} _ module.AppModuleSimulation = AppModule{} _ module.HasGenesis = AppModule{} - _ module.HasServices = AppModule{} + // _ module.HasServices = AppModule{} _ appmodule.AppModule = AppModule{} ) @@ -91,14 +89,14 @@ func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *r } // GetTxCmd returns the capability module's root tx command. -func (a AppModuleBasic) GetTxCmd() *cobra.Command { - return cli.GetTxCmd() -} +// func (a AppModuleBasic) GetTxCmd() *cobra.Command { +// return cli.GetTxCmd() +// } // GetQueryCmd returns the capability module's root query command. -func (AppModuleBasic) GetQueryCmd() *cobra.Command { - return cli.GetQueryCmd() -} +// func (AppModuleBasic) GetQueryCmd() *cobra.Command { +// return cli.GetQueryCmd() +// } // ---------------------------------------------------------------------------- // AppModule @@ -142,16 +140,16 @@ func (am AppModule) Name() string { // RegisterServices registers a GRPC query service to respond to the // module-specific GRPC queries. -func (am AppModule) RegisterServices(cfg module.Configurator) { - types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) - types.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServerImpl(am.keeper)) +// func (am AppModule) RegisterServices(cfg module.Configurator) { +// types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) +// types.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServerImpl(am.keeper)) - m := keeper.NewMigrator(am.keeper, am.legacySubspace) +// m := keeper.NewMigrator(am.keeper, am.legacySubspace) - if err := cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2); err != nil { - panic(fmt.Sprintf("failed to migrate x/%s from version 1 to 2: %v", types.ModuleName, err)) - } -} +// if err := cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2); err != nil { +// panic(fmt.Sprintf("failed to migrate x/%s from version 1 to 2: %v", types.ModuleName, err)) +// } +// } // RegisterInvariants registers the capability module's invariants. func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} diff --git a/x/asset/spec/01_concepts.md b/x/asset/spec/01_concepts.md index 76bd9677..00b6ff2a 100644 --- a/x/asset/spec/01_concepts.md +++ b/x/asset/spec/01_concepts.md @@ -6,28 +6,14 @@ order: 1 ## The Realio Asset Token Model -The Realio Asset module is centeredd aroumd a token model. It contains the following fields: +The Realio Asset module is centered around a token model where certain whitelisted accounts can issue their own token. A token issued by this module will be managed by `manager` accounts assigned by the issuer of the asset. -```protobuf -message Token { - string name = 1; - string symbol = 2; - int64 total = 3; - int64 decimals = 4; - bool authorizationRequired = 5; - string creator = 6; - map authorized = 7; - int64 created = 8; -} +### Token extensions -``` - -### Token Authorization - -The `Token` model provides a means to whitelist users via the `authorizationRequired` and `authorized` fields -A token that has the `authorizationRequired` turned on, can maintain a whitelist map of user addresses. These addresses -are the only ones able to send/receive the token. The Realio Network is agnostic to the logic of applications that use -the whitelisting. It is up to the clients to determine when to whitelist and what to do with it. +Token extensions are additional features that can be flug-in for each token. There're are four types of extensions `Mint`, `Burn`, `Transfer Auth` and `Freeze`. The `Issuer` can choose what extensions to be included for his token at creation time, and only the `manager` can trigger the extension's logic. +### EVM integration +While it is the asset token in represented in the bank module, enabling the token interface in evm environment is very convenient and open up the possibility of integrating new features into the ecosystem. +Each token is automatically enabled to work in the evm environment when created, which means user can interact with the token through evm side like metamask or anyother evm wallet and more other protocol integrated in the future. diff --git a/x/asset/spec/02_state.md b/x/asset/spec/02_state.md index bcc1e164..b005538e 100644 --- a/x/asset/spec/02_state.md +++ b/x/asset/spec/02_state.md @@ -8,50 +8,45 @@ order: 2 The `x/asset` module keeps the following objects in state: -| State Object | Description | Key | Value | Store | -|----------------------|--------------------------------|--------------------------| --------------- |-------| -| `Token` | Token bytecode | `[]byte{1} + []byte(id)` | `[]byte{token}` | KV | -| `TokenAuthorization` | Token Authorization bytecode | `[]byte{2} + []byte(id)` | `[]byte(id)` | KV | +| State Object | Description | Key | Value | Store | +|----------------------|----------------------------------------|-----------------------------------------------------------|---------------------------------------|-------| +| `Params` | Params of asset module | `[]byte{1}` | `[]byte(params)` | KV | +| `Token` | Token information | `[]byte{2} + []byte(token_id)` | `[]byte{token}` | KV | +| `TokenExtensions` | Token extensions info of a denom | `[]byte{3} + []byte(token_id)` | `[]byte{token_manager}` | KV | +| `WhitelistAddresses` | Whitelist Addresses | `[]byte{4} + []byte(address)` | `[]byte{bool}` | KV | +| `FreezeAddresses` | Whitelist Addresses | `[]byte{5} + []byte(address)` | `[]byte{bool}` | KV | +| `MaxSupply` | Maximum supply of token | `[]byte{6} + []byte(token_id)` | `[]byte{int64}` | KV | -### Token +### Token Allows creation of tokens with optional user authorization. ```go type Token struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` - Total int64 `protobuf:"varint,3,opt,name=total,proto3" json:"total,omitempty"` - Decimals int64 `protobuf:"varint,4,opt,name=decimals,proto3" json:"decimals,omitempty"` - AuthorizationRequired bool `protobuf:"varint,5,opt,name=authorizationRequired,proto3" json:"authorizationRequired,omitempty"` - Creator string `protobuf:"bytes,6,opt,name=creator,proto3" json:"creator,omitempty"` - Authorized map[string]*TokenAuthorization `protobuf:"bytes,7,rep,name=authorized,proto3" json:"authorized,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Created int64 `protobuf:"varint,8,opt,name=created,proto3" json:"created,omitempty"` + TokenId string `protobuf:"bytes,1,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` + Issuer string `protobuf:"bytes,2,opt,name=issuer,proto3" json:"issuer,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Symbol string `protobuf:"bytes,4,opt,name=symbol,proto3" json:"symbol,omitempty"` + Decimal uint32 `protobuf:"varint,5,opt,name=decimal,proto3" json:"decimal,omitempty"` + Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"` + EvmAddress string `protobuf:"bytes,9,opt,name=evm_address,json=evmAddress,proto3" json:"evm_address,omitempty"` } ``` -### Token Authorization +When create the token, `asset` module auto generate for it a evm address. This address is used as a dynamic precompiles. -A Token authorization struct represents a single addresses current authorization state for a token +### TokenExtensions ```go -type TokenAuthorization struct { - TokenSymbol string `protobuf:"bytes,1,opt,name=tokenSymbol,proto3" json:"tokenSymbol,omitempty"` - Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` - Authorized bool `protobuf:"varint,3,opt,name=authorized,proto3" json:"authorized,omitempty"` +type TokenExtensions struct { + Managers []string `protobuf:"bytes,1,rep,name=managers,proto3" json:"managers,omitempty"` + ExtensionsList []string `protobuf:"bytes,3,rep,name=extensions_list,json=extensionsList,proto3" json:"extensions_list,omitempty"` } ``` +`extensions_list` is the list of actions that the manager can execute. +### WhitelistAddresses -## Genesis State +`WhitelistAddresses` is a list of the address that's allow to create new asset. -The `x/asset` module's `GenesisState` defines the state necessary for initializing the chain from a previous exported height. It contains the module parameters and the registered token pairs : - -```go -// GenesisState defines the module's genesis state. -type GenesisState struct { - Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` - PortId string `protobuf:"bytes,2,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` -} -``` \ No newline at end of file diff --git a/x/asset/spec/03_params.md b/x/asset/spec/03_params.md index 06f1e439..c7a05497 100644 --- a/x/asset/spec/03_params.md +++ b/x/asset/spec/03_params.md @@ -6,8 +6,10 @@ order: 3 The asset module contains the following parameters: -| Key | Type | Example | -|---------------|-----------------|------------------------| -| port | string | "ario" | -| InflationRate | string (dec) | "0.130000000000000000" | -| BlocksPerYear | string (uint64) | "6311520" | +| Key | Type | Example | +|----------------------|---------------|------------------------| +| AllowExtensions | []string | ["burn","freeze"] | + +## Details + +- AllowExtensions: list of extensions that the module provides. They can be update after the chain upgrade to enable new extension add-on to the module. diff --git a/x/asset/spec/04_events.md b/x/asset/spec/04_events.md deleted file mode 100644 index be929e92..00000000 --- a/x/asset/spec/04_events.md +++ /dev/null @@ -1,37 +0,0 @@ - - -# Events - -The `x/asset` module emits the following events: - -## Create new token - -| Type | Attribute Key | Attribute Value | -| --------------- |---------------|-----------------| -| `create_token` | `"amount"` | `{totatl}` | -| `create_token` | `"symbol"` | `{symbol}` | - -## Update token - -| Type | Attribute Key | Attribute Value | -| --------------- |---------------|-----------------| -| `update_token` | `"symbol"` | `{symbol}` | - - -## Authorize address - -| Type | Attribute Key | Attribute Value | -| --------------- |--------------|------------------| -| `authorize_token` | `"symbol"` | `{symbol}` | -| `authorize_token` | `"address"` | `{sdk_address}` | - - -## Un Authorize address - -| Type | Attribute Key | Attribute Value | -| --------------- |---------------|-----------------| -| `unauthorize_token` | `"symbol"` | `{symbol}` | -| `unauthorize_token` | `"address"` | `{sdk_address}` | - diff --git a/x/asset/spec/04_msgs.md b/x/asset/spec/04_msgs.md new file mode 100644 index 00000000..34eb2224 --- /dev/null +++ b/x/asset/spec/04_msgs.md @@ -0,0 +1,230 @@ + + +# Messages + +## 1. MsgIssueToken + +`MsgIssueToken` allow issuer to create token. The issuer must be in param's whitelist addresses to be able to execute this msg. + +```go + type MsgIssueToken struct { + Issuer address + Managers [ ]address + Name string + Symbol string + Decimal uint32 + Description string + ExtensionsList [ ]string + Distributor [ ]string + InitialSupply [ ]math.Int + } +``` + +```go + type MsgIssueTokenResponse struct { + } +``` + +CLI: + +```bash + realio-networkd tx issue-token [token.json] [flags] +``` + +Example token.json: + +```json + { + "Manager": ["realioabc..."], + "Symbol": "riel", + "Decimal": 18, + "Description": "", + "AllowNewExtensions": true, + "ExtensionsList": [], + } +``` + +Validation: + +- Check if Creator is whitelisted. We only allow some certain accounts to create tokens, these accounts is determined via gov proposal. +- Check if token has been created or not by iterating through all denom existing. +- Sanity check on token info like decimal, description + +Flow: + +1. The token-id for the token will be derived from Creator and Symbol with the format of asset/{Issuer}/{Symbol-Lowercase} +2. Create a evm address for the asset. +3. Create a dynamic precompiles linking to the newly created evm address. +4. Save the token basic information (name, symbol, decimal and description) in the x/bank metadata store +5. Save the token management info and distribution info in the x/asset store. + +## 2. AssignManagers + +`MsgAssignManagers` allow issue to set managers for the token. + +```go + type MsgAssignManagers struct { + TokenId string + Issuer address + Addresses []addresses + } +``` + +```go + type MsgAssignManagersResponse struct { + } +``` + +CLI: + +```bash + realio-networkd tx assign-managers [privilege.json] [flags] +``` + +Example privilege.json: + +```json + { + "TokenId": "asset/realio1.../tokena", + "Issuer": "realio1...", + "Assign": [ + "realio2...", + "realio3..." + ] + } +``` + +Validation: + +- Check if token exists +- Check if caller is issuer of the token +- Check if addresses is valid +- Check if manager doesn't exist in the current managers list of token + +Flow: + +- Get `TokenManager` from store by token_id +- Loop through addresses and append manager addresses to `TokenManager.Managers` + +## 3. UnassignManager + +```go + type MsgUnassignRoles struct { + TokenId string + Issuer address + Assigners []address + } +``` + +```go + type MsgUnassignRolesResponse struct { + } +``` + +Validation: + +- Check if token exists +- Check if caller is issuer of the token +- Check if addresses is valid +- Check if addresses is in `TokenExtensions.Managers` + +Flow: + +- Get `TokenManager` from store by token_id +- Loop through addresses and remove manager addresses from `TokenManager.Managers` + +## 4. Burn + +This msg only can be executed when the token's `ExtensionsList` has `burn` extension. + +```go + type MsgBurn struct { + Manager address + TokenId string + BurnFromAddr address + Amount math.Int + } +``` + +Validation: + +- Checks if the token specified in the msg exists. +- Checks if the extension is supported. +- Check if addresses is valid +- Checks if the address is in `TokenManager.Managers` +- Checks if address is freezed in `FreezeAddresses` + +Flow: + +- Get `TokenManager` from store by token_id +- Check if `BurnFromAddr` has enough token to burn +- Burn the asset from `BurnFromAddr` + +### 5. Mint + +This msg only can be executed when the token's `ExtensionsList` has `mint` extension. + +```go + type MsgMint struct { + Manager address + TokenId string + Receiver address + Amount math.Int + } +``` + +Validation: + +- Checks if the token specified in the msg exists. +- Checks if the extension is supported. +- Check if addresses is valid +- Checks if the address is in `TokenManager.Managers` +- Checks if mint amount exceed `MaxSupply`. + +Flow: + +- Get `TokenManager` from store by token_id +- Mint the asset for corresponding receiver +- Increase the supply. + +### 6. Freeze + +This msg only can be executed when the token's `ExtensionsList` has `freeze` extension. + +```go + type MsgFreeze struct { + Manager address + TokenId string + Receiver address + } +``` + +Validation: + +- Checks if the token specified in the msg exists. +- Checks if the extension is supported. +- Check if addresses is valid +- Checks if the address is in `TokenManager.Managers` + +Flow: + +- Get `TokenManager` from store by token_id +- Set address into `FreezeAddresses` +- All account in `FreezeAddresses` can not be transfer token out or burned. + +### 7. Set max supply + +```go + type MsgSetMaxSupply struct { + Manager address + TokenId string + MaxSupply int64 + } +``` + +This message can only executed once, it will set the maximum supply for the token + + + diff --git a/x/asset/spec/05_client.md b/x/asset/spec/05_client.md deleted file mode 100644 index 71de1325..00000000 --- a/x/asset/spec/05_client.md +++ /dev/null @@ -1,26 +0,0 @@ - - -# Client - -## CLI - -A user can query and interact with the `x/asset` module using the CLI. - -### Query - -The `query` commands allow users to query `x/asset` state. - -```sh -realio-networkd query mint --help -``` - -#### params - -The `params` command allow users to query the current module parameters - -```sh -realio-networkd query mint params [flags] -``` - diff --git a/x/asset/spec/05_query.md b/x/asset/spec/05_query.md new file mode 100644 index 00000000..5f7c6522 --- /dev/null +++ b/x/asset/spec/05_query.md @@ -0,0 +1,81 @@ + + +# Queries + +## 1. QueryParams + +The `QueryParams` allows users to query asset module params + +```go + type QueryParamsRequest struct { + } +``` + +```go + type QueryParamsResponse struct { + Params Params + } +``` + +CLI: + +```bash + realio-networkd q params +``` + +## 2. QueryToken + +The `QueryToken` allows users to query a token related information + +```go + type QueryTokenRequest struct { + TokenId string + } +``` + +```go + type QueryTokenResponse struct { + Token Token + TokenManager TokenManager + TokenDistributor TokenDistributor + } +``` + +CLI: + +```bash + realio-networkd q token [token-id] +``` + +## 3. QueryAllTokens + +The `QueryAllTokens` allows users to query all tokens related information + +```go + type QueryAllTokensRequest struct { + } +``` + +```go + type QueryAllTokensResponse struct { + TokenInfo []TokenInfo + + } +``` + +```go + type TokenInfo struct { + Token Token + TokenManager TokenManager + TokenDistributor TokenDistributor + + } +``` + +CLI: + +```bash + realio-networkd q all-tokens +``` diff --git a/x/asset/spec/06_logic.md b/x/asset/spec/06_logic.md new file mode 100644 index 00000000..1411a1c1 --- /dev/null +++ b/x/asset/spec/06_logic.md @@ -0,0 +1,108 @@ + + +# Logic + +This file describes the core logics in this module. + +## Token namespace + +The token id/denom for the token will be derived from the Issuer and the Symbol with the format of asset/{Issuer}/{Symbol-Lowercase}. This allow many different issuers to issue token with the same symbol, differentiate their denom by including in their creator. + +## Token creation + +The token creation process involves the `Issuer` executing `MsgIssueToken` that defines info fields for the tokens. Amount those fields, these are the important feilds that dictacts how the token is operated: + +- List of extensions (imutable): + Choose what exetensions to enable for the token when creating it. The set of extensions is fixed, meaning that all extensions that are not enabled is permanently disabled. +- Manager (mutable): + Assign the manager account which could be an user account or smart contract account. If this field is blank then the manager will set to be the `issuer` of the token. +- Symbol (imutable): + This field will be used to derive the token denom, in the format `asset/{Issuer}/{Symbol-Lowercase}`. +- Initial Supply (imutable): + Upon creation an amount equal to the initial supply will be minted thus create a circulating supply for the token. +- Distributor (imutable): + Think of this account as the treasury manager account whose task is to distribute the token to holders. The initial supply will be minted to this account. + +## Extension + +We'll go into details on how each of the extension works + +### Mint extension +Only manager is allowed to execute `Mint`. Then mint the corresponding amount to the recipient. Note, total supply can not exceed `MaxSupply` + +### Burn extension +Only manager is allowed to execute `Burn`. Then burn the corresponding amount from the address. Note, address that be freezed can not burn. + +### Freeze extension +Only manager is allowed to execute `Freeze`. Its will lock all amount of a asset of that address. An address be freezed with a token can not transfer out or burn. + +### TransferAuth extension +Only manager is allowed to execute `TransferAuth`. Its will update the Token's Issue to new receiver. + +### + +## EVM integration + +### EVM interface + +On token creation, all token will be linked to a erc20-precompiles, which allows it to integrate with the ERC20 standard and have an EVM-compatible contract address. This EVM address acts as an abstract interface layer that bypasses the typical logic within ERC20 or EVM contracts. Instead of executing logic directly in the contract, all actions are reflected to the `asset` module's predefined precompiles, where the token’s core state and extensions are managed. + +The token itself exists as a coin within the bank state, maintaining its own logic and extensions independently of any ERC20 or EVM contract logic. The ERC20 contract deployed on the EVM serves purely as an interface, with its logic effectively bypassed. When other EVM contracts interact with this interface, their requests are forwarded via JSON-RPC calls to the `asset` module, which directly handles and executes the necessary operations. This is achieved by creating a `dynamic precompile`, ensuring that the token’s behavior aligns with its internal state while still providing compatibility with the EVM ecosystem. + +The precompiles actions will depend on the `AllowExtensionList` when creating the token. Therefore different tokens will have precompiles with different addresses and extensions. + +### EVM Precompiles + +EVM precompiles are EVM interface contracts with state access. These smart contracts can directly interact with Cosmos SDK modules, enabling their own operations while also interacting with the EVM state and other SDK modules. + +In `asset` module, there are 2 evm precompiles contracts: `IAsset.sol` corresponding to `asset` precompile and `IERC20Extensions.sol` corresponding to `erc20` precompile. + +The `IAsset.sol` is an interface through which Solidity contracts can interact with Realio asset module. This is convenient for developers as they don’t need to know the implementation details behind the `x/asset` module in the Realio Network. Instead, they can interact with `asset` functions using the Ethereum interface they are familiar with. + +`asset` precompile provides several functions: + +- `issueToken` enables other contracts or users to create an ERC20 token. +- `updateExtensionsList` allow token manager to interact to `asset` module and update the extensions list. +- `assignRoles` allow token issuer to assign role for token. +- `unassignRoles` allow token issuer to unassign role for token. + +The functions is defined as follows: + +```solidity + function issueToken( + address issuerAddress, + string memory name, + string memory symbol, + uint8 deciaml, + bool allowNewExtensions, + string[] memory extensionsList + ) external returns (bool success, address contractAddress); + + function updateExtensionsList(string memory tokenId, string[] memory newExtensionsList) public; + + struct Role { + uint8 role; // 1 represent manager, 2 represent distributor + address account; + } + + function assignRoles(string memory tokenId, Role[] roles) public; + + function unassignRoles(string memory tokenId, address[] accounts) public; +``` + +When this function is called, the token-issuer precompile forwards the request to the asset module, invoking the IssueToken function within the module to handle the token creation process. + +On the other hand, the `erc20` precompile acts as the ERC20 interface for all tokens managed by the asset module. It implements all standard ERC20 functions as defined in `IERC20Extensions.sol`, including `transfer`, `transferFrom`, `approve`, `increaseAllowance`, and `decreaseAllowance`. + +Additionally, the `IERC20Extensions.sol` contract provides extra methods to support interactions with other extensions, enabling more advanced functionality: + +```solidity + function mint(address to, uint256 amount) public; + + function burn(uint256 value) public; + function burnFrom(address account, uint256 value) public; + + function freeze(address account) public; +``` diff --git a/x/asset/spec/README.md b/x/asset/spec/README.md index 23b4d990..a593824a 100644 --- a/x/asset/spec/README.md +++ b/x/asset/spec/README.md @@ -1,33 +1,66 @@ -# `mint` +# `asset` -## Abstract +## The Realio Asset Token Model -The `x/asset` module enables the creation and management of on chain assets in the Realio Network. +The Realio Asset module is centered around a token model where certain whitelisted accounts can issue their own token. A token issued by this module will be defined by the `issuer` and managed by the `manager` role. `manager` role can be assigned to arbitrary accounts (could be either user accounts or module/contract account) by the token issuer. -With this module, you can create assets that represent digitally native and real-world assets such as security tokens and stablecoins. -There is functionality to place transfer restrictions via whitelists on an asset that help support securities, compliance, and certification use cases. +Token extensions are additional features that can be flug-in for each token. There're are four types of extensions `Mint`, `Burn`, `Transfer Auth` and `Freeze`. The `Issuer` can choose what extensions to be included for his token at creation time, and only the `manager` can trigger the extension's logic. +![asset_module](imgs/asset_module.png)` + +## Asset module precompile + +To enhance user experience as well as interoperability with EVM contracts, we decide to build a precompile for the asset module. All the messages of asset modules can now also be triggered with an evm call. Thus, evm contracts can interact with the module and users can call the modules via metamask or any ethereum friendly UI. + +## ERC-20 Precompiles + +ERC-20 precompiles are offered by evmOS for better integration with Cosmos SDK. With ERC-20 precompiles, we now can have bank tokens with the interface of erc-20 contracts and therefore can talk to other EVM contracts. Utilizing this feature enables the evm contracts to interact with the asset tokens via erc20 call, opening lots of defi usecases for the asset module. + +### Link Asset to Precompiles + +Each token of the asset module is automatically linked to ERC20 Precompile, when issuer execute the MsgIssueToken, a new token instance will be created in the asset module and a new evm address is derived based on the token's info, which will be assigned an erc20-precompiles. After that, all calls to the evm address will now redirect to the erc20-precompiles. + +![asset_precompiles](imgs/linking_precompiles.png) + +### Token extensions call via precompiles + +Each ERC20 precompiles come with a limited number of call which are: + +- Transfer +- TransferFrom +- Approve +- IncreaseAllowance +- DecreaseAllowance + +We introduce these optional calls to precompile to execute the token's extensions: + +- Mint +- Burn +- Freeze +- TransferAuth + + +It's important to note that each token has its own set of enabled extensions, the precompile linked to that token must also reflect that. In other words, for each precompile these calls will be enabled/disabled however the linked token's extensions be. ## Contents 1. **[Concept](01_concepts.md)** 2. **[State](02_state.md)** - * [Minter](02_state.md#minter) - * [Params](02_state.md#params) -3. **[Begin-Block](03_begin_block.md)** - * [NextAnnualProvisions](03_begin_block.md#nextannualprovisions) - * [BlockProvision](03_begin_block.md#blockprovision) -4. **[Parameters](04_params.md)** -5. **[Events](05_events.md)** - * [BeginBlocker](05_events.md#beginblocker) -6. **[Client](06_client.md)** - * [CLI](06_client.md#cli) - * [gRPC](06_client.md#grpc) - * [REST](06_client.md#rest) + - [Token](02_state.md#token) + - [TokenManagement](02_state.md#tokenmanagement) + - [TokenDistribution](02_state.md#tokendistribution) + - [WhitelistAddresses](02_state.md#whitelistaddresses) + - [DynamicPrecompiles](02_state.md#dynamicprecompiles) +3. **[Parameters](03_params.md)** +4. **[Messages](04_msgs.md)** +5. **[Query](05_query.md)** +6. **[Logic](06_logic.md)** + - [Extension](06_logic.md#extension) + - [EVM interaction](06_logic.md#evm-interaction) diff --git a/x/asset/spec/imgs/asset_evm.png b/x/asset/spec/imgs/asset_evm.png new file mode 100644 index 00000000..568a90ea Binary files /dev/null and b/x/asset/spec/imgs/asset_evm.png differ diff --git a/x/asset/spec/imgs/asset_module.png b/x/asset/spec/imgs/asset_module.png new file mode 100644 index 00000000..1cefb9ec Binary files /dev/null and b/x/asset/spec/imgs/asset_module.png differ diff --git a/x/asset/spec/imgs/asset_precompiles.png b/x/asset/spec/imgs/asset_precompiles.png new file mode 100644 index 00000000..db9af39b Binary files /dev/null and b/x/asset/spec/imgs/asset_precompiles.png differ diff --git a/x/asset/spec/imgs/linking_precompiles.png b/x/asset/spec/imgs/linking_precompiles.png new file mode 100644 index 00000000..11480b35 Binary files /dev/null and b/x/asset/spec/imgs/linking_precompiles.png differ diff --git a/x/asset/types/codec.go b/x/asset/types/codec.go index c643b5f6..c4c0b559 100644 --- a/x/asset/types/codec.go +++ b/x/asset/types/codec.go @@ -9,10 +9,6 @@ import ( func RegisterCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgCreateToken{}, "asset/CreateToken", nil) - cdc.RegisterConcrete(&MsgUpdateToken{}, "asset/UpdateToken", nil) - cdc.RegisterConcrete(&MsgAuthorizeAddress{}, "asset/AuthorizeAddress", nil) - cdc.RegisterConcrete(&MsgUnAuthorizeAddress{}, "asset/UnAuthorizeAddress", nil) - cdc.RegisterConcrete(&MsgTransferToken{}, "asset/TransferToken", nil) // this line is used by starport scaffolding # 2 } @@ -20,18 +16,6 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { registry.RegisterImplementations((*sdk.Msg)(nil), &MsgCreateToken{}, ) - registry.RegisterImplementations((*sdk.Msg)(nil), - &MsgUpdateToken{}, - ) - registry.RegisterImplementations((*sdk.Msg)(nil), - &MsgAuthorizeAddress{}, - ) - registry.RegisterImplementations((*sdk.Msg)(nil), - &MsgUnAuthorizeAddress{}, - ) - registry.RegisterImplementations((*sdk.Msg)(nil), - &MsgTransferToken{}, - ) // this line is used by starport scaffolding # 3 msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) @@ -40,4 +24,4 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { var ( Amino = codec.NewLegacyAmino() ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry()) -) +) \ No newline at end of file diff --git a/x/asset/types/errors.go b/x/asset/types/errors.go index ed8cc25c..2f84afe5 100644 --- a/x/asset/types/errors.go +++ b/x/asset/types/errors.go @@ -8,9 +8,12 @@ import ( // x/asset module sentinel errors var ( - ErrSample = errorsmod.Register(ModuleName, 1100, "sample error") - ErrInvalidPacketTimeout = errorsmod.Register(ModuleName, 1500, "invalid packet timeout") - ErrInvalidVersion = errorsmod.Register(ModuleName, 1501, "invalid version") - ErrNotAuthorized = errorsmod.Register(ModuleName, 1502, "transaction not authorized") - ErrSetTokenUnable = errorsmod.Register(ModuleName, 1503, "token is unable to be set") + ErrUnauthorize = errorsmod.Register(ModuleName, 1501, "unauthorized address") + ErrTokenSet = errorsmod.Register(ModuleName, 1502, "token is unable to be set") + ErrTokenManagementSet = errorsmod.Register(ModuleName, 1503, "token management is unable to be set") + ErrTokenDistributionSet = errorsmod.Register(ModuleName, 1504, "token distribution is unable to be set") + ErrTokenGet = errorsmod.Register(ModuleName, 1505, "token is unable to be get") + ErrTokenManagementGet = errorsmod.Register(ModuleName, 1506, "token management is unable to be get") + ErrTokenDistributionGet = errorsmod.Register(ModuleName, 1507, "token distribution is unable to be get") + ErrAccAddress = errorsmod.Register(ModuleName, 1508, "unable to convert string to acc address") ) diff --git a/x/asset/types/events.go b/x/asset/types/events.go index a61b5afa..9a8cb6ea 100644 --- a/x/asset/types/events.go +++ b/x/asset/types/events.go @@ -2,12 +2,10 @@ package types // staking module event types const ( - EventTypeTokenCreated = "create_token" - EventTypeTokenUpdated = "update_token" - EventTypeTokenAuthorized = "authorize_token" - EventTypeTokenUnAuthorized = "unauthorize_token" + EventTypeTokenCreated = "create_token" + EventTypeTokenAuthorizeUpdated = "update_authorize_token" - AttributeKeySymbol = "symbol" + AttributeKeyTokenId = "token_id" AttributeKeyIndex = "index" AttributeKeyAddress = "address" diff --git a/x/asset/types/expected_keepers.go b/x/asset/types/expected_keepers.go index bfd59e4a..09fbc687 100644 --- a/x/asset/types/expected_keepers.go +++ b/x/asset/types/expected_keepers.go @@ -3,15 +3,18 @@ package types import ( "context" + "cosmossdk.io/core/address" sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) // AccountKeeper defines the expected account keeper type AccountKeeper interface { + AddressCodec() address.Codec GetAccount(ctx context.Context, addr sdk.AccAddress) sdk.AccountI GetModuleAddress(moduleName string) sdk.AccAddress // Methods imported from account should be defined here + GetModuleAccount(ctx context.Context, moduleName string) sdk.ModuleAccountI } // BankKeeper defines the expected interface needed to retrieve account balances. diff --git a/x/asset/types/genesis.go b/x/asset/types/genesis.go index a6ac6f97..4492f761 100644 --- a/x/asset/types/genesis.go +++ b/x/asset/types/genesis.go @@ -18,4 +18,4 @@ func (gs GenesisState) Validate() error { // this line is used by starport scaffolding # genesis/types/validate return gs.Params.Validate() -} +} \ No newline at end of file diff --git a/x/asset/types/genesis_test.go b/x/asset/types/genesis_test.go deleted file mode 100644 index 31e32dae..00000000 --- a/x/asset/types/genesis_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package types_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/realiotech/realio-network/x/asset/types" -) - -func TestGenesisState_Validate(t *testing.T) { - for _, tc := range []struct { - desc string - genState *types.GenesisState - valid bool - }{ - { - desc: "default is valid", - genState: types.DefaultGenesis(), - valid: true, - }, - { - desc: "valid genesis state", - genState: &types.GenesisState{ - Tokens: []types.Token{}, - // this line is used by starport scaffolding # types/genesis/validField - }, - valid: true, - }, - // this line is used by starport scaffolding # types/genesis/testcase - } { - t.Run(tc.desc, func(t *testing.T) { - err := tc.genState.Validate() - if tc.valid { - require.NoError(t, err) - } else { - require.Error(t, err) - } - }) - } -} diff --git a/x/asset/types/keys.go b/x/asset/types/keys.go index 7d38fe70..5f32e6f3 100644 --- a/x/asset/types/keys.go +++ b/x/asset/types/keys.go @@ -1,14 +1,16 @@ package types import ( - "strings" - "cosmossdk.io/collections" ) var ( - ParamsKey = collections.NewPrefix(0) - TokenKeyPrefix = collections.NewPrefix("Token/value/") + ParamsKey = collections.NewPrefix(0) + TokenKey = collections.NewPrefix(1) + TokenManagementKey = collections.NewPrefix(2) + TokenDistributionKey = collections.NewPrefix(3) + WhitelistAddressesKey = collections.NewPrefix(4) + FreezeAddressesKey = collections.NewPrefix(5) ) const ( @@ -34,10 +36,3 @@ var PortKey = KeyPrefix("asset-port-") func KeyPrefix(p string) []byte { return []byte(p) } - -// TokenKey returns the store key to retrieve a Token from the index fields -func TokenKey( - index string, -) string { - return strings.ToLower(index) + "/" -} diff --git a/x/asset/types/message_authorize_address.go b/x/asset/types/message_authorize_address.go deleted file mode 100644 index ead47aea..00000000 --- a/x/asset/types/message_authorize_address.go +++ /dev/null @@ -1,27 +0,0 @@ -package types - -import ( - errorsmod "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -const TypeMsgAuthorizeAddress = "authorize_address" - -var _ sdk.Msg = &MsgAuthorizeAddress{} - -func NewMsgAuthorizeAddress(manager string, symbol string, address string) *MsgAuthorizeAddress { - return &MsgAuthorizeAddress{ - Manager: manager, - Symbol: symbol, - Address: address, - } -} - -func (msg *MsgAuthorizeAddress) ValidateBasic() error { - _, err := sdk.AccAddressFromBech32(msg.Manager) - if err != nil { - return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid manager address (%s)", err) - } - return nil -} diff --git a/x/asset/types/message_create_token.go b/x/asset/types/message_create_token.go deleted file mode 100644 index 0b080bde..00000000 --- a/x/asset/types/message_create_token.go +++ /dev/null @@ -1,29 +0,0 @@ -package types - -import ( - errorsmod "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -const TypeMsgCreateToken = "create_token" - -var _ sdk.Msg = &MsgCreateToken{} - -func NewMsgCreateToken(manager string, name string, symbol string, total string, authorizationRequired bool) *MsgCreateToken { - return &MsgCreateToken{ - Manager: manager, - Name: name, - Symbol: symbol, - Total: total, - AuthorizationRequired: authorizationRequired, - } -} - -func (msg *MsgCreateToken) ValidateBasic() error { - _, err := sdk.AccAddressFromBech32(msg.Manager) - if err != nil { - return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid manager address (%s)", err) - } - return nil -} diff --git a/x/asset/types/message_test.go b/x/asset/types/message_test.go deleted file mode 100644 index 37e991d1..00000000 --- a/x/asset/types/message_test.go +++ /dev/null @@ -1,164 +0,0 @@ -package types - -import ( - "testing" - - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/stretchr/testify/suite" - - "github.com/realiotech/realio-network/testutil" -) - -type MessageTestSuite struct { - suite.Suite -} - -func TestMessageAuthorizeTestSuite(t *testing.T) { - suite.Run(t, new(MessageTestSuite)) -} - -func (suite *MessageTestSuite) TestMsgAuthorizeAddress_ValidateBasic() { - tests := []struct { - name string - msg MsgAuthorizeAddress - err error - }{ - { - name: "invalid address", - msg: MsgAuthorizeAddress{ - Manager: "invalid_address", - }, - err: sdkerrors.ErrInvalidAddress, - }, { - name: "valid address", - msg: MsgAuthorizeAddress{ - Manager: testutil.GenAddress().String(), - }, - }, - } - for _, tt := range tests { - err := tt.msg.ValidateBasic() - if tt.err != nil { - suite.Require().ErrorIs(err, tt.err) - return - } - suite.Require().NoError(err) - } -} - -func (suite *MessageTestSuite) TestMsgCreateToken_ValidateBasic() { - tests := []struct { - name string - msg MsgCreateToken - err error - }{ - { - name: "invalid address", - msg: MsgCreateToken{ - Manager: "invalid_address", - }, - err: sdkerrors.ErrInvalidAddress, - }, { - name: "valid address", - msg: MsgCreateToken{ - Manager: testutil.GenAddress().String(), - }, - }, - } - for _, tt := range tests { - err := tt.msg.ValidateBasic() - if tt.err != nil { - suite.Require().ErrorIs(err, tt.err) - return - } - suite.Require().NoError(err) - } -} - -func (suite *MessageTestSuite) TestMsgTransferToken_ValidateBasic() { - tests := []struct { - name string - msg MsgTransferToken - err error - }{ - { - name: "invalid address", - msg: MsgTransferToken{ - From: "invalid_address", - }, - err: sdkerrors.ErrInvalidAddress, - }, { - name: "valid address", - msg: MsgTransferToken{ - To: testutil.GenAddress().String(), - From: testutil.GenAddress().String(), - }, - }, - } - for _, tt := range tests { - err := tt.msg.ValidateBasic() - if tt.err != nil { - suite.Require().ErrorIs(err, tt.err) - return - } - suite.Require().NoError(err) - } -} - -func (suite *MessageTestSuite) TestMsgUnAuthorizeAddress_ValidateBasic() { - tests := []struct { - name string - msg MsgUnAuthorizeAddress - err error - }{ - { - name: "invalid address", - msg: MsgUnAuthorizeAddress{ - Manager: "invalid_address", - }, - err: sdkerrors.ErrInvalidAddress, - }, { - name: "valid address", - msg: MsgUnAuthorizeAddress{ - Manager: testutil.GenAddress().String(), - }, - }, - } - for _, tt := range tests { - err := tt.msg.ValidateBasic() - if tt.err != nil { - suite.Require().ErrorIs(err, tt.err) - return - } - suite.Require().NoError(err) - } -} - -func (suite *MessageTestSuite) TestMsgUpdateToken_ValidateBasic() { - tests := []struct { - name string - msg MsgUpdateToken - err error - }{ - { - name: "invalid address", - msg: MsgUpdateToken{ - Manager: "invalid_address", - }, - err: sdkerrors.ErrInvalidAddress, - }, { - name: "valid address", - msg: MsgUpdateToken{ - Manager: testutil.GenAddress().String(), - }, - }, - } - for _, tt := range tests { - err := tt.msg.ValidateBasic() - if tt.err != nil { - suite.Require().ErrorIs(err, tt.err) - return - } - suite.Require().NoError(err) - } -} diff --git a/x/asset/types/message_transfer_token.go b/x/asset/types/message_transfer_token.go deleted file mode 100644 index 000ef3ef..00000000 --- a/x/asset/types/message_transfer_token.go +++ /dev/null @@ -1,31 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -const TypeMsgTransferToken = "transfer_token" - -var _ sdk.Msg = &MsgTransferToken{} - -func NewMsgTransferToken(symbol string, from string, to string, amount string) *MsgTransferToken { - return &MsgTransferToken{ - Symbol: symbol, - From: from, - To: to, - Amount: amount, - } -} - -func (msg *MsgTransferToken) ValidateBasic() error { - if _, err := sdk.AccAddressFromBech32(msg.From); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid from address: %s", err) - } - - if _, err := sdk.AccAddressFromBech32(msg.To); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid to address: %s", err) - } - - return nil -} diff --git a/x/asset/types/message_un_authorize_address.go b/x/asset/types/message_un_authorize_address.go deleted file mode 100644 index c62cd23c..00000000 --- a/x/asset/types/message_un_authorize_address.go +++ /dev/null @@ -1,29 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -const TypeMsgUnAuthorizeAddress = "un_authorize_address" - -var _ sdk.Msg = &MsgUnAuthorizeAddress{} - -func NewMsgUnAuthorizeAddress(manager string, symbol string, address string) *MsgUnAuthorizeAddress { - return &MsgUnAuthorizeAddress{ - Manager: manager, - Symbol: symbol, - Address: address, - } -} - -func (msg *MsgUnAuthorizeAddress) ValidateBasic() error { - if _, err := sdk.AccAddressFromBech32(msg.Manager); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid manager address: %s", err) - } - if _, err := sdk.AccAddressFromBech32(msg.Address); err != nil { - return sdkerrors.ErrInvalidAddress.Wrapf("invalid address: %s", err) - } - - return nil -} diff --git a/x/asset/types/message_update_token.go b/x/asset/types/message_update_token.go deleted file mode 100644 index bc47e09d..00000000 --- a/x/asset/types/message_update_token.go +++ /dev/null @@ -1,27 +0,0 @@ -package types - -import ( - errorsmod "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -const TypeMsgUpdateToken = "update_token" - -var _ sdk.Msg = &MsgUpdateToken{} - -func NewMsgUpdateToken(manager string, symbol string, authorizationRequired bool) *MsgUpdateToken { - return &MsgUpdateToken{ - Manager: manager, - Symbol: symbol, - AuthorizationRequired: authorizationRequired, - } -} - -func (msg *MsgUpdateToken) ValidateBasic() error { - _, err := sdk.AccAddressFromBech32(msg.Manager) - if err != nil { - return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid manager address (%s)", err) - } - return nil -} diff --git a/x/asset/types/msgs.go b/x/asset/types/msgs.go new file mode 100644 index 00000000..02c9551a --- /dev/null +++ b/x/asset/types/msgs.go @@ -0,0 +1,36 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var _ sdk.Msg = &MsgCreateToken{} + +func NewMsgCreateToken(issuer []byte, name string, symbol string, description string, decimal uint32, managers [][]byte, extensionsList []string, allowNewExtensions bool) *MsgCreateToken { + return &MsgCreateToken{ + Issuer: issuer, + Name: name, + Symbol: symbol, + Description: description, + Decimal: decimal, + Managers: managers, + ExtensionsList: extensionsList, + AllowNewExtensions: allowNewExtensions, + } +} + +func NewMsgAssignRoles(issuer []byte, tokenId string, managers []byte) *MsgAssignRoles { + return &MsgAssignRoles{ + Issuer: issuer, + TokenId: tokenId, + Managers: managers, + } +} + +func NewMsgUnassignRoles(issuer []byte, tokenId string, managers []byte) *MsgUnassignRoles { + return &MsgUnassignRoles{ + Issuer: issuer, + TokenId: tokenId, + Managers: managers, + } +} diff --git a/x/asset/types/params.go b/x/asset/types/params.go index 5989f733..4831c3a0 100644 --- a/x/asset/types/params.go +++ b/x/asset/types/params.go @@ -1,10 +1,16 @@ package types -import "gopkg.in/yaml.v2" +import ( + "fmt" + + "gopkg.in/yaml.v2" +) // NewParams creates a new Params instance func NewParams() Params { - return Params{} + return Params{ + AllowExtensions: []string{"transfer", "mint"}, + } } // DefaultParams returns a default set of parameters @@ -14,6 +20,11 @@ func DefaultParams() Params { // Validate validates the set of params func (p Params) Validate() error { + for _, extension := range p.AllowExtensions { + if extension == "" { + return fmt.Errorf("Extension can not be empty") + } + } return nil } diff --git a/x/asset/types/params.pb.go b/x/asset/types/params.pb.go index 1cdcd055..b96710a5 100644 --- a/x/asset/types/params.pb.go +++ b/x/asset/types/params.pb.go @@ -25,6 +25,7 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // Params defines the parameters for the module. type Params struct { + AllowExtensions []string `protobuf:"bytes,1,rep,name=allow_extensions,json=allowExtensions,proto3" json:"allow_extensions,omitempty"` } func (m *Params) Reset() { *m = Params{} } @@ -59,6 +60,13 @@ func (m *Params) XXX_DiscardUnknown() { var xxx_messageInfo_Params proto.InternalMessageInfo +func (m *Params) GetAllowExtensions() []string { + if m != nil { + return m.AllowExtensions + } + return nil +} + func init() { proto.RegisterType((*Params)(nil), "realionetwork.asset.v1.Params") } @@ -68,18 +76,20 @@ func init() { } var fileDescriptor_d68d5b1218748d2a = []byte{ - // 162 bytes of a gzipped FileDescriptorProto + // 195 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2e, 0x4a, 0x4d, 0xcc, 0xc9, 0xcc, 0xcf, 0x4b, 0x2d, 0x29, 0xcf, 0x2f, 0xca, 0xd6, 0x4f, 0x2c, 0x2e, 0x4e, 0x2d, 0xd1, 0x2f, 0x33, 0xd4, 0x2f, 0x48, 0x2c, 0x4a, 0xcc, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x43, 0x51, 0xa4, 0x07, 0x56, 0xa4, 0x57, 0x66, 0x28, 0x25, 0x92, 0x9e, 0x9f, 0x9e, 0x0f, - 0x56, 0xa2, 0x0f, 0x62, 0x41, 0x54, 0x2b, 0xf1, 0x71, 0xb1, 0x05, 0x80, 0x75, 0x5b, 0xb1, 0xcc, - 0x58, 0x20, 0xcf, 0xe0, 0xe4, 0x73, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, - 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, - 0x46, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0x10, 0x2b, 0x4a, 0x52, - 0x93, 0x33, 0xa0, 0x4c, 0x5d, 0x98, 0x9b, 0x2a, 0xa0, 0xae, 0x2a, 0xa9, 0x2c, 0x48, 0x2d, 0x4e, - 0x62, 0x03, 0x5b, 0x62, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x30, 0xc3, 0xf2, 0x1b, 0xb9, 0x00, - 0x00, 0x00, + 0x56, 0xa2, 0x0f, 0x62, 0x41, 0x54, 0x2b, 0x59, 0x72, 0xb1, 0x05, 0x80, 0x75, 0x0b, 0x69, 0x72, + 0x09, 0x24, 0xe6, 0xe4, 0xe4, 0x97, 0xc7, 0xa7, 0x56, 0x94, 0xa4, 0xe6, 0x15, 0x67, 0xe6, 0xe7, + 0x15, 0x4b, 0x30, 0x2a, 0x30, 0x6b, 0x70, 0x06, 0xf1, 0x83, 0xc5, 0x5d, 0xe1, 0xc2, 0x56, 0x2c, + 0x33, 0x16, 0xc8, 0x33, 0x38, 0xf9, 0x9c, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, + 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43, + 0x94, 0x51, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x3e, 0xc4, 0x35, 0x25, + 0xa9, 0xc9, 0x19, 0x50, 0xa6, 0x2e, 0xcc, 0xf9, 0x15, 0x50, 0x0f, 0x94, 0x54, 0x16, 0xa4, 0x16, + 0x27, 0xb1, 0x81, 0xdd, 0x63, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x00, 0x1d, 0x28, 0xcd, 0xe4, + 0x00, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -102,6 +112,15 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.AllowExtensions) > 0 { + for iNdEx := len(m.AllowExtensions) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.AllowExtensions[iNdEx]) + copy(dAtA[i:], m.AllowExtensions[iNdEx]) + i = encodeVarintParams(dAtA, i, uint64(len(m.AllowExtensions[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } return len(dAtA) - i, nil } @@ -122,6 +141,12 @@ func (m *Params) Size() (n int) { } var l int _ = l + if len(m.AllowExtensions) > 0 { + for _, s := range m.AllowExtensions { + l = len(s) + n += 1 + l + sovParams(uint64(l)) + } + } return n } @@ -160,6 +185,38 @@ func (m *Params) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowExtensions", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AllowExtensions = append(m.AllowExtensions, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipParams(dAtA[iNdEx:]) diff --git a/x/asset/types/params_legacy.go b/x/asset/types/params_legacy.go index 9d9c8e1a..e20dde92 100644 --- a/x/asset/types/params_legacy.go +++ b/x/asset/types/params_legacy.go @@ -14,4 +14,4 @@ func ParamKeyTable() paramtypes.KeyTable { // ParamSetPairs get the params.ParamSet func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { return paramtypes.ParamSetPairs{} -} +} \ No newline at end of file diff --git a/x/asset/types/query.go b/x/asset/types/query.go deleted file mode 100644 index 0946bcd7..00000000 --- a/x/asset/types/query.go +++ /dev/null @@ -1,6 +0,0 @@ -package types - -// NewQueryTokenRequest creates a new instance of QueryTokenRequest. -func NewQueryTokenRequest(symbol string) *QueryTokenRequest { - return &QueryTokenRequest{Symbol: symbol} -} diff --git a/x/asset/types/query.pb.go b/x/asset/types/query.pb.go index d811a746..024cf8ff 100644 --- a/x/asset/types/query.pb.go +++ b/x/asset/types/query.pb.go @@ -287,106 +287,6 @@ func (m *QueryTokenResponse) GetToken() Token { return Token{} } -// QueryParamsRequest is request type for the Query/Params RPC method. -type QueryIsAuthorizedRequest struct { - // symbol is the token symbol to query for. - Symbol string `protobuf:"bytes,1,opt,name=symbol,proto3" json:"symbol,omitempty"` - Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` -} - -func (m *QueryIsAuthorizedRequest) Reset() { *m = QueryIsAuthorizedRequest{} } -func (m *QueryIsAuthorizedRequest) String() string { return proto.CompactTextString(m) } -func (*QueryIsAuthorizedRequest) ProtoMessage() {} -func (*QueryIsAuthorizedRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_b6e3fc89e45a1671, []int{6} -} -func (m *QueryIsAuthorizedRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *QueryIsAuthorizedRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_QueryIsAuthorizedRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *QueryIsAuthorizedRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryIsAuthorizedRequest.Merge(m, src) -} -func (m *QueryIsAuthorizedRequest) XXX_Size() int { - return m.Size() -} -func (m *QueryIsAuthorizedRequest) XXX_DiscardUnknown() { - xxx_messageInfo_QueryIsAuthorizedRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_QueryIsAuthorizedRequest proto.InternalMessageInfo - -func (m *QueryIsAuthorizedRequest) GetSymbol() string { - if m != nil { - return m.Symbol - } - return "" -} - -func (m *QueryIsAuthorizedRequest) GetAddress() string { - if m != nil { - return m.Address - } - return "" -} - -// QueryParamsResponse is response type for the Query/Params RPC method. -type QueryIsAuthorizedResponse struct { - // params holds all the parameters of this module. - IsAuthorized bool `protobuf:"varint,1,opt,name=isAuthorized,proto3" json:"isAuthorized,omitempty"` -} - -func (m *QueryIsAuthorizedResponse) Reset() { *m = QueryIsAuthorizedResponse{} } -func (m *QueryIsAuthorizedResponse) String() string { return proto.CompactTextString(m) } -func (*QueryIsAuthorizedResponse) ProtoMessage() {} -func (*QueryIsAuthorizedResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_b6e3fc89e45a1671, []int{7} -} -func (m *QueryIsAuthorizedResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *QueryIsAuthorizedResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_QueryIsAuthorizedResponse.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *QueryIsAuthorizedResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryIsAuthorizedResponse.Merge(m, src) -} -func (m *QueryIsAuthorizedResponse) XXX_Size() int { - return m.Size() -} -func (m *QueryIsAuthorizedResponse) XXX_DiscardUnknown() { - xxx_messageInfo_QueryIsAuthorizedResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_QueryIsAuthorizedResponse proto.InternalMessageInfo - -func (m *QueryIsAuthorizedResponse) GetIsAuthorized() bool { - if m != nil { - return m.IsAuthorized - } - return false -} - func init() { proto.RegisterType((*QueryParamsRequest)(nil), "realionetwork.asset.v1.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "realionetwork.asset.v1.QueryParamsResponse") @@ -394,8 +294,6 @@ func init() { proto.RegisterType((*QueryTokensResponse)(nil), "realionetwork.asset.v1.QueryTokensResponse") proto.RegisterType((*QueryTokenRequest)(nil), "realionetwork.asset.v1.QueryTokenRequest") proto.RegisterType((*QueryTokenResponse)(nil), "realionetwork.asset.v1.QueryTokenResponse") - proto.RegisterType((*QueryIsAuthorizedRequest)(nil), "realionetwork.asset.v1.QueryIsAuthorizedRequest") - proto.RegisterType((*QueryIsAuthorizedResponse)(nil), "realionetwork.asset.v1.QueryIsAuthorizedResponse") } func init() { @@ -403,40 +301,34 @@ func init() { } var fileDescriptor_b6e3fc89e45a1671 = []byte{ - // 513 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x94, 0xcf, 0x6f, 0xd3, 0x30, - 0x14, 0xc7, 0x9b, 0xb1, 0x06, 0xf0, 0x76, 0xc1, 0x4c, 0x53, 0xa9, 0xc0, 0x4c, 0x46, 0x82, 0xfd, - 0x10, 0x31, 0x2d, 0x07, 0x34, 0x81, 0x34, 0xb1, 0x1b, 0xd2, 0x24, 0x20, 0x70, 0xe2, 0xe6, 0xae, - 0x56, 0x1a, 0xad, 0x8d, 0xb3, 0xd8, 0x1d, 0x94, 0x69, 0x17, 0x6e, 0x9c, 0x40, 0xe2, 0x5f, 0xe1, - 0x8f, 0xd8, 0x71, 0x12, 0x17, 0x4e, 0x08, 0xb5, 0x1c, 0xf8, 0x33, 0x50, 0x9e, 0x9d, 0xd0, 0x88, - 0xa6, 0x29, 0xb7, 0xe4, 0xe5, 0x7d, 0xdf, 0xe7, 0x63, 0xf9, 0x29, 0x88, 0x26, 0x82, 0xf7, 0x43, - 0x19, 0x09, 0xfd, 0x56, 0x26, 0x47, 0x8c, 0x2b, 0x25, 0x34, 0x3b, 0x69, 0xb1, 0xe3, 0xa1, 0x48, - 0x46, 0x5e, 0x9c, 0x48, 0x2d, 0xf1, 0x7a, 0xa1, 0xc7, 0x83, 0x1e, 0xef, 0xa4, 0xd5, 0x5c, 0x0b, - 0x64, 0x20, 0xa1, 0x85, 0xa5, 0x4f, 0xa6, 0xbb, 0x79, 0x33, 0x90, 0x32, 0xe8, 0x0b, 0xc6, 0xe3, - 0x90, 0xf1, 0x28, 0x92, 0x9a, 0xeb, 0x50, 0x46, 0xca, 0x7e, 0xbd, 0x53, 0xc2, 0x8b, 0x79, 0xc2, - 0x07, 0x59, 0x53, 0x99, 0x94, 0x96, 0x47, 0x22, 0x32, 0x3d, 0x74, 0x0d, 0xe1, 0x97, 0xa9, 0xe3, - 0x0b, 0x08, 0xfa, 0xe2, 0x78, 0x28, 0x94, 0xa6, 0xaf, 0xd0, 0xf5, 0x42, 0x55, 0xc5, 0x32, 0x52, - 0x02, 0x3f, 0x41, 0xae, 0x01, 0x34, 0x9c, 0x0d, 0x67, 0x73, 0xa5, 0x4d, 0xbc, 0xd9, 0x47, 0xf2, - 0x4c, 0x6e, 0x7f, 0xf9, 0xfc, 0xc7, 0xed, 0x9a, 0x6f, 0x33, 0x39, 0xea, 0x75, 0x8a, 0xcf, 0x51, - 0xbe, 0x45, 0x65, 0x55, 0x8b, 0x7a, 0x8c, 0x5c, 0xd0, 0x4c, 0x51, 0x97, 0x36, 0x57, 0xda, 0xb7, - 0xca, 0x50, 0x90, 0xcb, 0x48, 0x26, 0x42, 0x77, 0xd0, 0xb5, 0xbf, 0x33, 0x2d, 0x08, 0xaf, 0x23, - 0x57, 0x8d, 0x06, 0x1d, 0xd9, 0x07, 0xf9, 0xab, 0xbe, 0x7d, 0xa3, 0xcf, 0xa7, 0xb5, 0x72, 0xfe, - 0x2e, 0xaa, 0xc3, 0x30, 0x7b, 0xd2, 0x85, 0xf0, 0x26, 0x41, 0x0f, 0x50, 0x03, 0x06, 0x3e, 0x53, - 0x4f, 0x87, 0xba, 0x27, 0x93, 0xf0, 0xbd, 0xe8, 0x56, 0x48, 0xe0, 0x06, 0xba, 0xcc, 0xbb, 0xdd, - 0x44, 0x28, 0xd5, 0x58, 0x82, 0x0f, 0xd9, 0x2b, 0xdd, 0x43, 0x37, 0x66, 0x4c, 0xb3, 0x96, 0x14, - 0xad, 0x86, 0x53, 0x75, 0x18, 0x7a, 0xc5, 0x2f, 0xd4, 0xda, 0xbf, 0x97, 0x51, 0x1d, 0x26, 0xe0, - 0x8f, 0x0e, 0x72, 0xcd, 0xcd, 0xe0, 0xed, 0xb2, 0xf3, 0xfc, 0xbb, 0x0c, 0xcd, 0x9d, 0x85, 0x7a, - 0x8d, 0x11, 0xbd, 0xfb, 0xe1, 0xdb, 0xaf, 0x2f, 0x4b, 0x1b, 0x98, 0xb0, 0xb9, 0x1b, 0x0a, 0x2e, - 0xe6, 0xca, 0x2b, 0x5c, 0x0a, 0xdb, 0x52, 0xe1, 0x52, 0xdc, 0xa1, 0x6a, 0x17, 0xb3, 0x2e, 0xf8, - 0x93, 0x83, 0xea, 0x10, 0xc5, 0x5b, 0xd5, 0xe3, 0x33, 0x93, 0xed, 0x45, 0x5a, 0xad, 0x08, 0x03, - 0x91, 0x2d, 0x7c, 0x6f, 0xbe, 0x08, 0x3b, 0x35, 0xdb, 0x70, 0x86, 0xbf, 0x3a, 0x68, 0x75, 0xfa, - 0xc2, 0xf1, 0x83, 0xb9, 0xb4, 0x19, 0x9b, 0xd6, 0x6c, 0xfd, 0x47, 0xc2, 0x6a, 0xee, 0x81, 0xe6, - 0x2e, 0x7e, 0x54, 0xa6, 0x19, 0x2a, 0x9e, 0xa7, 0x72, 0x59, 0x76, 0x6a, 0x57, 0xf5, 0x6c, 0xff, - 0xe0, 0x7c, 0x4c, 0x9c, 0x8b, 0x31, 0x71, 0x7e, 0x8e, 0x89, 0xf3, 0x79, 0x42, 0x6a, 0x17, 0x13, - 0x52, 0xfb, 0x3e, 0x21, 0xb5, 0x37, 0xed, 0x20, 0xd4, 0xbd, 0x61, 0xc7, 0x3b, 0x94, 0x03, 0x3b, - 0x5c, 0x8b, 0xc3, 0x9e, 0x7d, 0xbc, 0x9f, 0x81, 0xde, 0x59, 0x94, 0x1e, 0xc5, 0x42, 0x75, 0x5c, - 0xf8, 0x43, 0x3d, 0xfc, 0x13, 0x00, 0x00, 0xff, 0xff, 0xe8, 0x45, 0x42, 0x94, 0x5c, 0x05, 0x00, - 0x00, + // 421 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0xcf, 0x6e, 0xda, 0x30, + 0x1c, 0xc7, 0x93, 0x31, 0x22, 0xcd, 0x9c, 0xe6, 0x21, 0x34, 0xa1, 0xcd, 0x43, 0x99, 0xb4, 0xf1, + 0x47, 0x8b, 0x45, 0x76, 0x9a, 0xd6, 0x13, 0xe7, 0x4a, 0x6d, 0xd3, 0x9e, 0x7a, 0x0b, 0xc8, 0x0a, + 0x11, 0x10, 0x87, 0xd8, 0xd0, 0xa2, 0xaa, 0x97, 0xde, 0x7a, 0x6a, 0xa5, 0x3e, 0x49, 0xdf, 0x82, + 0x23, 0x52, 0x2f, 0x3d, 0x55, 0x15, 0xf4, 0x41, 0xaa, 0xd8, 0x0e, 0x6d, 0xd4, 0x86, 0x70, 0x33, + 0xe6, 0xfb, 0xe7, 0x63, 0xff, 0x1c, 0x60, 0x46, 0xc4, 0x1d, 0xfa, 0x34, 0x20, 0xfc, 0x84, 0x46, + 0x03, 0xec, 0x32, 0x46, 0x38, 0x9e, 0xb6, 0xf1, 0x78, 0x42, 0xa2, 0x99, 0x15, 0x46, 0x94, 0x53, + 0x58, 0x49, 0x69, 0x2c, 0xa1, 0xb1, 0xa6, 0xed, 0x6a, 0xd9, 0xa3, 0x1e, 0x15, 0x12, 0x1c, 0xaf, + 0xa4, 0xba, 0xfa, 0xcd, 0xa3, 0xd4, 0x1b, 0x12, 0xec, 0x86, 0x3e, 0x76, 0x83, 0x80, 0x72, 0x97, + 0xfb, 0x34, 0x60, 0xea, 0xdf, 0x9f, 0x19, 0x7d, 0xa1, 0x1b, 0xb9, 0xa3, 0x44, 0x94, 0x05, 0xc5, + 0xe9, 0x80, 0x04, 0x52, 0x63, 0x96, 0x01, 0x3c, 0x88, 0x19, 0xf7, 0x85, 0xd1, 0x21, 0xe3, 0x09, + 0x61, 0xdc, 0x3c, 0x04, 0x5f, 0x52, 0xbb, 0x2c, 0xa4, 0x01, 0x23, 0x70, 0x07, 0x18, 0xb2, 0xe0, + 0xab, 0x5e, 0xd3, 0xeb, 0x25, 0x1b, 0x59, 0xef, 0x1f, 0xc9, 0x92, 0xbe, 0xce, 0xc7, 0xf9, 0xc3, + 0x0f, 0xcd, 0x51, 0x9e, 0x75, 0xd5, 0x51, 0x5c, 0xbf, 0xae, 0x72, 0x54, 0x55, 0xb2, 0xab, 0xaa, + 0xfe, 0x03, 0x43, 0x60, 0xc6, 0x55, 0x85, 0x7a, 0xc9, 0xfe, 0x9e, 0x55, 0x25, 0x7c, 0x49, 0x93, + 0xb4, 0x98, 0x2d, 0xf0, 0xf9, 0x25, 0x53, 0x15, 0xc1, 0x0a, 0x30, 0xd8, 0x6c, 0xd4, 0xa5, 0x43, + 0x01, 0xff, 0xc9, 0x51, 0xbf, 0xcc, 0xbd, 0xd7, 0x58, 0xeb, 0xfe, 0x7f, 0xa0, 0x28, 0xc2, 0xd4, + 0x49, 0xb7, 0xaa, 0x97, 0x0e, 0xfb, 0xb6, 0x00, 0x8a, 0x22, 0x11, 0x5e, 0xea, 0xc0, 0x90, 0x57, + 0x01, 0x9b, 0x59, 0x01, 0x6f, 0x6f, 0xbf, 0xda, 0xda, 0x4a, 0x2b, 0x41, 0xcd, 0x5f, 0x17, 0x77, + 0x4f, 0x37, 0x1f, 0x6a, 0x10, 0xe1, 0x8d, 0x4f, 0x42, 0xb0, 0xc8, 0x3b, 0xce, 0x61, 0x49, 0x8d, + 0x27, 0x87, 0x25, 0x3d, 0xb4, 0x7c, 0x16, 0x39, 0x1f, 0x78, 0xa5, 0x83, 0xa2, 0xb0, 0xc2, 0x46, + 0x7e, 0x7c, 0x42, 0xd2, 0xdc, 0x46, 0xaa, 0x40, 0xb0, 0x00, 0x69, 0xc0, 0xdf, 0x9b, 0x41, 0xf0, + 0x99, 0x7c, 0x03, 0xe7, 0x9d, 0xdd, 0xf9, 0x12, 0xe9, 0x8b, 0x25, 0xd2, 0x1f, 0x97, 0x48, 0xbf, + 0x5e, 0x21, 0x6d, 0xb1, 0x42, 0xda, 0xfd, 0x0a, 0x69, 0xc7, 0xb6, 0xe7, 0xf3, 0xfe, 0xa4, 0x6b, + 0xf5, 0xe8, 0x48, 0x85, 0x71, 0xd2, 0xeb, 0xab, 0xe5, 0x9f, 0x24, 0xf8, 0x54, 0x45, 0xf3, 0x59, + 0x48, 0x58, 0xd7, 0x10, 0xdf, 0xd6, 0xdf, 0xe7, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6f, 0xf4, 0x3b, + 0x34, 0x16, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -457,8 +349,6 @@ type QueryClient interface { Tokens(ctx context.Context, in *QueryTokensRequest, opts ...grpc.CallOption) (*QueryTokensResponse, error) // Parameters queries the tokens of the module. Token(ctx context.Context, in *QueryTokenRequest, opts ...grpc.CallOption) (*QueryTokenResponse, error) - // Parameters queries the tokens of the module. - IsAuthorized(ctx context.Context, in *QueryIsAuthorizedRequest, opts ...grpc.CallOption) (*QueryIsAuthorizedResponse, error) } type queryClient struct { @@ -496,15 +386,6 @@ func (c *queryClient) Token(ctx context.Context, in *QueryTokenRequest, opts ... return out, nil } -func (c *queryClient) IsAuthorized(ctx context.Context, in *QueryIsAuthorizedRequest, opts ...grpc.CallOption) (*QueryIsAuthorizedResponse, error) { - out := new(QueryIsAuthorizedResponse) - err := c.cc.Invoke(ctx, "/realionetwork.asset.v1.Query/IsAuthorized", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - // QueryServer is the server API for Query service. type QueryServer interface { // Parameters queries the parameters of the module. @@ -513,8 +394,6 @@ type QueryServer interface { Tokens(context.Context, *QueryTokensRequest) (*QueryTokensResponse, error) // Parameters queries the tokens of the module. Token(context.Context, *QueryTokenRequest) (*QueryTokenResponse, error) - // Parameters queries the tokens of the module. - IsAuthorized(context.Context, *QueryIsAuthorizedRequest) (*QueryIsAuthorizedResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -530,9 +409,6 @@ func (*UnimplementedQueryServer) Tokens(ctx context.Context, req *QueryTokensReq func (*UnimplementedQueryServer) Token(ctx context.Context, req *QueryTokenRequest) (*QueryTokenResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Token not implemented") } -func (*UnimplementedQueryServer) IsAuthorized(ctx context.Context, req *QueryIsAuthorizedRequest) (*QueryIsAuthorizedResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method IsAuthorized not implemented") -} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -592,24 +468,6 @@ func _Query_Token_Handler(srv interface{}, ctx context.Context, dec func(interfa return interceptor(ctx, in, info, handler) } -func _Query_IsAuthorized_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(QueryIsAuthorizedRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(QueryServer).IsAuthorized(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/realionetwork.asset.v1.Query/IsAuthorized", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).IsAuthorized(ctx, req.(*QueryIsAuthorizedRequest)) - } - return interceptor(ctx, in, info, handler) -} - var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "realionetwork.asset.v1.Query", HandlerType: (*QueryServer)(nil), @@ -626,10 +484,6 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "Token", Handler: _Query_Token_Handler, }, - { - MethodName: "IsAuthorized", - Handler: _Query_IsAuthorized_Handler, - }, }, Streams: []grpc.StreamDesc{}, Metadata: "realionetwork/asset/v1/query.proto", @@ -814,76 +668,6 @@ func (m *QueryTokenResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *QueryIsAuthorizedRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *QueryIsAuthorizedRequest) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *QueryIsAuthorizedRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Address) > 0 { - i -= len(m.Address) - copy(dAtA[i:], m.Address) - i = encodeVarintQuery(dAtA, i, uint64(len(m.Address))) - i-- - dAtA[i] = 0x12 - } - if len(m.Symbol) > 0 { - i -= len(m.Symbol) - copy(dAtA[i:], m.Symbol) - i = encodeVarintQuery(dAtA, i, uint64(len(m.Symbol))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *QueryIsAuthorizedResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *QueryIsAuthorizedResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *QueryIsAuthorizedResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.IsAuthorized { - i-- - if m.IsAuthorized { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { offset -= sovQuery(v) base := offset @@ -963,35 +747,6 @@ func (m *QueryTokenResponse) Size() (n int) { return n } -func (m *QueryIsAuthorizedRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Symbol) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) - } - l = len(m.Address) - if l > 0 { - n += 1 + l + sovQuery(uint64(l)) - } - return n -} - -func (m *QueryIsAuthorizedResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.IsAuthorized { - n += 2 - } - return n -} - func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1430,190 +1185,6 @@ func (m *QueryTokenResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryIsAuthorizedRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: QueryIsAuthorizedRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: QueryIsAuthorizedRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Symbol", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthQuery - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthQuery - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Symbol = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthQuery - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthQuery - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Address = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipQuery(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *QueryIsAuthorizedResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: QueryIsAuthorizedResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: QueryIsAuthorizedResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field IsAuthorized", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.IsAuthorized = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipQuery(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/asset/types/query.pb.gw.go b/x/asset/types/query.pb.gw.go index e9d9ece9..4c943a0a 100644 --- a/x/asset/types/query.pb.gw.go +++ b/x/asset/types/query.pb.gw.go @@ -123,82 +123,6 @@ func local_request_Query_Token_0(ctx context.Context, marshaler runtime.Marshale } -func request_Query_IsAuthorized_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryIsAuthorizedRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["symbol"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "symbol") - } - - protoReq.Symbol, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "symbol", err) - } - - val, ok = pathParams["address"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") - } - - protoReq.Address, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) - } - - msg, err := client.IsAuthorized(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_Query_IsAuthorized_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryIsAuthorizedRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["symbol"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "symbol") - } - - protoReq.Symbol, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "symbol", err) - } - - val, ok = pathParams["address"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") - } - - protoReq.Address, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) - } - - msg, err := server.IsAuthorized(ctx, &protoReq) - return msg, metadata, err - -} - // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -274,29 +198,6 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) - mux.Handle("GET", pattern_Query_IsAuthorized_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_Query_IsAuthorized_0(rctx, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Query_IsAuthorized_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - return nil } @@ -398,26 +299,6 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) - mux.Handle("GET", pattern_Query_IsAuthorized_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_Query_IsAuthorized_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Query_IsAuthorized_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - return nil } @@ -427,8 +308,6 @@ var ( pattern_Query_Tokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"realionetwork", "asset", "v1", "tokens"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_Token_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"realionetwork", "asset", "v1", "tokens", "symbol"}, "", runtime.AssumeColonVerbOpt(false))) - - pattern_Query_IsAuthorized_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"realionetwork", "asset", "v1", "isauthorized", "symbol", "address"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( @@ -437,6 +316,4 @@ var ( forward_Query_Tokens_0 = runtime.ForwardResponseMessage forward_Query_Token_0 = runtime.ForwardResponseMessage - - forward_Query_IsAuthorized_0 = runtime.ForwardResponseMessage ) diff --git a/x/asset/types/token.go b/x/asset/types/token.go index 600cc100..e1d040e8 100644 --- a/x/asset/types/token.go +++ b/x/asset/types/token.go @@ -1,53 +1,54 @@ package types -import sdk "github.com/cosmos/cosmos-sdk/types" +import ( + fmt "fmt" + "strings" -func NewToken(name string, symbol string, total string, manager string, authorizationRequired bool) Token { + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// NewToken creates a new Token instance +func NewToken(id, name string, decimal uint32, description string, symbol string, issuer []byte, evmAddress string) Token { return Token{ - Name: name, - Symbol: symbol, - Total: total, - Manager: manager, - AuthorizationRequired: authorizationRequired, - Authorized: []*TokenAuthorization{}, + TokenId: id, + Name: name, + Decimal: decimal, + Description: description, + Symbol: symbol, + Issuer: issuer, + EvmAddress: evmAddress, } } -func NewAuthorization(address sdk.Address) *TokenAuthorization { - return &TokenAuthorization{Address: address.String(), Authorized: true} +func NewTokenManagement(managers [][]byte, allowNewExtension bool, extensionList []string, maxSupply math.Int) TokenManagement { + return TokenManagement{ + Managers: managers, + AllowNewExtensions: allowNewExtension, + ExtensionsList: extensionList, + MaxSupply: maxSupply, + } } -func (t *Token) AuthorizeAddress(addr sdk.Address) *Token { - found := false - for _, a := range t.Authorized { - if a.Address == addr.String() { - a.Authorized = true - found = true - break - } +func ValidateTokenId(tokenId string) error { + tokenParts := strings.Split(tokenId, "/") + if len(tokenParts) < 3 { + return fmt.Errorf("invalid token id format, should be asset/IssuerAddress/lowercaseTokenName") } - if !found { - newAuthorized := NewAuthorization(addr) - t.Authorized = append(t.Authorized, newAuthorized) + + if tokenParts[0] != ModuleName { + return fmt.Errorf("invalid token id format, should be asset/IssuerAddress/lowercaseTokenName") } - return t -} -func (t *Token) UnAuthorizeAddress(addr sdk.Address) *Token { - for _, a := range t.Authorized { - if a.Address == addr.String() { - a.Authorized = false - break - } + _, err := sdk.AccAddressFromBech32(tokenParts[1]) + if err != nil { + return fmt.Errorf("invalid issuer address") } - return t -} -func (t *Token) AddressIsAuthorized(addr sdk.AccAddress) bool { - for _, a := range t.Authorized { - if a.Address == addr.String() && a.Authorized { - return true - } + tokenName := strings.Join(tokenParts[2:], "/") + if strings.ToLower(tokenName) != tokenName { + return fmt.Errorf("token name should be in lower case") } - return false + + return nil } diff --git a/x/asset/types/token.pb.go b/x/asset/types/token.pb.go index dac78eed..bad0898f 100644 --- a/x/asset/types/token.pb.go +++ b/x/asset/types/token.pb.go @@ -4,6 +4,7 @@ package types import ( + cosmossdk_io_math "cosmossdk.io/math" fmt "fmt" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" @@ -23,14 +24,42 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +type Role int32 + +const ( + // ROLE_UNSPECIFIED defines a no-op role. + EmptyRole Role = 0 + // ROLE_MANAGER defines a token manager role. + ManagerRole Role = 1 +) + +var Role_name = map[int32]string{ + 0: "ROLE_UNSPECIFIED", + 1: "ROLE_MANAGER", +} + +var Role_value = map[string]int32{ + "ROLE_UNSPECIFIED": 0, + "ROLE_MANAGER": 1, +} + +func (x Role) String() string { + return proto.EnumName(Role_name, int32(x)) +} + +func (Role) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_2f83138fc60a3176, []int{0} +} + // Token represents an asset in the module type Token struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` - Total string `protobuf:"bytes,3,opt,name=total,proto3" json:"total,omitempty"` - AuthorizationRequired bool `protobuf:"varint,4,opt,name=authorizationRequired,proto3" json:"authorizationRequired,omitempty"` - Manager string `protobuf:"bytes,5,opt,name=manager,proto3" json:"manager,omitempty"` - Authorized []*TokenAuthorization `protobuf:"bytes,6,rep,name=authorized,proto3" json:"authorized,omitempty"` + TokenId string `protobuf:"bytes,1,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` + Issuer []byte `protobuf:"bytes,2,opt,name=issuer,proto3" json:"issuer,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Symbol string `protobuf:"bytes,4,opt,name=symbol,proto3" json:"symbol,omitempty"` + Decimal uint32 `protobuf:"varint,5,opt,name=decimal,proto3" json:"decimal,omitempty"` + Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"` + EvmAddress string `protobuf:"bytes,9,opt,name=evm_address,json=evmAddress,proto3" json:"evm_address,omitempty"` } func (m *Token) Reset() { *m = Token{} } @@ -66,6 +95,20 @@ func (m *Token) XXX_DiscardUnknown() { var xxx_messageInfo_Token proto.InternalMessageInfo +func (m *Token) GetTokenId() string { + if m != nil { + return m.TokenId + } + return "" +} + +func (m *Token) GetIssuer() []byte { + if m != nil { + return m.Issuer + } + return nil +} + func (m *Token) GetName() string { if m != nil { return m.Name @@ -80,36 +123,93 @@ func (m *Token) GetSymbol() string { return "" } -func (m *Token) GetTotal() string { +func (m *Token) GetDecimal() uint32 { if m != nil { - return m.Total + return m.Decimal } - return "" + return 0 } -func (m *Token) GetAuthorizationRequired() bool { +func (m *Token) GetDescription() string { if m != nil { - return m.AuthorizationRequired + return m.Description } - return false + return "" } -func (m *Token) GetManager() string { +func (m *Token) GetEvmAddress() string { if m != nil { - return m.Manager + return m.EvmAddress } return "" } -func (m *Token) GetAuthorized() []*TokenAuthorization { +// TokenManagement represents the asset manager's execute functions. +type TokenManagement struct { + Managers [][]byte `protobuf:"bytes,1,rep,name=managers,proto3" json:"managers,omitempty"` + AllowNewExtensions bool `protobuf:"varint,2,opt,name=allow_new_extensions,json=allowNewExtensions,proto3" json:"allow_new_extensions,omitempty"` + ExtensionsList []string `protobuf:"bytes,3,rep,name=extensions_list,json=extensionsList,proto3" json:"extensions_list,omitempty"` + MaxSupply cosmossdk_io_math.Int `protobuf:"bytes,4,opt,name=max_supply,json=maxSupply,proto3,customtype=cosmossdk.io/math.Int" json:"max_supply"` +} + +func (m *TokenManagement) Reset() { *m = TokenManagement{} } +func (m *TokenManagement) String() string { return proto.CompactTextString(m) } +func (*TokenManagement) ProtoMessage() {} +func (*TokenManagement) Descriptor() ([]byte, []int) { + return fileDescriptor_2f83138fc60a3176, []int{1} +} +func (m *TokenManagement) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TokenManagement) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TokenManagement.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TokenManagement) XXX_Merge(src proto.Message) { + xxx_messageInfo_TokenManagement.Merge(m, src) +} +func (m *TokenManagement) XXX_Size() int { + return m.Size() +} +func (m *TokenManagement) XXX_DiscardUnknown() { + xxx_messageInfo_TokenManagement.DiscardUnknown(m) +} + +var xxx_messageInfo_TokenManagement proto.InternalMessageInfo + +func (m *TokenManagement) GetManagers() [][]byte { + if m != nil { + return m.Managers + } + return nil +} + +func (m *TokenManagement) GetAllowNewExtensions() bool { + if m != nil { + return m.AllowNewExtensions + } + return false +} + +func (m *TokenManagement) GetExtensionsList() []string { if m != nil { - return m.Authorized + return m.ExtensionsList } return nil } func init() { + proto.RegisterEnum("realionetwork.asset.v1.Role", Role_name, Role_value) proto.RegisterType((*Token)(nil), "realionetwork.asset.v1.Token") + proto.RegisterType((*TokenManagement)(nil), "realionetwork.asset.v1.TokenManagement") } func init() { @@ -117,25 +217,38 @@ func init() { } var fileDescriptor_2f83138fc60a3176 = []byte{ - // 286 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0xb1, 0x4e, 0xc3, 0x30, - 0x10, 0x86, 0x6b, 0xda, 0x06, 0x30, 0x9b, 0x55, 0x2a, 0xab, 0x83, 0x15, 0x75, 0x8a, 0x90, 0x70, - 0xd4, 0xc2, 0x0b, 0xc0, 0x88, 0x98, 0x22, 0x26, 0x36, 0xa7, 0xb5, 0x92, 0xa8, 0x49, 0xae, 0x38, - 0x97, 0x42, 0x79, 0x0a, 0x1e, 0x8b, 0xb1, 0x23, 0x23, 0x4a, 0xc4, 0x7b, 0xa0, 0xba, 0x89, 0x44, - 0xa4, 0x8a, 0xed, 0xff, 0x7d, 0xff, 0xfd, 0x27, 0x7f, 0x74, 0x6a, 0xb4, 0x4a, 0x13, 0xc8, 0x35, - 0xbe, 0x82, 0x59, 0xf9, 0xaa, 0x28, 0x34, 0xfa, 0x9b, 0x99, 0x8f, 0xb0, 0xd2, 0xb9, 0x5c, 0x1b, - 0x40, 0x60, 0xe3, 0x4e, 0x46, 0xda, 0x8c, 0xdc, 0xcc, 0x26, 0xa3, 0x08, 0x22, 0xb0, 0x11, 0x7f, - 0xaf, 0x0e, 0xe9, 0x89, 0xff, 0x5f, 0xa3, 0x2a, 0x31, 0x06, 0x93, 0xbc, 0x2b, 0x4c, 0xa0, 0xa9, - 0x9f, 0xfe, 0x10, 0x3a, 0x7c, 0xda, 0x0f, 0x19, 0xa3, 0x83, 0x5c, 0x65, 0x9a, 0x13, 0x97, 0x78, - 0xe7, 0x81, 0xd5, 0x6c, 0x4c, 0x9d, 0x62, 0x9b, 0x85, 0x90, 0xf2, 0x13, 0xfb, 0xda, 0x38, 0x36, - 0xa2, 0x43, 0x04, 0x54, 0x29, 0xef, 0xdb, 0xe7, 0x83, 0x61, 0xb7, 0xf4, 0xb2, 0x73, 0x22, 0xd0, - 0x2f, 0x65, 0x62, 0xf4, 0x92, 0x0f, 0x5c, 0xe2, 0x9d, 0x05, 0xc7, 0x87, 0x8c, 0xd3, 0xd3, 0x4c, - 0xe5, 0x2a, 0xd2, 0x86, 0x0f, 0x6d, 0x5b, 0x6b, 0xd9, 0x03, 0xa5, 0xed, 0x8a, 0x5e, 0x72, 0xc7, - 0xed, 0x7b, 0x17, 0xf3, 0x2b, 0x79, 0x9c, 0x87, 0xb4, 0x9f, 0xb8, 0xeb, 0x5c, 0xf8, 0xb3, 0x7d, - 0xff, 0xf8, 0x59, 0x09, 0xb2, 0xab, 0x04, 0xf9, 0xae, 0x04, 0xf9, 0xa8, 0x45, 0x6f, 0x57, 0x8b, - 0xde, 0x57, 0x2d, 0x7a, 0xcf, 0xf3, 0x28, 0xc1, 0xb8, 0x0c, 0xe5, 0x02, 0xb2, 0x86, 0x1e, 0xea, - 0x45, 0xdc, 0xc8, 0xeb, 0x96, 0xe4, 0x5b, 0xc3, 0x12, 0xb7, 0x6b, 0x5d, 0x84, 0x8e, 0x85, 0x77, - 0xf3, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x66, 0x07, 0x38, 0xad, 0xc1, 0x01, 0x00, 0x00, + // 487 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x92, 0xcf, 0x6e, 0xd3, 0x4c, + 0x14, 0xc5, 0x3d, 0x5f, 0xd2, 0x34, 0x99, 0xa4, 0x5f, 0xa2, 0x51, 0xa9, 0x4c, 0x24, 0x1c, 0x13, + 0x16, 0x44, 0x48, 0xd8, 0x14, 0xb6, 0x6c, 0x52, 0x30, 0x28, 0x52, 0x1a, 0x90, 0x5b, 0x36, 0x6c, + 0xac, 0x49, 0x7c, 0x95, 0x8c, 0xe2, 0x99, 0xb1, 0x3c, 0x93, 0x7f, 0x6f, 0x80, 0xba, 0xe2, 0x05, + 0xba, 0xe2, 0x51, 0x90, 0x50, 0x97, 0x5d, 0x22, 0x16, 0x15, 0x4a, 0x5e, 0x04, 0x65, 0x9c, 0xb6, + 0xb0, 0x3b, 0xe7, 0xcc, 0xef, 0x8e, 0x74, 0xaf, 0x0e, 0x6e, 0x67, 0x40, 0x13, 0x26, 0x05, 0xe8, + 0x85, 0xcc, 0xa6, 0x3e, 0x55, 0x0a, 0xb4, 0x3f, 0x3f, 0xf6, 0xb5, 0x9c, 0x82, 0xf0, 0xd2, 0x4c, + 0x6a, 0x49, 0x8e, 0xfe, 0x61, 0x3c, 0xc3, 0x78, 0xf3, 0xe3, 0xe6, 0xe1, 0x58, 0x8e, 0xa5, 0x41, + 0xfc, 0xad, 0xca, 0xe9, 0xf6, 0x77, 0x84, 0xf7, 0xce, 0xb7, 0xd3, 0xe4, 0x21, 0x2e, 0x9b, 0x6f, + 0x22, 0x16, 0xdb, 0xc8, 0x45, 0x9d, 0x4a, 0xb8, 0x6f, 0x7c, 0x2f, 0x26, 0x47, 0xb8, 0xc4, 0x94, + 0x9a, 0x41, 0x66, 0xff, 0xe7, 0xa2, 0x4e, 0x2d, 0xdc, 0x39, 0x42, 0x70, 0x51, 0x50, 0x0e, 0x76, + 0xc1, 0xe0, 0x46, 0x6f, 0x59, 0xb5, 0xe2, 0x43, 0x99, 0xd8, 0x45, 0x93, 0xee, 0x1c, 0xb1, 0xf1, + 0x7e, 0x0c, 0x23, 0xc6, 0x69, 0x62, 0xef, 0xb9, 0xa8, 0x73, 0x10, 0xde, 0x5a, 0xe2, 0xe2, 0x6a, + 0x0c, 0x6a, 0x94, 0xb1, 0x54, 0x33, 0x29, 0xec, 0x92, 0x19, 0xfb, 0x3b, 0x22, 0x2d, 0x5c, 0x85, + 0x39, 0x8f, 0x68, 0x1c, 0x67, 0xa0, 0x94, 0x5d, 0x31, 0x04, 0x86, 0x39, 0xef, 0xe6, 0x49, 0xfb, + 0x07, 0xc2, 0x75, 0xb3, 0xc5, 0x29, 0x15, 0x74, 0x0c, 0x1c, 0x84, 0x26, 0x4d, 0x5c, 0xe6, 0xc6, + 0x65, 0xca, 0x46, 0x6e, 0xa1, 0x53, 0x0b, 0xef, 0x3c, 0x79, 0x81, 0x0f, 0x69, 0x92, 0xc8, 0x45, + 0x24, 0x60, 0x11, 0xc1, 0x52, 0x83, 0x50, 0x4c, 0x0a, 0x65, 0xd6, 0x2b, 0x87, 0xc4, 0xbc, 0x0d, + 0x60, 0x11, 0xdc, 0xbd, 0x90, 0xa7, 0xb8, 0x7e, 0xcf, 0x45, 0x09, 0x53, 0xda, 0x2e, 0xb8, 0x85, + 0x4e, 0x25, 0xfc, 0xff, 0x3e, 0xee, 0x33, 0xa5, 0xc9, 0x6b, 0x8c, 0x39, 0x5d, 0x46, 0x6a, 0x96, + 0xa6, 0xc9, 0x2a, 0xbf, 0xc1, 0xc9, 0xa3, 0xab, 0x9b, 0x96, 0xf5, 0xeb, 0xa6, 0xf5, 0x60, 0x24, + 0x15, 0x97, 0x4a, 0xc5, 0x53, 0x8f, 0x49, 0x9f, 0x53, 0x3d, 0xf1, 0x7a, 0x42, 0x87, 0x15, 0x4e, + 0x97, 0x67, 0x86, 0x7f, 0x76, 0x8e, 0x8b, 0xa1, 0x4c, 0x80, 0x3c, 0xc1, 0x8d, 0xf0, 0x43, 0x3f, + 0x88, 0x3e, 0x0d, 0xce, 0x3e, 0x06, 0x6f, 0x7a, 0xef, 0x7a, 0xc1, 0xdb, 0x86, 0xd5, 0x3c, 0xb8, + 0xb8, 0x74, 0x2b, 0x01, 0x4f, 0xf5, 0xca, 0x40, 0x8f, 0x71, 0xcd, 0x40, 0xa7, 0xdd, 0x41, 0xf7, + 0x7d, 0x10, 0x36, 0x50, 0xb3, 0x7e, 0x71, 0xe9, 0x56, 0xf3, 0x1b, 0x64, 0x5b, 0xa4, 0x59, 0xfc, + 0xf2, 0xcd, 0xb1, 0x4e, 0xfa, 0x57, 0x6b, 0x07, 0x5d, 0xaf, 0x1d, 0xf4, 0x7b, 0xed, 0xa0, 0xaf, + 0x1b, 0xc7, 0xba, 0xde, 0x38, 0xd6, 0xcf, 0x8d, 0x63, 0x7d, 0x7e, 0x39, 0x66, 0x7a, 0x32, 0x1b, + 0x7a, 0x23, 0xc9, 0xfd, 0xbc, 0x37, 0x1a, 0x46, 0x93, 0x9d, 0x7c, 0x7e, 0xdb, 0xb3, 0xe5, 0xae, + 0x69, 0x7a, 0x95, 0x82, 0x1a, 0x96, 0x4c, 0x73, 0x5e, 0xfd, 0x09, 0x00, 0x00, 0xff, 0xff, 0x56, + 0xd0, 0x21, 0xe1, 0x8d, 0x02, 0x00, 0x00, } func (m *Token) Marshal() (dAtA []byte, err error) { @@ -158,61 +271,117 @@ func (m *Token) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if len(m.Authorized) > 0 { - for iNdEx := len(m.Authorized) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Authorized[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintToken(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x32 - } - } - if len(m.Manager) > 0 { - i -= len(m.Manager) - copy(dAtA[i:], m.Manager) - i = encodeVarintToken(dAtA, i, uint64(len(m.Manager))) + if len(m.EvmAddress) > 0 { + i -= len(m.EvmAddress) + copy(dAtA[i:], m.EvmAddress) + i = encodeVarintToken(dAtA, i, uint64(len(m.EvmAddress))) i-- - dAtA[i] = 0x2a + dAtA[i] = 0x4a } - if m.AuthorizationRequired { - i-- - if m.AuthorizationRequired { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintToken(dAtA, i, uint64(len(m.Description))) i-- - dAtA[i] = 0x20 + dAtA[i] = 0x32 } - if len(m.Total) > 0 { - i -= len(m.Total) - copy(dAtA[i:], m.Total) - i = encodeVarintToken(dAtA, i, uint64(len(m.Total))) + if m.Decimal != 0 { + i = encodeVarintToken(dAtA, i, uint64(m.Decimal)) i-- - dAtA[i] = 0x1a + dAtA[i] = 0x28 } if len(m.Symbol) > 0 { i -= len(m.Symbol) copy(dAtA[i:], m.Symbol) i = encodeVarintToken(dAtA, i, uint64(len(m.Symbol))) i-- - dAtA[i] = 0x12 + dAtA[i] = 0x22 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = encodeVarintToken(dAtA, i, uint64(len(m.Name))) i-- + dAtA[i] = 0x1a + } + if len(m.Issuer) > 0 { + i -= len(m.Issuer) + copy(dAtA[i:], m.Issuer) + i = encodeVarintToken(dAtA, i, uint64(len(m.Issuer))) + i-- + dAtA[i] = 0x12 + } + if len(m.TokenId) > 0 { + i -= len(m.TokenId) + copy(dAtA[i:], m.TokenId) + i = encodeVarintToken(dAtA, i, uint64(len(m.TokenId))) + i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } +func (m *TokenManagement) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TokenManagement) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TokenManagement) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.MaxSupply.Size() + i -= size + if _, err := m.MaxSupply.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintToken(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.ExtensionsList) > 0 { + for iNdEx := len(m.ExtensionsList) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ExtensionsList[iNdEx]) + copy(dAtA[i:], m.ExtensionsList[iNdEx]) + i = encodeVarintToken(dAtA, i, uint64(len(m.ExtensionsList[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if m.AllowNewExtensions { + i-- + if m.AllowNewExtensions { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Managers) > 0 { + for iNdEx := len(m.Managers) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Managers[iNdEx]) + copy(dAtA[i:], m.Managers[iNdEx]) + i = encodeVarintToken(dAtA, i, uint64(len(m.Managers[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func encodeVarintToken(dAtA []byte, offset int, v uint64) int { offset -= sovToken(v) base := offset @@ -230,6 +399,14 @@ func (m *Token) Size() (n int) { } var l int _ = l + l = len(m.TokenId) + if l > 0 { + n += 1 + l + sovToken(uint64(l)) + } + l = len(m.Issuer) + if l > 0 { + n += 1 + l + sovToken(uint64(l)) + } l = len(m.Name) if l > 0 { n += 1 + l + sovToken(uint64(l)) @@ -238,23 +415,43 @@ func (m *Token) Size() (n int) { if l > 0 { n += 1 + l + sovToken(uint64(l)) } - l = len(m.Total) + if m.Decimal != 0 { + n += 1 + sovToken(uint64(m.Decimal)) + } + l = len(m.Description) if l > 0 { n += 1 + l + sovToken(uint64(l)) } - if m.AuthorizationRequired { - n += 2 - } - l = len(m.Manager) + l = len(m.EvmAddress) if l > 0 { n += 1 + l + sovToken(uint64(l)) } - if len(m.Authorized) > 0 { - for _, e := range m.Authorized { - l = e.Size() + return n +} + +func (m *TokenManagement) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Managers) > 0 { + for _, b := range m.Managers { + l = len(b) + n += 1 + l + sovToken(uint64(l)) + } + } + if m.AllowNewExtensions { + n += 2 + } + if len(m.ExtensionsList) > 0 { + for _, s := range m.ExtensionsList { + l = len(s) n += 1 + l + sovToken(uint64(l)) } } + l = m.MaxSupply.Size() + n += 1 + l + sovToken(uint64(l)) return n } @@ -294,6 +491,72 @@ func (m *Token) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowToken + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthToken + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthToken + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TokenId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Issuer", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowToken + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthToken + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthToken + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Issuer = append(m.Issuer[:0], dAtA[iNdEx:postIndex]...) + if m.Issuer == nil { + m.Issuer = []byte{} + } + iNdEx = postIndex + case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } @@ -325,7 +588,7 @@ func (m *Token) Unmarshal(dAtA []byte) error { } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 2: + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Symbol", wireType) } @@ -357,9 +620,28 @@ func (m *Token) Unmarshal(dAtA []byte) error { } m.Symbol = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Decimal", wireType) + } + m.Decimal = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowToken + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Decimal |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Total", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -387,11 +669,125 @@ func (m *Token) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Total = string(dAtA[iNdEx:postIndex]) + m.Description = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 4: + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EvmAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowToken + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthToken + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthToken + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EvmAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipToken(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthToken + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TokenManagement) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowToken + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TokenManagement: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TokenManagement: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Managers", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowToken + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthToken + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthToken + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Managers = append(m.Managers, make([]byte, postIndex-iNdEx)) + copy(m.Managers[len(m.Managers)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationRequired", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field AllowNewExtensions", wireType) } var v int for shift := uint(0); ; shift += 7 { @@ -408,10 +804,10 @@ func (m *Token) Unmarshal(dAtA []byte) error { break } } - m.AuthorizationRequired = bool(v != 0) - case 5: + m.AllowNewExtensions = bool(v != 0) + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Manager", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ExtensionsList", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -439,13 +835,13 @@ func (m *Token) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Manager = string(dAtA[iNdEx:postIndex]) + m.ExtensionsList = append(m.ExtensionsList, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 6: + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Authorized", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field MaxSupply", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowToken @@ -455,23 +851,23 @@ func (m *Token) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthToken } - postIndex := iNdEx + msglen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthToken } if postIndex > l { return io.ErrUnexpectedEOF } - m.Authorized = append(m.Authorized, &TokenAuthorization{}) - if err := m.Authorized[len(m.Authorized)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.MaxSupply.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/x/asset/types/tokenauthorization.pb.go b/x/asset/types/tokenauthorization.pb.go deleted file mode 100644 index 97cb95de..00000000 --- a/x/asset/types/tokenauthorization.pb.go +++ /dev/null @@ -1,363 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: realionetwork/asset/v1/tokenauthorization.proto - -package types - -import ( - fmt "fmt" - _ "github.com/cosmos/gogoproto/gogoproto" - proto "github.com/cosmos/gogoproto/proto" - io "io" - math "math" - math_bits "math/bits" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package - -// TokenAuthorization represents the current authorization state for an -// address:token -type TokenAuthorization struct { - Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` - Authorized bool `protobuf:"varint,3,opt,name=authorized,proto3" json:"authorized,omitempty"` -} - -func (m *TokenAuthorization) Reset() { *m = TokenAuthorization{} } -func (m *TokenAuthorization) String() string { return proto.CompactTextString(m) } -func (*TokenAuthorization) ProtoMessage() {} -func (*TokenAuthorization) Descriptor() ([]byte, []int) { - return fileDescriptor_082a161f7b2bd506, []int{0} -} -func (m *TokenAuthorization) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *TokenAuthorization) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_TokenAuthorization.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *TokenAuthorization) XXX_Merge(src proto.Message) { - xxx_messageInfo_TokenAuthorization.Merge(m, src) -} -func (m *TokenAuthorization) XXX_Size() int { - return m.Size() -} -func (m *TokenAuthorization) XXX_DiscardUnknown() { - xxx_messageInfo_TokenAuthorization.DiscardUnknown(m) -} - -var xxx_messageInfo_TokenAuthorization proto.InternalMessageInfo - -func (m *TokenAuthorization) GetAddress() string { - if m != nil { - return m.Address - } - return "" -} - -func (m *TokenAuthorization) GetAuthorized() bool { - if m != nil { - return m.Authorized - } - return false -} - -func init() { - proto.RegisterType((*TokenAuthorization)(nil), "realionetwork.asset.v1.TokenAuthorization") -} - -func init() { - proto.RegisterFile("realionetwork/asset/v1/tokenauthorization.proto", fileDescriptor_082a161f7b2bd506) -} - -var fileDescriptor_082a161f7b2bd506 = []byte{ - // 207 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x2f, 0x4a, 0x4d, 0xcc, - 0xc9, 0xcc, 0xcf, 0x4b, 0x2d, 0x29, 0xcf, 0x2f, 0xca, 0xd6, 0x4f, 0x2c, 0x2e, 0x4e, 0x2d, 0xd1, - 0x2f, 0x33, 0xd4, 0x2f, 0xc9, 0xcf, 0x4e, 0xcd, 0x4b, 0x2c, 0x2d, 0xc9, 0xc8, 0x2f, 0xca, 0xac, - 0x4a, 0x2c, 0xc9, 0xcc, 0xcf, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x43, 0xd1, 0xa0, - 0x07, 0xd6, 0xa0, 0x57, 0x66, 0x28, 0x25, 0x92, 0x9e, 0x9f, 0x9e, 0x0f, 0x56, 0xa2, 0x0f, 0x62, - 0x41, 0x54, 0x2b, 0xf9, 0x71, 0x09, 0x85, 0x80, 0x4c, 0x72, 0x44, 0x36, 0x49, 0x48, 0x82, 0x8b, - 0x3d, 0x31, 0x25, 0xa5, 0x28, 0xb5, 0xb8, 0x58, 0x82, 0x49, 0x81, 0x51, 0x83, 0x33, 0x08, 0xc6, - 0x15, 0x92, 0xe3, 0xe2, 0x82, 0x59, 0x9a, 0x9a, 0x22, 0xc1, 0xac, 0xc0, 0xa8, 0xc1, 0x11, 0x84, - 0x24, 0xe2, 0xe4, 0x73, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, - 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x46, 0xe9, - 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0x50, 0x3f, 0x95, 0xa4, 0x26, 0x67, 0x40, - 0x99, 0xba, 0x30, 0xff, 0x55, 0x40, 0x7d, 0x58, 0x52, 0x59, 0x90, 0x5a, 0x9c, 0xc4, 0x06, 0x76, - 0xa4, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xa5, 0xcd, 0x3b, 0xfc, 0x05, 0x01, 0x00, 0x00, -} - -func (m *TokenAuthorization) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *TokenAuthorization) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *TokenAuthorization) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Authorized { - i-- - if m.Authorized { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x18 - } - if len(m.Address) > 0 { - i -= len(m.Address) - copy(dAtA[i:], m.Address) - i = encodeVarintTokenauthorization(dAtA, i, uint64(len(m.Address))) - i-- - dAtA[i] = 0x12 - } - return len(dAtA) - i, nil -} - -func encodeVarintTokenauthorization(dAtA []byte, offset int, v uint64) int { - offset -= sovTokenauthorization(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *TokenAuthorization) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Address) - if l > 0 { - n += 1 + l + sovTokenauthorization(uint64(l)) - } - if m.Authorized { - n += 2 - } - return n -} - -func sovTokenauthorization(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozTokenauthorization(x uint64) (n int) { - return sovTokenauthorization(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *TokenAuthorization) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTokenauthorization - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: TokenAuthorization: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TokenAuthorization: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTokenauthorization - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthTokenauthorization - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthTokenauthorization - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Address = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Authorized", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTokenauthorization - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.Authorized = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipTokenauthorization(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTokenauthorization - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipTokenauthorization(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowTokenauthorization - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowTokenauthorization - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowTokenauthorization - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthTokenauthorization - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupTokenauthorization - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthTokenauthorization - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthTokenauthorization = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowTokenauthorization = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupTokenauthorization = fmt.Errorf("proto: unexpected end of group") -) diff --git a/x/asset/types/tx.pb.go b/x/asset/types/tx.pb.go index b986b865..f51ce540 100644 --- a/x/asset/types/tx.pb.go +++ b/x/asset/types/tx.pb.go @@ -5,6 +5,7 @@ package types import ( context "context" + cosmossdk_io_math "cosmossdk.io/math" fmt "fmt" _ "github.com/cosmos/cosmos-proto" _ "github.com/cosmos/cosmos-sdk/types/msgservice" @@ -31,11 +32,16 @@ var _ = math.Inf const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type MsgCreateToken struct { - Manager string `protobuf:"bytes,1,opt,name=manager,proto3" json:"manager,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Symbol string `protobuf:"bytes,3,opt,name=symbol,proto3" json:"symbol,omitempty"` - Total string `protobuf:"bytes,4,opt,name=total,proto3" json:"total,omitempty"` - AuthorizationRequired bool `protobuf:"varint,6,opt,name=authorizationRequired,proto3" json:"authorizationRequired,omitempty"` + // issuer is the address that defines the token + Issuer []byte `protobuf:"bytes,1,opt,name=issuer,proto3" json:"issuer,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Decimal uint32 `protobuf:"varint,3,opt,name=decimal,proto3" json:"decimal,omitempty"` + Symbol string `protobuf:"bytes,4,opt,name=symbol,proto3" json:"symbol,omitempty"` + Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` + Managers [][]byte `protobuf:"bytes,6,rep,name=managers,proto3" json:"managers,omitempty"` + AllowNewExtensions bool `protobuf:"varint,8,opt,name=allow_new_extensions,json=allowNewExtensions,proto3" json:"allow_new_extensions,omitempty"` + ExtensionsList []string `protobuf:"bytes,9,rep,name=extensions_list,json=extensionsList,proto3" json:"extensions_list,omitempty"` + MaxSupply cosmossdk_io_math.Int `protobuf:"bytes,10,opt,name=max_supply,json=maxSupply,proto3,customtype=cosmossdk.io/math.Int" json:"max_supply"` } func (m *MsgCreateToken) Reset() { *m = MsgCreateToken{} } @@ -71,11 +77,11 @@ func (m *MsgCreateToken) XXX_DiscardUnknown() { var xxx_messageInfo_MsgCreateToken proto.InternalMessageInfo -func (m *MsgCreateToken) GetManager() string { +func (m *MsgCreateToken) GetIssuer() []byte { if m != nil { - return m.Manager + return m.Issuer } - return "" + return nil } func (m *MsgCreateToken) GetName() string { @@ -85,6 +91,13 @@ func (m *MsgCreateToken) GetName() string { return "" } +func (m *MsgCreateToken) GetDecimal() uint32 { + if m != nil { + return m.Decimal + } + return 0 +} + func (m *MsgCreateToken) GetSymbol() string { if m != nil { return m.Symbol @@ -92,21 +105,36 @@ func (m *MsgCreateToken) GetSymbol() string { return "" } -func (m *MsgCreateToken) GetTotal() string { +func (m *MsgCreateToken) GetDescription() string { if m != nil { - return m.Total + return m.Description } return "" } -func (m *MsgCreateToken) GetAuthorizationRequired() bool { +func (m *MsgCreateToken) GetManagers() [][]byte { + if m != nil { + return m.Managers + } + return nil +} + +func (m *MsgCreateToken) GetAllowNewExtensions() bool { if m != nil { - return m.AuthorizationRequired + return m.AllowNewExtensions } return false } +func (m *MsgCreateToken) GetExtensionsList() []string { + if m != nil { + return m.ExtensionsList + } + return nil +} + type MsgCreateTokenResponse struct { + TokenId string `protobuf:"bytes,1,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` } func (m *MsgCreateTokenResponse) Reset() { *m = MsgCreateTokenResponse{} } @@ -142,216 +170,32 @@ func (m *MsgCreateTokenResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgCreateTokenResponse proto.InternalMessageInfo -type MsgUpdateToken struct { - Manager string `protobuf:"bytes,1,opt,name=manager,proto3" json:"manager,omitempty"` - Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` - AuthorizationRequired bool `protobuf:"varint,3,opt,name=authorizationRequired,proto3" json:"authorizationRequired,omitempty"` -} - -func (m *MsgUpdateToken) Reset() { *m = MsgUpdateToken{} } -func (m *MsgUpdateToken) String() string { return proto.CompactTextString(m) } -func (*MsgUpdateToken) ProtoMessage() {} -func (*MsgUpdateToken) Descriptor() ([]byte, []int) { - return fileDescriptor_1cfda60866e68e13, []int{2} -} -func (m *MsgUpdateToken) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *MsgUpdateToken) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_MsgUpdateToken.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *MsgUpdateToken) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgUpdateToken.Merge(m, src) -} -func (m *MsgUpdateToken) XXX_Size() int { - return m.Size() -} -func (m *MsgUpdateToken) XXX_DiscardUnknown() { - xxx_messageInfo_MsgUpdateToken.DiscardUnknown(m) -} - -var xxx_messageInfo_MsgUpdateToken proto.InternalMessageInfo - -func (m *MsgUpdateToken) GetManager() string { - if m != nil { - return m.Manager - } - return "" -} - -func (m *MsgUpdateToken) GetSymbol() string { - if m != nil { - return m.Symbol - } - return "" -} - -func (m *MsgUpdateToken) GetAuthorizationRequired() bool { - if m != nil { - return m.AuthorizationRequired - } - return false -} - -type MsgUpdateTokenResponse struct { -} - -func (m *MsgUpdateTokenResponse) Reset() { *m = MsgUpdateTokenResponse{} } -func (m *MsgUpdateTokenResponse) String() string { return proto.CompactTextString(m) } -func (*MsgUpdateTokenResponse) ProtoMessage() {} -func (*MsgUpdateTokenResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_1cfda60866e68e13, []int{3} -} -func (m *MsgUpdateTokenResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *MsgUpdateTokenResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_MsgUpdateTokenResponse.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *MsgUpdateTokenResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgUpdateTokenResponse.Merge(m, src) -} -func (m *MsgUpdateTokenResponse) XXX_Size() int { - return m.Size() -} -func (m *MsgUpdateTokenResponse) XXX_DiscardUnknown() { - xxx_messageInfo_MsgUpdateTokenResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_MsgUpdateTokenResponse proto.InternalMessageInfo - -type MsgAuthorizeAddress struct { - Manager string `protobuf:"bytes,1,opt,name=manager,proto3" json:"manager,omitempty"` - Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` - Address string `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` -} - -func (m *MsgAuthorizeAddress) Reset() { *m = MsgAuthorizeAddress{} } -func (m *MsgAuthorizeAddress) String() string { return proto.CompactTextString(m) } -func (*MsgAuthorizeAddress) ProtoMessage() {} -func (*MsgAuthorizeAddress) Descriptor() ([]byte, []int) { - return fileDescriptor_1cfda60866e68e13, []int{4} -} -func (m *MsgAuthorizeAddress) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *MsgAuthorizeAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_MsgAuthorizeAddress.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *MsgAuthorizeAddress) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgAuthorizeAddress.Merge(m, src) -} -func (m *MsgAuthorizeAddress) XXX_Size() int { - return m.Size() -} -func (m *MsgAuthorizeAddress) XXX_DiscardUnknown() { - xxx_messageInfo_MsgAuthorizeAddress.DiscardUnknown(m) -} - -var xxx_messageInfo_MsgAuthorizeAddress proto.InternalMessageInfo - -func (m *MsgAuthorizeAddress) GetManager() string { - if m != nil { - return m.Manager - } - return "" -} - -func (m *MsgAuthorizeAddress) GetSymbol() string { - if m != nil { - return m.Symbol - } - return "" -} - -func (m *MsgAuthorizeAddress) GetAddress() string { +func (m *MsgCreateTokenResponse) GetTokenId() string { if m != nil { - return m.Address + return m.TokenId } return "" } -type MsgAuthorizeAddressResponse struct { -} - -func (m *MsgAuthorizeAddressResponse) Reset() { *m = MsgAuthorizeAddressResponse{} } -func (m *MsgAuthorizeAddressResponse) String() string { return proto.CompactTextString(m) } -func (*MsgAuthorizeAddressResponse) ProtoMessage() {} -func (*MsgAuthorizeAddressResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_1cfda60866e68e13, []int{5} -} -func (m *MsgAuthorizeAddressResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *MsgAuthorizeAddressResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_MsgAuthorizeAddressResponse.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *MsgAuthorizeAddressResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgAuthorizeAddressResponse.Merge(m, src) -} -func (m *MsgAuthorizeAddressResponse) XXX_Size() int { - return m.Size() -} -func (m *MsgAuthorizeAddressResponse) XXX_DiscardUnknown() { - xxx_messageInfo_MsgAuthorizeAddressResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_MsgAuthorizeAddressResponse proto.InternalMessageInfo - -type MsgUnAuthorizeAddress struct { - Manager string `protobuf:"bytes,1,opt,name=manager,proto3" json:"manager,omitempty"` - Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` - Address string `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` +type MsgAssignRoles struct { + // issuer is the address that defines the token + Issuer []byte `protobuf:"bytes,1,opt,name=issuer,proto3" json:"issuer,omitempty"` + TokenId string `protobuf:"bytes,2,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` + Managers []byte `protobuf:"bytes,3,opt,name=managers,proto3" json:"managers,omitempty"` } -func (m *MsgUnAuthorizeAddress) Reset() { *m = MsgUnAuthorizeAddress{} } -func (m *MsgUnAuthorizeAddress) String() string { return proto.CompactTextString(m) } -func (*MsgUnAuthorizeAddress) ProtoMessage() {} -func (*MsgUnAuthorizeAddress) Descriptor() ([]byte, []int) { - return fileDescriptor_1cfda60866e68e13, []int{6} +func (m *MsgAssignRoles) Reset() { *m = MsgAssignRoles{} } +func (m *MsgAssignRoles) String() string { return proto.CompactTextString(m) } +func (*MsgAssignRoles) ProtoMessage() {} +func (*MsgAssignRoles) Descriptor() ([]byte, []int) { + return fileDescriptor_1cfda60866e68e13, []int{2} } -func (m *MsgUnAuthorizeAddress) XXX_Unmarshal(b []byte) error { +func (m *MsgAssignRoles) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *MsgUnAuthorizeAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *MsgAssignRoles) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_MsgUnAuthorizeAddress.Marshal(b, m, deterministic) + return xxx_messageInfo_MsgAssignRoles.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -361,54 +205,54 @@ func (m *MsgUnAuthorizeAddress) XXX_Marshal(b []byte, deterministic bool) ([]byt return b[:n], nil } } -func (m *MsgUnAuthorizeAddress) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgUnAuthorizeAddress.Merge(m, src) +func (m *MsgAssignRoles) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAssignRoles.Merge(m, src) } -func (m *MsgUnAuthorizeAddress) XXX_Size() int { +func (m *MsgAssignRoles) XXX_Size() int { return m.Size() } -func (m *MsgUnAuthorizeAddress) XXX_DiscardUnknown() { - xxx_messageInfo_MsgUnAuthorizeAddress.DiscardUnknown(m) +func (m *MsgAssignRoles) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAssignRoles.DiscardUnknown(m) } -var xxx_messageInfo_MsgUnAuthorizeAddress proto.InternalMessageInfo +var xxx_messageInfo_MsgAssignRoles proto.InternalMessageInfo -func (m *MsgUnAuthorizeAddress) GetManager() string { +func (m *MsgAssignRoles) GetIssuer() []byte { if m != nil { - return m.Manager + return m.Issuer } - return "" + return nil } -func (m *MsgUnAuthorizeAddress) GetSymbol() string { +func (m *MsgAssignRoles) GetTokenId() string { if m != nil { - return m.Symbol + return m.TokenId } return "" } -func (m *MsgUnAuthorizeAddress) GetAddress() string { +func (m *MsgAssignRoles) GetManagers() []byte { if m != nil { - return m.Address + return m.Managers } - return "" + return nil } -type MsgUnAuthorizeAddressResponse struct { +type MsgAssignRolesResponse struct { } -func (m *MsgUnAuthorizeAddressResponse) Reset() { *m = MsgUnAuthorizeAddressResponse{} } -func (m *MsgUnAuthorizeAddressResponse) String() string { return proto.CompactTextString(m) } -func (*MsgUnAuthorizeAddressResponse) ProtoMessage() {} -func (*MsgUnAuthorizeAddressResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_1cfda60866e68e13, []int{7} +func (m *MsgAssignRolesResponse) Reset() { *m = MsgAssignRolesResponse{} } +func (m *MsgAssignRolesResponse) String() string { return proto.CompactTextString(m) } +func (*MsgAssignRolesResponse) ProtoMessage() {} +func (*MsgAssignRolesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1cfda60866e68e13, []int{3} } -func (m *MsgUnAuthorizeAddressResponse) XXX_Unmarshal(b []byte) error { +func (m *MsgAssignRolesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *MsgUnAuthorizeAddressResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *MsgAssignRolesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_MsgUnAuthorizeAddressResponse.Marshal(b, m, deterministic) + return xxx_messageInfo_MsgAssignRolesResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -418,37 +262,37 @@ func (m *MsgUnAuthorizeAddressResponse) XXX_Marshal(b []byte, deterministic bool return b[:n], nil } } -func (m *MsgUnAuthorizeAddressResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgUnAuthorizeAddressResponse.Merge(m, src) +func (m *MsgAssignRolesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAssignRolesResponse.Merge(m, src) } -func (m *MsgUnAuthorizeAddressResponse) XXX_Size() int { +func (m *MsgAssignRolesResponse) XXX_Size() int { return m.Size() } -func (m *MsgUnAuthorizeAddressResponse) XXX_DiscardUnknown() { - xxx_messageInfo_MsgUnAuthorizeAddressResponse.DiscardUnknown(m) +func (m *MsgAssignRolesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAssignRolesResponse.DiscardUnknown(m) } -var xxx_messageInfo_MsgUnAuthorizeAddressResponse proto.InternalMessageInfo +var xxx_messageInfo_MsgAssignRolesResponse proto.InternalMessageInfo -type MsgTransferToken struct { - Symbol string `protobuf:"bytes,1,opt,name=symbol,proto3" json:"symbol,omitempty"` - From string `protobuf:"bytes,2,opt,name=from,proto3" json:"from,omitempty"` - To string `protobuf:"bytes,3,opt,name=to,proto3" json:"to,omitempty"` - Amount string `protobuf:"bytes,4,opt,name=amount,proto3" json:"amount,omitempty"` +type MsgUnassignRoles struct { + // issuer is the address that defines the token + Issuer []byte `protobuf:"bytes,1,opt,name=issuer,proto3" json:"issuer,omitempty"` + TokenId string `protobuf:"bytes,2,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` + Managers []byte `protobuf:"bytes,3,opt,name=managers,proto3" json:"managers,omitempty"` } -func (m *MsgTransferToken) Reset() { *m = MsgTransferToken{} } -func (m *MsgTransferToken) String() string { return proto.CompactTextString(m) } -func (*MsgTransferToken) ProtoMessage() {} -func (*MsgTransferToken) Descriptor() ([]byte, []int) { - return fileDescriptor_1cfda60866e68e13, []int{8} +func (m *MsgUnassignRoles) Reset() { *m = MsgUnassignRoles{} } +func (m *MsgUnassignRoles) String() string { return proto.CompactTextString(m) } +func (*MsgUnassignRoles) ProtoMessage() {} +func (*MsgUnassignRoles) Descriptor() ([]byte, []int) { + return fileDescriptor_1cfda60866e68e13, []int{4} } -func (m *MsgTransferToken) XXX_Unmarshal(b []byte) error { +func (m *MsgUnassignRoles) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *MsgTransferToken) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *MsgUnassignRoles) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_MsgTransferToken.Marshal(b, m, deterministic) + return xxx_messageInfo_MsgUnassignRoles.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -458,61 +302,54 @@ func (m *MsgTransferToken) XXX_Marshal(b []byte, deterministic bool) ([]byte, er return b[:n], nil } } -func (m *MsgTransferToken) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgTransferToken.Merge(m, src) +func (m *MsgUnassignRoles) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUnassignRoles.Merge(m, src) } -func (m *MsgTransferToken) XXX_Size() int { +func (m *MsgUnassignRoles) XXX_Size() int { return m.Size() } -func (m *MsgTransferToken) XXX_DiscardUnknown() { - xxx_messageInfo_MsgTransferToken.DiscardUnknown(m) +func (m *MsgUnassignRoles) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUnassignRoles.DiscardUnknown(m) } -var xxx_messageInfo_MsgTransferToken proto.InternalMessageInfo - -func (m *MsgTransferToken) GetSymbol() string { - if m != nil { - return m.Symbol - } - return "" -} +var xxx_messageInfo_MsgUnassignRoles proto.InternalMessageInfo -func (m *MsgTransferToken) GetFrom() string { +func (m *MsgUnassignRoles) GetIssuer() []byte { if m != nil { - return m.From + return m.Issuer } - return "" + return nil } -func (m *MsgTransferToken) GetTo() string { +func (m *MsgUnassignRoles) GetTokenId() string { if m != nil { - return m.To + return m.TokenId } return "" } -func (m *MsgTransferToken) GetAmount() string { +func (m *MsgUnassignRoles) GetManagers() []byte { if m != nil { - return m.Amount + return m.Managers } - return "" + return nil } -type MsgTransferTokenResponse struct { +type MsgUnassignRolesResponse struct { } -func (m *MsgTransferTokenResponse) Reset() { *m = MsgTransferTokenResponse{} } -func (m *MsgTransferTokenResponse) String() string { return proto.CompactTextString(m) } -func (*MsgTransferTokenResponse) ProtoMessage() {} -func (*MsgTransferTokenResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_1cfda60866e68e13, []int{9} +func (m *MsgUnassignRolesResponse) Reset() { *m = MsgUnassignRolesResponse{} } +func (m *MsgUnassignRolesResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUnassignRolesResponse) ProtoMessage() {} +func (*MsgUnassignRolesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1cfda60866e68e13, []int{5} } -func (m *MsgTransferTokenResponse) XXX_Unmarshal(b []byte) error { +func (m *MsgUnassignRolesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *MsgTransferTokenResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *MsgUnassignRolesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_MsgTransferTokenResponse.Marshal(b, m, deterministic) + return xxx_messageInfo_MsgUnassignRolesResponse.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -522,17 +359,17 @@ func (m *MsgTransferTokenResponse) XXX_Marshal(b []byte, deterministic bool) ([] return b[:n], nil } } -func (m *MsgTransferTokenResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgTransferTokenResponse.Merge(m, src) +func (m *MsgUnassignRolesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUnassignRolesResponse.Merge(m, src) } -func (m *MsgTransferTokenResponse) XXX_Size() int { +func (m *MsgUnassignRolesResponse) XXX_Size() int { return m.Size() } -func (m *MsgTransferTokenResponse) XXX_DiscardUnknown() { - xxx_messageInfo_MsgTransferTokenResponse.DiscardUnknown(m) +func (m *MsgUnassignRolesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUnassignRolesResponse.DiscardUnknown(m) } -var xxx_messageInfo_MsgTransferTokenResponse proto.InternalMessageInfo +var xxx_messageInfo_MsgUnassignRolesResponse proto.InternalMessageInfo type MsgUpdateParams struct { // authority is the address that controls the module (defaults to x/gov unless @@ -548,7 +385,7 @@ func (m *MsgUpdateParams) Reset() { *m = MsgUpdateParams{} } func (m *MsgUpdateParams) String() string { return proto.CompactTextString(m) } func (*MsgUpdateParams) ProtoMessage() {} func (*MsgUpdateParams) Descriptor() ([]byte, []int) { - return fileDescriptor_1cfda60866e68e13, []int{10} + return fileDescriptor_1cfda60866e68e13, []int{6} } func (m *MsgUpdateParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -598,7 +435,7 @@ func (m *MsgUpdateParamsResponse) Reset() { *m = MsgUpdateParamsResponse func (m *MsgUpdateParamsResponse) String() string { return proto.CompactTextString(m) } func (*MsgUpdateParamsResponse) ProtoMessage() {} func (*MsgUpdateParamsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_1cfda60866e68e13, []int{11} + return fileDescriptor_1cfda60866e68e13, []int{7} } func (m *MsgUpdateParamsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -630,14 +467,10 @@ var xxx_messageInfo_MsgUpdateParamsResponse proto.InternalMessageInfo func init() { proto.RegisterType((*MsgCreateToken)(nil), "realionetwork.asset.v1.MsgCreateToken") proto.RegisterType((*MsgCreateTokenResponse)(nil), "realionetwork.asset.v1.MsgCreateTokenResponse") - proto.RegisterType((*MsgUpdateToken)(nil), "realionetwork.asset.v1.MsgUpdateToken") - proto.RegisterType((*MsgUpdateTokenResponse)(nil), "realionetwork.asset.v1.MsgUpdateTokenResponse") - proto.RegisterType((*MsgAuthorizeAddress)(nil), "realionetwork.asset.v1.MsgAuthorizeAddress") - proto.RegisterType((*MsgAuthorizeAddressResponse)(nil), "realionetwork.asset.v1.MsgAuthorizeAddressResponse") - proto.RegisterType((*MsgUnAuthorizeAddress)(nil), "realionetwork.asset.v1.MsgUnAuthorizeAddress") - proto.RegisterType((*MsgUnAuthorizeAddressResponse)(nil), "realionetwork.asset.v1.MsgUnAuthorizeAddressResponse") - proto.RegisterType((*MsgTransferToken)(nil), "realionetwork.asset.v1.MsgTransferToken") - proto.RegisterType((*MsgTransferTokenResponse)(nil), "realionetwork.asset.v1.MsgTransferTokenResponse") + proto.RegisterType((*MsgAssignRoles)(nil), "realionetwork.asset.v1.MsgAssignRoles") + proto.RegisterType((*MsgAssignRolesResponse)(nil), "realionetwork.asset.v1.MsgAssignRolesResponse") + proto.RegisterType((*MsgUnassignRoles)(nil), "realionetwork.asset.v1.MsgUnassignRoles") + proto.RegisterType((*MsgUnassignRolesResponse)(nil), "realionetwork.asset.v1.MsgUnassignRolesResponse") proto.RegisterType((*MsgUpdateParams)(nil), "realionetwork.asset.v1.MsgUpdateParams") proto.RegisterType((*MsgUpdateParamsResponse)(nil), "realionetwork.asset.v1.MsgUpdateParamsResponse") } @@ -645,47 +478,48 @@ func init() { func init() { proto.RegisterFile("realionetwork/asset/v1/tx.proto", fileDescriptor_1cfda60866e68e13) } var fileDescriptor_1cfda60866e68e13 = []byte{ - // 627 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x95, 0x4f, 0x4f, 0xdb, 0x30, - 0x18, 0xc6, 0x9b, 0x52, 0xca, 0x78, 0x61, 0x0c, 0x79, 0xfc, 0x09, 0xd9, 0x08, 0xa8, 0x93, 0x18, - 0x62, 0x22, 0x19, 0xb0, 0x5d, 0xd0, 0x2e, 0xb0, 0xeb, 0x2a, 0x4d, 0x15, 0xbb, 0xec, 0x32, 0xb9, - 0xad, 0x9b, 0x56, 0x6d, 0xe2, 0x62, 0xbb, 0x8c, 0x22, 0x4d, 0x9a, 0x76, 0xd8, 0x79, 0xdf, 0x61, - 0x5f, 0xa0, 0x1f, 0x83, 0x23, 0xc7, 0x9d, 0xa6, 0xa9, 0x3d, 0xf0, 0x35, 0xa6, 0xc4, 0x4e, 0xda, - 0x94, 0xb4, 0xb4, 0x3b, 0xec, 0xe6, 0xd7, 0x7e, 0xde, 0xf7, 0xf9, 0xc5, 0xf6, 0x1b, 0xc3, 0x16, - 0x23, 0xb8, 0x51, 0xa3, 0x1e, 0x11, 0x9f, 0x29, 0xab, 0xdb, 0x98, 0x73, 0x22, 0xec, 0x8b, 0x03, - 0x5b, 0x5c, 0x5a, 0x4d, 0x46, 0x05, 0x45, 0x6b, 0x31, 0x81, 0x15, 0x08, 0xac, 0x8b, 0x03, 0x63, - 0xc5, 0xa1, 0x0e, 0x0d, 0x24, 0xb6, 0x3f, 0x92, 0x6a, 0xe3, 0xd9, 0x88, 0x72, 0x4d, 0xcc, 0xb0, - 0xcb, 0x95, 0x68, 0xa3, 0x44, 0xb9, 0x4b, 0xf9, 0x27, 0x99, 0x2d, 0x03, 0xb5, 0xb4, 0x2e, 0x23, - 0xdb, 0xe5, 0x8e, 0x9f, 0xe6, 0x72, 0x47, 0x2e, 0xe4, 0x3a, 0x1a, 0x2c, 0xe5, 0xb9, 0xf3, 0x96, - 0x11, 0x2c, 0xc8, 0x19, 0xad, 0x13, 0x0f, 0xe9, 0x30, 0xe7, 0x62, 0x0f, 0x3b, 0x84, 0xe9, 0xda, - 0xb6, 0xb6, 0x3b, 0x5f, 0x08, 0x43, 0x84, 0x20, 0xe3, 0x61, 0x97, 0xe8, 0xe9, 0x60, 0x3a, 0x18, - 0xa3, 0x35, 0xc8, 0xf2, 0xb6, 0x5b, 0xa4, 0x0d, 0x7d, 0x26, 0x98, 0x55, 0x11, 0x5a, 0x81, 0x59, - 0x41, 0x05, 0x6e, 0xe8, 0x99, 0x60, 0x5a, 0x06, 0xe8, 0x15, 0xac, 0xe2, 0x96, 0xa8, 0x52, 0x56, - 0xbb, 0xc2, 0xa2, 0x46, 0xbd, 0x02, 0x39, 0x6f, 0xd5, 0x18, 0x29, 0xeb, 0xd9, 0x6d, 0x6d, 0xf7, - 0x41, 0x21, 0x79, 0xf1, 0x78, 0xf1, 0xdb, 0x6d, 0x67, 0x2f, 0xa4, 0xc8, 0xe9, 0xb0, 0x16, 0x27, - 0x2e, 0x10, 0xde, 0xa4, 0x1e, 0x27, 0xb9, 0xef, 0xf2, 0x63, 0x3e, 0x34, 0xcb, 0x13, 0x7c, 0x4c, - 0x1f, 0x3c, 0x1d, 0x03, 0x1f, 0x89, 0x38, 0x33, 0x2d, 0xe2, 0x00, 0x47, 0x84, 0x48, 0xe1, 0x71, - 0x9e, 0x3b, 0x27, 0xaa, 0x06, 0x39, 0x29, 0x97, 0x19, 0xe1, 0xfc, 0x1f, 0x30, 0x75, 0x98, 0xc3, - 0x32, 0x59, 0x6d, 0x7c, 0x18, 0x0e, 0xa1, 0x6c, 0xc2, 0x93, 0x04, 0xc3, 0x88, 0xe7, 0x1c, 0x56, - 0x7d, 0x52, 0xef, 0x3f, 0x12, 0x6d, 0xc1, 0x66, 0xa2, 0x65, 0xc4, 0x54, 0x81, 0xe5, 0x3c, 0x77, - 0xce, 0x18, 0xf6, 0x78, 0x85, 0x30, 0x79, 0x8e, 0x7d, 0x53, 0x2d, 0x66, 0x8a, 0x20, 0x53, 0x61, - 0xd4, 0x0d, 0xaf, 0xa4, 0x3f, 0x46, 0x4b, 0x90, 0x16, 0x54, 0x31, 0xa4, 0xfd, 0x56, 0x83, 0x2c, - 0x76, 0x69, 0xcb, 0x13, 0xea, 0x2e, 0xaa, 0x28, 0x67, 0x80, 0x3e, 0xec, 0x13, 0x31, 0x7c, 0x81, - 0x47, 0xd1, 0x09, 0xbe, 0x0f, 0x9a, 0x0c, 0x3d, 0x85, 0x79, 0x75, 0xf6, 0xa2, 0xad, 0x28, 0xfa, - 0x13, 0xe8, 0x0d, 0x64, 0x65, 0x33, 0x06, 0x28, 0x0b, 0x87, 0xa6, 0x95, 0xdc, 0xe0, 0x96, 0xac, - 0x76, 0x9a, 0xb9, 0xfe, 0xbd, 0x95, 0x2a, 0xa8, 0x9c, 0xe3, 0x25, 0x7f, 0x87, 0xfa, 0xd5, 0x72, - 0x1b, 0xb0, 0x3e, 0x64, 0x1f, 0x92, 0x1d, 0xfe, 0x9c, 0x85, 0x99, 0x3c, 0x77, 0x50, 0x15, 0x16, - 0x63, 0x78, 0xcf, 0x47, 0x19, 0x0e, 0x15, 0x32, 0xec, 0x09, 0x85, 0xa1, 0x23, 0x22, 0xb0, 0x30, - 0xf8, 0x7f, 0xd8, 0x19, 0x93, 0x3f, 0xa0, 0x33, 0xac, 0xc9, 0x74, 0x83, 0x36, 0x83, 0x9d, 0xbb, - 0x73, 0x2f, 0xe6, 0xfd, 0x36, 0x09, 0x1d, 0x88, 0x04, 0x2c, 0xdf, 0xb9, 0xec, 0x2f, 0xc6, 0xd4, - 0x18, 0x16, 0x1b, 0x47, 0x53, 0x88, 0x23, 0xd7, 0x2b, 0x40, 0x09, 0x4d, 0xb6, 0x3f, 0x8e, 0xfd, - 0x8e, 0xdc, 0x78, 0x3d, 0x95, 0x3c, 0xf2, 0xae, 0xc3, 0xc3, 0x78, 0x33, 0xed, 0x8e, 0xa9, 0x13, - 0x53, 0x1a, 0x2f, 0x27, 0x55, 0x86, 0x66, 0xc6, 0xec, 0xd7, 0xdb, 0xce, 0x9e, 0x76, 0xfa, 0xee, - 0xba, 0x6b, 0x6a, 0x37, 0x5d, 0x53, 0xfb, 0xd3, 0x35, 0xb5, 0x1f, 0x3d, 0x33, 0x75, 0xd3, 0x33, - 0x53, 0xbf, 0x7a, 0x66, 0xea, 0xe3, 0xa1, 0x53, 0x13, 0xd5, 0x56, 0xd1, 0x2a, 0x51, 0xd7, 0x96, - 0xc5, 0x05, 0x29, 0x55, 0xd5, 0x70, 0x3f, 0x7c, 0xe1, 0x2e, 0xd5, 0x1b, 0x27, 0xda, 0x4d, 0xc2, - 0x8b, 0xd9, 0xe0, 0xb1, 0x3a, 0xfa, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x7c, 0x60, 0x84, 0x13, 0x56, - 0x07, 0x00, 0x00, + // 654 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x54, 0xcd, 0x4e, 0xdb, 0x4c, + 0x14, 0x8d, 0x09, 0x84, 0x64, 0xc2, 0xcf, 0xa7, 0x11, 0x1f, 0x18, 0xab, 0x35, 0x56, 0x2a, 0x15, + 0x0b, 0xa9, 0x36, 0x3f, 0x3b, 0xc4, 0xa6, 0x54, 0x5d, 0x20, 0x41, 0x55, 0xb9, 0xed, 0xa6, 0x9b, + 0x68, 0x48, 0x46, 0xce, 0x28, 0xf6, 0x8c, 0xe5, 0x3b, 0x21, 0xc9, 0xa2, 0x52, 0xd5, 0x27, 0xe8, + 0xa3, 0xd0, 0xb7, 0x60, 0x89, 0xba, 0xaa, 0xba, 0x40, 0x15, 0x2c, 0x78, 0x8d, 0xca, 0x63, 0x27, + 0xb1, 0x11, 0x89, 0x58, 0x75, 0x37, 0x67, 0xee, 0x99, 0x73, 0x8e, 0xee, 0xcc, 0x1d, 0xb4, 0x15, + 0x53, 0x12, 0x30, 0xc1, 0xa9, 0xec, 0x8b, 0xb8, 0xeb, 0x12, 0x00, 0x2a, 0xdd, 0x8b, 0x3d, 0x57, + 0x0e, 0x9c, 0x28, 0x16, 0x52, 0xe0, 0xf5, 0x02, 0xc1, 0x51, 0x04, 0xe7, 0x62, 0xcf, 0x58, 0xf3, + 0x85, 0x2f, 0x14, 0xc5, 0x4d, 0x56, 0x29, 0xdb, 0x78, 0x31, 0x45, 0x2e, 0x22, 0x31, 0x09, 0x21, + 0x23, 0x35, 0xa6, 0x79, 0x8a, 0x2e, 0xe5, 0x19, 0x67, 0xb3, 0x25, 0x20, 0x14, 0xd0, 0x4c, 0x1d, + 0x52, 0x90, 0x95, 0x36, 0x52, 0xe4, 0x86, 0xe0, 0x27, 0xa7, 0x42, 0xf0, 0xd3, 0x42, 0xe3, 0xe7, + 0x1c, 0x5a, 0x39, 0x03, 0xff, 0x4d, 0x4c, 0x89, 0xa4, 0x1f, 0x13, 0x31, 0xbc, 0x8e, 0x2a, 0x0c, + 0xa0, 0x47, 0x63, 0x5d, 0xb3, 0x34, 0x7b, 0xc9, 0xcb, 0x10, 0xc6, 0x68, 0x9e, 0x93, 0x90, 0xea, + 0x73, 0x96, 0x66, 0xd7, 0x3c, 0xb5, 0xc6, 0x3a, 0x5a, 0x6c, 0xd3, 0x16, 0x0b, 0x49, 0xa0, 0x97, + 0x2d, 0xcd, 0x5e, 0xf6, 0x46, 0x30, 0x51, 0x81, 0x61, 0x78, 0x2e, 0x02, 0x7d, 0x5e, 0xf1, 0x33, + 0x84, 0x2d, 0x54, 0x6f, 0x53, 0x68, 0xc5, 0x2c, 0x92, 0x4c, 0x70, 0x7d, 0x41, 0x15, 0xf3, 0x5b, + 0xd8, 0x40, 0xd5, 0x90, 0x70, 0xe2, 0xd3, 0x18, 0xf4, 0x8a, 0x55, 0xb6, 0x97, 0xbc, 0x31, 0xc6, + 0xbb, 0x68, 0x8d, 0x04, 0x81, 0xe8, 0x37, 0x39, 0xed, 0x37, 0xe9, 0x40, 0x52, 0x0e, 0x4c, 0x70, + 0xd0, 0xab, 0x96, 0x66, 0x57, 0x3d, 0xac, 0x6a, 0xef, 0x68, 0xff, 0xed, 0xb8, 0x82, 0xb7, 0xd1, + 0xea, 0x84, 0xd7, 0x0c, 0x18, 0x48, 0xbd, 0x66, 0x95, 0xed, 0x9a, 0xb7, 0x32, 0xd9, 0x3e, 0x65, + 0x20, 0xf1, 0x11, 0x42, 0x21, 0x19, 0x34, 0xa1, 0x17, 0x45, 0xc1, 0x50, 0x47, 0x49, 0xae, 0xe3, + 0xe7, 0x57, 0x37, 0x5b, 0xa5, 0xdf, 0x37, 0x5b, 0xff, 0xa7, 0xed, 0x83, 0x76, 0xd7, 0x61, 0xc2, + 0x0d, 0x89, 0xec, 0x38, 0x27, 0x5c, 0x7a, 0xb5, 0x90, 0x0c, 0x3e, 0x28, 0xfe, 0x61, 0xfd, 0xdb, + 0xfd, 0xe5, 0x4e, 0xd6, 0xa9, 0xc6, 0x01, 0x5a, 0x2f, 0xf6, 0xd4, 0xa3, 0x10, 0x09, 0x0e, 0x14, + 0x6f, 0xa2, 0xaa, 0xba, 0xb1, 0x26, 0x6b, 0xab, 0xee, 0xd6, 0xbc, 0x45, 0x85, 0x4f, 0xda, 0x8d, + 0x40, 0x5d, 0xc4, 0x6b, 0x00, 0xe6, 0x73, 0x4f, 0x04, 0x14, 0xa6, 0x5e, 0x44, 0x5e, 0x64, 0xae, + 0x20, 0x52, 0xe8, 0x5d, 0x59, 0x1d, 0x1a, 0xe3, 0x62, 0x44, 0x5d, 0x45, 0xcc, 0xb9, 0x8d, 0x22, + 0x36, 0x38, 0xfa, 0xef, 0x0c, 0xfc, 0x4f, 0x9c, 0xfc, 0xa3, 0x24, 0x06, 0xd2, 0x1f, 0xfa, 0x8d, + 0xb3, 0x7c, 0x41, 0xab, 0x49, 0x2d, 0x6a, 0x13, 0x49, 0xdf, 0xab, 0x71, 0xc0, 0xcf, 0x50, 0x8d, + 0xf4, 0x64, 0x47, 0xc4, 0x4c, 0x0e, 0xb3, 0x16, 0x4e, 0x36, 0xf0, 0x11, 0xaa, 0xa4, 0x63, 0xa3, + 0xe2, 0xd4, 0xf7, 0x4d, 0xe7, 0xf1, 0x51, 0x74, 0x52, 0xb5, 0xe3, 0xf9, 0xe4, 0x82, 0xbd, 0xec, + 0xcc, 0xe1, 0x4a, 0x92, 0x6b, 0xa2, 0xd6, 0xd8, 0x44, 0x1b, 0x0f, 0xec, 0x47, 0xc9, 0xf6, 0x7f, + 0x94, 0x51, 0xf9, 0x0c, 0x7c, 0xdc, 0x41, 0x4b, 0x85, 0x78, 0xdb, 0xd3, 0x0c, 0x1f, 0x08, 0x19, + 0xee, 0x13, 0x89, 0xe3, 0xa7, 0x43, 0x51, 0x3d, 0x3f, 0xa5, 0x2f, 0x67, 0x9c, 0xcf, 0xf1, 0x0c, + 0xe7, 0x69, 0xbc, 0xbc, 0x4d, 0xfe, 0x0d, 0xce, 0xb2, 0xc9, 0xf1, 0x66, 0xda, 0x3c, 0xf2, 0xca, + 0x70, 0x17, 0x2d, 0x17, 0x9f, 0x98, 0x3d, 0xab, 0x1f, 0x79, 0xa6, 0xb1, 0xfb, 0x54, 0xe6, 0xc8, + 0xcc, 0x58, 0xf8, 0x7a, 0x7f, 0xb9, 0xa3, 0x1d, 0x9f, 0x5e, 0xdd, 0x9a, 0xda, 0xf5, 0xad, 0xa9, + 0xfd, 0xb9, 0x35, 0xb5, 0xef, 0x77, 0x66, 0xe9, 0xfa, 0xce, 0x2c, 0xfd, 0xba, 0x33, 0x4b, 0x9f, + 0xf7, 0x7d, 0x26, 0x3b, 0xbd, 0x73, 0xa7, 0x25, 0x42, 0x37, 0x15, 0x97, 0xb4, 0xd5, 0xc9, 0x96, + 0xaf, 0x46, 0x9f, 0xee, 0x20, 0xfb, 0x76, 0xe5, 0x30, 0xa2, 0x70, 0x5e, 0x51, 0x1f, 0xe8, 0xc1, + 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0b, 0xc2, 0xbd, 0xcf, 0x0e, 0x06, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -701,11 +535,10 @@ const _ = grpc.SupportPackageIsVersion4 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type MsgClient interface { UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) + // this line is used by starport scaffolding # proto/tx/rpc CreateToken(ctx context.Context, in *MsgCreateToken, opts ...grpc.CallOption) (*MsgCreateTokenResponse, error) - UpdateToken(ctx context.Context, in *MsgUpdateToken, opts ...grpc.CallOption) (*MsgUpdateTokenResponse, error) - AuthorizeAddress(ctx context.Context, in *MsgAuthorizeAddress, opts ...grpc.CallOption) (*MsgAuthorizeAddressResponse, error) - UnAuthorizeAddress(ctx context.Context, in *MsgUnAuthorizeAddress, opts ...grpc.CallOption) (*MsgUnAuthorizeAddressResponse, error) - TransferToken(ctx context.Context, in *MsgTransferToken, opts ...grpc.CallOption) (*MsgTransferTokenResponse, error) + AssignRoles(ctx context.Context, in *MsgAssignRoles, opts ...grpc.CallOption) (*MsgAssignRolesResponse, error) + UnassignRoles(ctx context.Context, in *MsgUnassignRoles, opts ...grpc.CallOption) (*MsgUnassignRolesResponse, error) } type msgClient struct { @@ -734,36 +567,18 @@ func (c *msgClient) CreateToken(ctx context.Context, in *MsgCreateToken, opts .. return out, nil } -func (c *msgClient) UpdateToken(ctx context.Context, in *MsgUpdateToken, opts ...grpc.CallOption) (*MsgUpdateTokenResponse, error) { - out := new(MsgUpdateTokenResponse) - err := c.cc.Invoke(ctx, "/realionetwork.asset.v1.Msg/UpdateToken", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *msgClient) AuthorizeAddress(ctx context.Context, in *MsgAuthorizeAddress, opts ...grpc.CallOption) (*MsgAuthorizeAddressResponse, error) { - out := new(MsgAuthorizeAddressResponse) - err := c.cc.Invoke(ctx, "/realionetwork.asset.v1.Msg/AuthorizeAddress", in, out, opts...) +func (c *msgClient) AssignRoles(ctx context.Context, in *MsgAssignRoles, opts ...grpc.CallOption) (*MsgAssignRolesResponse, error) { + out := new(MsgAssignRolesResponse) + err := c.cc.Invoke(ctx, "/realionetwork.asset.v1.Msg/AssignRoles", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *msgClient) UnAuthorizeAddress(ctx context.Context, in *MsgUnAuthorizeAddress, opts ...grpc.CallOption) (*MsgUnAuthorizeAddressResponse, error) { - out := new(MsgUnAuthorizeAddressResponse) - err := c.cc.Invoke(ctx, "/realionetwork.asset.v1.Msg/UnAuthorizeAddress", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *msgClient) TransferToken(ctx context.Context, in *MsgTransferToken, opts ...grpc.CallOption) (*MsgTransferTokenResponse, error) { - out := new(MsgTransferTokenResponse) - err := c.cc.Invoke(ctx, "/realionetwork.asset.v1.Msg/TransferToken", in, out, opts...) +func (c *msgClient) UnassignRoles(ctx context.Context, in *MsgUnassignRoles, opts ...grpc.CallOption) (*MsgUnassignRolesResponse, error) { + out := new(MsgUnassignRolesResponse) + err := c.cc.Invoke(ctx, "/realionetwork.asset.v1.Msg/UnassignRoles", in, out, opts...) if err != nil { return nil, err } @@ -773,11 +588,10 @@ func (c *msgClient) TransferToken(ctx context.Context, in *MsgTransferToken, opt // MsgServer is the server API for Msg service. type MsgServer interface { UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) + // this line is used by starport scaffolding # proto/tx/rpc CreateToken(context.Context, *MsgCreateToken) (*MsgCreateTokenResponse, error) - UpdateToken(context.Context, *MsgUpdateToken) (*MsgUpdateTokenResponse, error) - AuthorizeAddress(context.Context, *MsgAuthorizeAddress) (*MsgAuthorizeAddressResponse, error) - UnAuthorizeAddress(context.Context, *MsgUnAuthorizeAddress) (*MsgUnAuthorizeAddressResponse, error) - TransferToken(context.Context, *MsgTransferToken) (*MsgTransferTokenResponse, error) + AssignRoles(context.Context, *MsgAssignRoles) (*MsgAssignRolesResponse, error) + UnassignRoles(context.Context, *MsgUnassignRoles) (*MsgUnassignRolesResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -790,17 +604,11 @@ func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateP func (*UnimplementedMsgServer) CreateToken(ctx context.Context, req *MsgCreateToken) (*MsgCreateTokenResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateToken not implemented") } -func (*UnimplementedMsgServer) UpdateToken(ctx context.Context, req *MsgUpdateToken) (*MsgUpdateTokenResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method UpdateToken not implemented") -} -func (*UnimplementedMsgServer) AuthorizeAddress(ctx context.Context, req *MsgAuthorizeAddress) (*MsgAuthorizeAddressResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method AuthorizeAddress not implemented") -} -func (*UnimplementedMsgServer) UnAuthorizeAddress(ctx context.Context, req *MsgUnAuthorizeAddress) (*MsgUnAuthorizeAddressResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method UnAuthorizeAddress not implemented") +func (*UnimplementedMsgServer) AssignRoles(ctx context.Context, req *MsgAssignRoles) (*MsgAssignRolesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AssignRoles not implemented") } -func (*UnimplementedMsgServer) TransferToken(ctx context.Context, req *MsgTransferToken) (*MsgTransferTokenResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method TransferToken not implemented") +func (*UnimplementedMsgServer) UnassignRoles(ctx context.Context, req *MsgUnassignRoles) (*MsgUnassignRolesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UnassignRoles not implemented") } func RegisterMsgServer(s grpc1.Server, srv MsgServer) { @@ -843,74 +651,38 @@ func _Msg_CreateToken_Handler(srv interface{}, ctx context.Context, dec func(int return interceptor(ctx, in, info, handler) } -func _Msg_UpdateToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MsgUpdateToken) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MsgServer).UpdateToken(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/realionetwork.asset.v1.Msg/UpdateToken", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).UpdateToken(ctx, req.(*MsgUpdateToken)) - } - return interceptor(ctx, in, info, handler) -} - -func _Msg_AuthorizeAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MsgAuthorizeAddress) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MsgServer).AuthorizeAddress(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/realionetwork.asset.v1.Msg/AuthorizeAddress", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).AuthorizeAddress(ctx, req.(*MsgAuthorizeAddress)) - } - return interceptor(ctx, in, info, handler) -} - -func _Msg_UnAuthorizeAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MsgUnAuthorizeAddress) +func _Msg_AssignRoles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgAssignRoles) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(MsgServer).UnAuthorizeAddress(ctx, in) + return srv.(MsgServer).AssignRoles(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/realionetwork.asset.v1.Msg/UnAuthorizeAddress", + FullMethod: "/realionetwork.asset.v1.Msg/AssignRoles", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).UnAuthorizeAddress(ctx, req.(*MsgUnAuthorizeAddress)) + return srv.(MsgServer).AssignRoles(ctx, req.(*MsgAssignRoles)) } return interceptor(ctx, in, info, handler) } -func _Msg_TransferToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MsgTransferToken) +func _Msg_UnassignRoles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUnassignRoles) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(MsgServer).TransferToken(ctx, in) + return srv.(MsgServer).UnassignRoles(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/realionetwork.asset.v1.Msg/TransferToken", + FullMethod: "/realionetwork.asset.v1.Msg/UnassignRoles", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).TransferToken(ctx, req.(*MsgTransferToken)) + return srv.(MsgServer).UnassignRoles(ctx, req.(*MsgUnassignRoles)) } return interceptor(ctx, in, info, handler) } @@ -928,20 +700,12 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ Handler: _Msg_CreateToken_Handler, }, { - MethodName: "UpdateToken", - Handler: _Msg_UpdateToken_Handler, - }, - { - MethodName: "AuthorizeAddress", - Handler: _Msg_AuthorizeAddress_Handler, + MethodName: "AssignRoles", + Handler: _Msg_AssignRoles_Handler, }, { - MethodName: "UnAuthorizeAddress", - Handler: _Msg_UnAuthorizeAddress_Handler, - }, - { - MethodName: "TransferToken", - Handler: _Msg_TransferToken_Handler, + MethodName: "UnassignRoles", + Handler: _Msg_UnassignRoles_Handler, }, }, Streams: []grpc.StreamDesc{}, @@ -968,29 +732,62 @@ func (m *MsgCreateToken) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.AuthorizationRequired { + { + size := m.MaxSupply.Size() + i -= size + if _, err := m.MaxSupply.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + if len(m.ExtensionsList) > 0 { + for iNdEx := len(m.ExtensionsList) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ExtensionsList[iNdEx]) + copy(dAtA[i:], m.ExtensionsList[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.ExtensionsList[iNdEx]))) + i-- + dAtA[i] = 0x4a + } + } + if m.AllowNewExtensions { i-- - if m.AuthorizationRequired { + if m.AllowNewExtensions { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- - dAtA[i] = 0x30 + dAtA[i] = 0x40 + } + if len(m.Managers) > 0 { + for iNdEx := len(m.Managers) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Managers[iNdEx]) + copy(dAtA[i:], m.Managers[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.Managers[iNdEx]))) + i-- + dAtA[i] = 0x32 + } } - if len(m.Total) > 0 { - i -= len(m.Total) - copy(dAtA[i:], m.Total) - i = encodeVarintTx(dAtA, i, uint64(len(m.Total))) + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintTx(dAtA, i, uint64(len(m.Description))) i-- - dAtA[i] = 0x22 + dAtA[i] = 0x2a } if len(m.Symbol) > 0 { i -= len(m.Symbol) copy(dAtA[i:], m.Symbol) i = encodeVarintTx(dAtA, i, uint64(len(m.Symbol))) i-- - dAtA[i] = 0x1a + dAtA[i] = 0x22 + } + if m.Decimal != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Decimal)) + i-- + dAtA[i] = 0x18 } if len(m.Name) > 0 { i -= len(m.Name) @@ -999,10 +796,10 @@ func (m *MsgCreateToken) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x12 } - if len(m.Manager) > 0 { - i -= len(m.Manager) - copy(dAtA[i:], m.Manager) - i = encodeVarintTx(dAtA, i, uint64(len(m.Manager))) + if len(m.Issuer) > 0 { + i -= len(m.Issuer) + copy(dAtA[i:], m.Issuer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Issuer))) i-- dAtA[i] = 0xa } @@ -1029,10 +826,17 @@ func (m *MsgCreateTokenResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l + if len(m.TokenId) > 0 { + i -= len(m.TokenId) + copy(dAtA[i:], m.TokenId) + i = encodeVarintTx(dAtA, i, uint64(len(m.TokenId))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } -func (m *MsgUpdateToken) Marshal() (dAtA []byte, err error) { +func (m *MsgAssignRoles) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1042,44 +846,41 @@ func (m *MsgUpdateToken) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *MsgUpdateToken) MarshalTo(dAtA []byte) (int, error) { +func (m *MsgAssignRoles) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *MsgUpdateToken) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *MsgAssignRoles) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - if m.AuthorizationRequired { - i-- - if m.AuthorizationRequired { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } + if len(m.Managers) > 0 { + i -= len(m.Managers) + copy(dAtA[i:], m.Managers) + i = encodeVarintTx(dAtA, i, uint64(len(m.Managers))) i-- - dAtA[i] = 0x18 + dAtA[i] = 0x1a } - if len(m.Symbol) > 0 { - i -= len(m.Symbol) - copy(dAtA[i:], m.Symbol) - i = encodeVarintTx(dAtA, i, uint64(len(m.Symbol))) + if len(m.TokenId) > 0 { + i -= len(m.TokenId) + copy(dAtA[i:], m.TokenId) + i = encodeVarintTx(dAtA, i, uint64(len(m.TokenId))) i-- dAtA[i] = 0x12 } - if len(m.Manager) > 0 { - i -= len(m.Manager) - copy(dAtA[i:], m.Manager) - i = encodeVarintTx(dAtA, i, uint64(len(m.Manager))) + if len(m.Issuer) > 0 { + i -= len(m.Issuer) + copy(dAtA[i:], m.Issuer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Issuer))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } -func (m *MsgUpdateTokenResponse) Marshal() (dAtA []byte, err error) { +func (m *MsgAssignRolesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1089,12 +890,12 @@ func (m *MsgUpdateTokenResponse) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *MsgUpdateTokenResponse) MarshalTo(dAtA []byte) (int, error) { +func (m *MsgAssignRolesResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *MsgUpdateTokenResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *MsgAssignRolesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -1102,7 +903,7 @@ func (m *MsgUpdateTokenResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } -func (m *MsgAuthorizeAddress) Marshal() (dAtA []byte, err error) { +func (m *MsgUnassignRoles) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1112,41 +913,41 @@ func (m *MsgAuthorizeAddress) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *MsgAuthorizeAddress) MarshalTo(dAtA []byte) (int, error) { +func (m *MsgUnassignRoles) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *MsgAuthorizeAddress) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *MsgUnassignRoles) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - if len(m.Address) > 0 { - i -= len(m.Address) - copy(dAtA[i:], m.Address) - i = encodeVarintTx(dAtA, i, uint64(len(m.Address))) + if len(m.Managers) > 0 { + i -= len(m.Managers) + copy(dAtA[i:], m.Managers) + i = encodeVarintTx(dAtA, i, uint64(len(m.Managers))) i-- dAtA[i] = 0x1a } - if len(m.Symbol) > 0 { - i -= len(m.Symbol) - copy(dAtA[i:], m.Symbol) - i = encodeVarintTx(dAtA, i, uint64(len(m.Symbol))) + if len(m.TokenId) > 0 { + i -= len(m.TokenId) + copy(dAtA[i:], m.TokenId) + i = encodeVarintTx(dAtA, i, uint64(len(m.TokenId))) i-- dAtA[i] = 0x12 } - if len(m.Manager) > 0 { - i -= len(m.Manager) - copy(dAtA[i:], m.Manager) - i = encodeVarintTx(dAtA, i, uint64(len(m.Manager))) + if len(m.Issuer) > 0 { + i -= len(m.Issuer) + copy(dAtA[i:], m.Issuer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Issuer))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } -func (m *MsgAuthorizeAddressResponse) Marshal() (dAtA []byte, err error) { +func (m *MsgUnassignRolesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1156,12 +957,12 @@ func (m *MsgAuthorizeAddressResponse) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *MsgAuthorizeAddressResponse) MarshalTo(dAtA []byte) (int, error) { +func (m *MsgUnassignRolesResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *MsgAuthorizeAddressResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *MsgUnassignRolesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -1169,7 +970,7 @@ func (m *MsgAuthorizeAddressResponse) MarshalToSizedBuffer(dAtA []byte) (int, er return len(dAtA) - i, nil } -func (m *MsgUnAuthorizeAddress) Marshal() (dAtA []byte, err error) { +func (m *MsgUpdateParams) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1179,171 +980,30 @@ func (m *MsgUnAuthorizeAddress) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *MsgUnAuthorizeAddress) MarshalTo(dAtA []byte) (int, error) { +func (m *MsgUpdateParams) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *MsgUnAuthorizeAddress) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *MsgUpdateParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - if len(m.Address) > 0 { - i -= len(m.Address) - copy(dAtA[i:], m.Address) - i = encodeVarintTx(dAtA, i, uint64(len(m.Address))) - i-- - dAtA[i] = 0x1a + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) } - if len(m.Symbol) > 0 { - i -= len(m.Symbol) - copy(dAtA[i:], m.Symbol) - i = encodeVarintTx(dAtA, i, uint64(len(m.Symbol))) - i-- - dAtA[i] = 0x12 - } - if len(m.Manager) > 0 { - i -= len(m.Manager) - copy(dAtA[i:], m.Manager) - i = encodeVarintTx(dAtA, i, uint64(len(m.Manager))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *MsgUnAuthorizeAddressResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *MsgUnAuthorizeAddressResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *MsgUnAuthorizeAddressResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - return len(dAtA) - i, nil -} - -func (m *MsgTransferToken) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *MsgTransferToken) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *MsgTransferToken) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Amount) > 0 { - i -= len(m.Amount) - copy(dAtA[i:], m.Amount) - i = encodeVarintTx(dAtA, i, uint64(len(m.Amount))) - i-- - dAtA[i] = 0x22 - } - if len(m.To) > 0 { - i -= len(m.To) - copy(dAtA[i:], m.To) - i = encodeVarintTx(dAtA, i, uint64(len(m.To))) - i-- - dAtA[i] = 0x1a - } - if len(m.From) > 0 { - i -= len(m.From) - copy(dAtA[i:], m.From) - i = encodeVarintTx(dAtA, i, uint64(len(m.From))) - i-- - dAtA[i] = 0x12 - } - if len(m.Symbol) > 0 { - i -= len(m.Symbol) - copy(dAtA[i:], m.Symbol) - i = encodeVarintTx(dAtA, i, uint64(len(m.Symbol))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *MsgTransferTokenResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *MsgTransferTokenResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *MsgTransferTokenResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - return len(dAtA) - i, nil -} - -func (m *MsgUpdateParams) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *MsgUpdateParams) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *MsgUpdateParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - { - size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTx(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - if len(m.Authority) > 0 { - i -= len(m.Authority) - copy(dAtA[i:], m.Authority) - i = encodeVarintTx(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0x12 + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintTx(dAtA, i, uint64(len(m.Authority))) i-- dAtA[i] = 0xa } @@ -1390,7 +1050,7 @@ func (m *MsgCreateToken) Size() (n int) { } var l int _ = l - l = len(m.Manager) + l = len(m.Issuer) if l > 0 { n += 1 + l + sovTx(uint64(l)) } @@ -1398,110 +1058,72 @@ func (m *MsgCreateToken) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } - l = len(m.Symbol) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if m.Decimal != 0 { + n += 1 + sovTx(uint64(m.Decimal)) } - l = len(m.Total) + l = len(m.Symbol) if l > 0 { n += 1 + l + sovTx(uint64(l)) } - if m.AuthorizationRequired { - n += 2 - } - return n -} - -func (m *MsgCreateTokenResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - return n -} - -func (m *MsgUpdateToken) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Manager) + l = len(m.Description) if l > 0 { n += 1 + l + sovTx(uint64(l)) } - l = len(m.Symbol) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) + if len(m.Managers) > 0 { + for _, b := range m.Managers { + l = len(b) + n += 1 + l + sovTx(uint64(l)) + } } - if m.AuthorizationRequired { + if m.AllowNewExtensions { n += 2 } - return n -} - -func (m *MsgUpdateTokenResponse) Size() (n int) { - if m == nil { - return 0 + if len(m.ExtensionsList) > 0 { + for _, s := range m.ExtensionsList { + l = len(s) + n += 1 + l + sovTx(uint64(l)) + } } - var l int - _ = l + l = m.MaxSupply.Size() + n += 1 + l + sovTx(uint64(l)) return n } -func (m *MsgAuthorizeAddress) Size() (n int) { +func (m *MsgCreateTokenResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l - l = len(m.Manager) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) - } - l = len(m.Symbol) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) - } - l = len(m.Address) + l = len(m.TokenId) if l > 0 { n += 1 + l + sovTx(uint64(l)) } return n } -func (m *MsgAuthorizeAddressResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - return n -} - -func (m *MsgUnAuthorizeAddress) Size() (n int) { +func (m *MsgAssignRoles) Size() (n int) { if m == nil { return 0 } var l int _ = l - l = len(m.Manager) + l = len(m.Issuer) if l > 0 { n += 1 + l + sovTx(uint64(l)) } - l = len(m.Symbol) + l = len(m.TokenId) if l > 0 { n += 1 + l + sovTx(uint64(l)) } - l = len(m.Address) + l = len(m.Managers) if l > 0 { n += 1 + l + sovTx(uint64(l)) } return n } -func (m *MsgUnAuthorizeAddressResponse) Size() (n int) { +func (m *MsgAssignRolesResponse) Size() (n int) { if m == nil { return 0 } @@ -1510,32 +1132,28 @@ func (m *MsgUnAuthorizeAddressResponse) Size() (n int) { return n } -func (m *MsgTransferToken) Size() (n int) { +func (m *MsgUnassignRoles) Size() (n int) { if m == nil { return 0 } var l int _ = l - l = len(m.Symbol) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) - } - l = len(m.From) + l = len(m.Issuer) if l > 0 { n += 1 + l + sovTx(uint64(l)) } - l = len(m.To) + l = len(m.TokenId) if l > 0 { n += 1 + l + sovTx(uint64(l)) } - l = len(m.Amount) + l = len(m.Managers) if l > 0 { n += 1 + l + sovTx(uint64(l)) } return n } -func (m *MsgTransferTokenResponse) Size() (n int) { +func (m *MsgUnassignRolesResponse) Size() (n int) { if m == nil { return 0 } @@ -1605,9 +1223,9 @@ func (m *MsgCreateToken) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Manager", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Issuer", wireType) } - var stringLen uint64 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -1617,243 +1235,29 @@ func (m *MsgCreateToken) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if byteLen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthTx } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Manager = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Symbol", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Symbol = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Total", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Total = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 6: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationRequired", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.AuthorizationRequired = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipTx(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *MsgCreateTokenResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: MsgCreateTokenResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: MsgCreateTokenResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipTx(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTx - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *MsgUpdateToken) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: MsgUpdateToken: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: MsgUpdateToken: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Issuer = append(m.Issuer[:0], dAtA[iNdEx:postIndex]...) + if m.Issuer == nil { + m.Issuer = []byte{} + } + iNdEx = postIndex + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Manager", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -1881,9 +1285,28 @@ func (m *MsgUpdateToken) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Manager = string(dAtA[iNdEx:postIndex]) + m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 2: + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Decimal", wireType) + } + m.Decimal = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Decimal |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Symbol", wireType) } @@ -1915,11 +1338,11 @@ func (m *MsgUpdateToken) Unmarshal(dAtA []byte) error { } m.Symbol = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationRequired", wireType) + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) } - var v int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -1929,117 +1352,29 @@ func (m *MsgUpdateToken) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - m.AuthorizationRequired = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipTx(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthTx } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *MsgUpdateTokenResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: MsgUpdateTokenResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: MsgUpdateTokenResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipTx(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { + postIndex := iNdEx + intStringLen + if postIndex < 0 { return ErrInvalidLengthTx } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *MsgAuthorizeAddress) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { + if postIndex > l { return io.ErrUnexpectedEOF } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: MsgAuthorizeAddress: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: MsgAuthorizeAddress: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Manager", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Managers", wireType) } - var stringLen uint64 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2049,27 +1384,47 @@ func (m *MsgAuthorizeAddress) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if byteLen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Manager = string(dAtA[iNdEx:postIndex]) + m.Managers = append(m.Managers, make([]byte, postIndex-iNdEx)) + copy(m.Managers[len(m.Managers)-1], dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 2: + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowNewExtensions", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowNewExtensions = bool(v != 0) + case 9: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Symbol", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ExtensionsList", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -2097,11 +1452,11 @@ func (m *MsgAuthorizeAddress) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Symbol = string(dAtA[iNdEx:postIndex]) + m.ExtensionsList = append(m.ExtensionsList, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 3: + case 10: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field MaxSupply", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -2129,7 +1484,9 @@ func (m *MsgAuthorizeAddress) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Address = string(dAtA[iNdEx:postIndex]) + if err := m.MaxSupply.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex @@ -2152,7 +1509,7 @@ func (m *MsgAuthorizeAddress) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgAuthorizeAddressResponse) Unmarshal(dAtA []byte) error { +func (m *MsgCreateTokenResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2175,12 +1532,44 @@ func (m *MsgAuthorizeAddressResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgAuthorizeAddressResponse: wiretype end group for non-group") + return fmt.Errorf("proto: MsgCreateTokenResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgAuthorizeAddressResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgCreateTokenResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TokenId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TokenId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) @@ -2202,7 +1591,7 @@ func (m *MsgAuthorizeAddressResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgUnAuthorizeAddress) Unmarshal(dAtA []byte) error { +func (m *MsgAssignRoles) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2225,17 +1614,17 @@ func (m *MsgUnAuthorizeAddress) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgUnAuthorizeAddress: wiretype end group for non-group") + return fmt.Errorf("proto: MsgAssignRoles: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgUnAuthorizeAddress: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgAssignRoles: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Manager", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Issuer", wireType) } - var stringLen uint64 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2245,27 +1634,29 @@ func (m *MsgUnAuthorizeAddress) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if byteLen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Manager = string(dAtA[iNdEx:postIndex]) + m.Issuer = append(m.Issuer[:0], dAtA[iNdEx:postIndex]...) + if m.Issuer == nil { + m.Issuer = []byte{} + } iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Symbol", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field TokenId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -2293,13 +1684,13 @@ func (m *MsgUnAuthorizeAddress) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Symbol = string(dAtA[iNdEx:postIndex]) + m.TokenId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Managers", wireType) } - var stringLen uint64 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2309,23 +1700,25 @@ func (m *MsgUnAuthorizeAddress) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if byteLen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Address = string(dAtA[iNdEx:postIndex]) + m.Managers = append(m.Managers[:0], dAtA[iNdEx:postIndex]...) + if m.Managers == nil { + m.Managers = []byte{} + } iNdEx = postIndex default: iNdEx = preIndex @@ -2348,7 +1741,7 @@ func (m *MsgUnAuthorizeAddress) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgUnAuthorizeAddressResponse) Unmarshal(dAtA []byte) error { +func (m *MsgAssignRolesResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2371,10 +1764,10 @@ func (m *MsgUnAuthorizeAddressResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgUnAuthorizeAddressResponse: wiretype end group for non-group") + return fmt.Errorf("proto: MsgAssignRolesResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgUnAuthorizeAddressResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgAssignRolesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: @@ -2398,7 +1791,7 @@ func (m *MsgUnAuthorizeAddressResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgTransferToken) Unmarshal(dAtA []byte) error { +func (m *MsgUnassignRoles) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2421,17 +1814,17 @@ func (m *MsgTransferToken) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgTransferToken: wiretype end group for non-group") + return fmt.Errorf("proto: MsgUnassignRoles: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgTransferToken: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgUnassignRoles: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Symbol", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Issuer", wireType) } - var stringLen uint64 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2441,27 +1834,29 @@ func (m *MsgTransferToken) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if byteLen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.Symbol = string(dAtA[iNdEx:postIndex]) + m.Issuer = append(m.Issuer[:0], dAtA[iNdEx:postIndex]...) + if m.Issuer == nil { + m.Issuer = []byte{} + } iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field From", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field TokenId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -2489,13 +1884,13 @@ func (m *MsgTransferToken) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.From = string(dAtA[iNdEx:postIndex]) + m.TokenId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field To", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Managers", wireType) } - var stringLen uint64 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTx @@ -2505,55 +1900,25 @@ func (m *MsgTransferToken) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if byteLen < 0 { return ErrInvalidLengthTx } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthTx } if postIndex > l { return io.ErrUnexpectedEOF } - m.To = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF + m.Managers = append(m.Managers[:0], dAtA[iNdEx:postIndex]...) + if m.Managers == nil { + m.Managers = []byte{} } - m.Amount = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -2576,7 +1941,7 @@ func (m *MsgTransferToken) Unmarshal(dAtA []byte) error { } return nil } -func (m *MsgTransferTokenResponse) Unmarshal(dAtA []byte) error { +func (m *MsgUnassignRolesResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2599,10 +1964,10 @@ func (m *MsgTransferTokenResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: MsgTransferTokenResponse: wiretype end group for non-group") + return fmt.Errorf("proto: MsgUnassignRolesResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: MsgTransferTokenResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: MsgUnassignRolesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: diff --git a/x/asset/types/types.go b/x/asset/types/types.go deleted file mode 100644 index ab1254f4..00000000 --- a/x/asset/types/types.go +++ /dev/null @@ -1 +0,0 @@ -package types