From 4437ab1f398607defb3cbf4a87fb900ba7233bcc Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Wed, 2 Oct 2024 09:24:43 -0400
Subject: [PATCH 01/63] Update go, fedora in dockerfile

---
 tests/container-scripts/run-testnet.sh | 74 --------------------------
 tests/dockerfile/Dockerfile            |  7 +--
 2 files changed, 4 insertions(+), 77 deletions(-)
 delete mode 100755 tests/container-scripts/run-testnet.sh

diff --git a/tests/container-scripts/run-testnet.sh b/tests/container-scripts/run-testnet.sh
deleted file mode 100755
index c377c9a7..00000000
--- a/tests/container-scripts/run-testnet.sh
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/bin/bash
-set -eux
-# your gaiad binary name
-BIN=althea
-
-NODES=$1
-
-for i in $(seq 1 $NODES);
-do
-    # add this ip for loopback dialing
-    ip addr add 7.7.7.$i/32 dev eth0 || true # allowed to fail
-
-    GAIA_HOME="--home /validator$i"
-    # this implicitly caps us at ~6000 nodes for this sim
-    # note that we start on 26656 the idea here is that the first
-    # node (node 1) is at the expected contact address from the gentx
-    # faciliating automated peer exchange
-    if [[ "$i" -eq 1 ]]; then
-        # node one gets localhost so we can easily shunt these ports
-        # to the docker host
-        RPC_ADDRESS="--rpc.laddr tcp://0.0.0.0:26657"
-        GRPC_ADDRESS="--grpc.address 0.0.0.0:9090"
-        GRPC_WEB_ADDRESS="--grpc-web.address 0.0.0.0:9092"
-        ETH_RPC_ADDRESS="--json-rpc.address 0.0.0.0:8545"
-        ETH_RPC_WS_ADDRESS="--json-rpc.ws-address 0.0.0.0:8546"
-        sed -i 's/enable-unsafe-cors = false/enable-unsafe-cors = true/g' /validator$i/config/app.toml
-        sed -i 's/enabled-unsafe-cors = false/enabled-unsafe-cors = true/g' /validator$i/config/app.toml
-        sed -i 's/enable = false/enable = true/g' /validator$i/config/app.toml #enables more than we want, but will work for now
-    else
-        # move these to another port and address, not becuase they will
-        # be used there, but instead to prevent them from causing problems
-        # you also can't duplicate the port selection against localhost
-        # for reasons that are not clear to me right now.
-        RPC_ADDRESS="--rpc.laddr tcp://7.7.7.$i:26658"
-        GRPC_ADDRESS="--grpc.address 7.7.7.$i:9091"
-        GRPC_WEB_ADDRESS="--grpc-web.address 7.7.7.$i:9093"
-        ETH_RPC_ADDRESS="--json-rpc.address 7.7.7.$i:8545"
-        ETH_RPC_WS_ADDRESS="--json-rpc.address 7.7.7.$i:8546"
-    fi
-    LISTEN_ADDRESS="--address tcp://7.7.7.$i:26655"
-    P2P_ADDRESS="--p2p.laddr tcp://7.7.7.$i:26656"
-    LOG_LEVEL="--log_level info"
-    INVARIANTS_CHECK="--inv-check-period 1"
-    ARGS="$GAIA_HOME $LISTEN_ADDRESS $RPC_ADDRESS $GRPC_ADDRESS $GRPC_WEB_ADDRESS $ETH_RPC_ADDRESS $ETH_RPC_WS_ADDRESS $INVARIANTS_CHECK $LOG_LEVEL $P2P_ADDRESS"
-    $BIN $ARGS start &> /validator$i/logs &
-done
-
-# Setup the IBC test chain (chain id ibc-test-1) using gaiad as the binary
-# Creates the same number of validators as the althea chain above, with their home directories at /ibc-validator#
-BIN=gaiad
-for i in $(seq 1 $NODES);
-do
-    ip addr add 7.7.8.$i/32 dev eth0 || true # allowed to fail
-
-    GAIA_HOME="--home /ibc-validator$i"
-    if [[ "$i" -eq 1 ]]; then
-        # node one gets localhost so we can easily shunt these ports
-        # to the docker host
-        RPC_ADDRESS="--rpc.laddr tcp://0.0.0.0:27657"
-        GRPC_ADDRESS="--grpc.address 0.0.0.0:9190"
-        # Must remap the grpc-web address because it conflicts with what we want to use
-        GRPC_WEB_ADDRESS="--grpc-web.address 0.0.0.0:9192"
-    else
-        RPC_ADDRESS="--rpc.laddr tcp://7.7.8.$i:26658"
-        GRPC_ADDRESS="--grpc.address 7.7.8.$i:9091"
-        # Must remap the grpc-web address because it conflicts with what we want to use
-        GRPC_WEB_ADDRESS="--grpc-web.address 7.7.8.$i:9093"
-    fi
-    LISTEN_ADDRESS="--address tcp://7.7.8.$i:26655"
-    P2P_ADDRESS="--p2p.laddr tcp://7.7.8.$i:26656"
-    LOG_LEVEL="--log_level info"
-    ARGS="$GAIA_HOME $LISTEN_ADDRESS $RPC_ADDRESS $GRPC_ADDRESS $GRPC_WEB_ADDRESS $LOG_LEVEL $P2P_ADDRESS"
-    $BIN $ARGS start &> /ibc-validator$i/logs &
-done
\ No newline at end of file
diff --git a/tests/dockerfile/Dockerfile b/tests/dockerfile/Dockerfile
index 5d00f446..44ae817b 100755
--- a/tests/dockerfile/Dockerfile
+++ b/tests/dockerfile/Dockerfile
@@ -1,8 +1,9 @@
-FROM fedora:37
+FROM fedora:40
 ENV GOPATH=/go
 ENV PATH=$PATH:/go/bin
-RUN dnf install -y git make gcc gcc-c++ which iproute iputils procps-ng vim-minimal tmux net-tools htop tar jq npm openssl-devel perl rust cargo golang wget
-
+RUN dnf install -y git make gcc gcc-c++ which iproute iputils procps-ng vim-minimal tmux net-tools htop tar jq npm openssl-devel perl rust cargo wget
+COPY --from=golang:1.22 /usr/local/go/ /usr/local/go/
+ENV PATH="/usr/local/go/bin:${PATH}"
 # Download the althea gaia fork as a IBC test chain
 ADD https://github.com/althea-net/ibc-test-chain/releases/download/v9.1.5/gaiad-v9.1.5-linux-amd64 /usr/bin/gaiad
 # Setup Hermes for IBC connections between chains

From 85b8bd9ec405f8494a0bcd3d255fb1b012439830 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Tue, 1 Oct 2024 14:53:18 -0400
Subject: [PATCH 02/63] Upgrade SDK to v0.46.16, IBC to 6.3.1, Ethermint to
 v0.22, add upgrade

---
 .github/workflows/integration-tests.yml       |   16 +
 app/ante/handler_options.go                   |   85 +-
 app/ante/utils_test.go                        |   16 +-
 app/app.go                                    |  367 ++++--
 app/migrate.go                                |    8 +-
 app/sigverify.go                              |    2 +-
 app/upgrades/neutrino/README.md               |    9 +
 app/upgrades/neutrino/handler.go              |   32 +
 app/upgrades/register.go                      |   26 +
 cmd/althea/genaccounts.go                     |    9 +-
 cmd/althea/root.go                            |   61 +-
 cmd/althea/testnet.go                         |   17 +-
 go.mod                                        |  242 ++--
 go.sum                                        | 1100 +++++++++++------
 ibcutils/testing/app.go                       |   11 +-
 ibcutils/testing/chain.go                     |   20 +-
 ibcutils/testing/config.go                    |   10 +-
 ibcutils/testing/endpoint.go                  |   14 +-
 ibcutils/testing/events.go                    |    6 +-
 ibcutils/testing/path.go                      |    2 +-
 ibcutils/testing/values.go                    |    8 +-
 ibcutils/utils.go                             |    4 +-
 ibcutils/utils_test.go                        |    6 +-
 integration_tests/test_runner/src/bin/main.rs |   23 +
 .../test_runner/src/tests/mod.rs              |    1 +
 .../test_runner/src/tests/upgrade.rs          |  169 +++
 .../manual-upgrade-test-internal.sh           |   58 +
 tests/container-scripts/run-testnet.sh        |   95 ++
 tests/container-scripts/setup-validators.sh   |   10 +
 tests/container-scripts/start-dlv.sh          |    7 +
 .../upgrade-test-internal.sh                  |   53 +
 tests/manual-upgrade-test.sh                  |   31 +
 tests/run-upgrade-test.sh                     |   37 +
 x/gasfree/keeper/keeper.go                    |    5 +-
 x/lockup/ante.go                              |    2 +-
 x/lockup/ante_test.go                         |   26 +-
 x/lockup/keeper/keeper.go                     |    5 +-
 x/lockup/keeper/test_common.go                |   39 +-
 x/lockup/types/genesis.go                     |    2 +-
 x/microtx/keeper/keeper.go                    |    7 +-
 x/microtx/keeper/liquid_account.go            |    2 +-
 x/microtx/types/msgs.go                       |    2 +-
 x/nativedex/client/cli/utils.go               |    6 +-
 x/nativedex/client/proposal_handler.go        |   17 +
 x/nativedex/keeper/keeper.go                  |    5 +-
 x/nativedex/proposal_handler.go               |    6 +-
 x/nativedex/types/codec.go                    |    4 +-
 x/nativedex/types/proposal.go                 |   71 +-
 x/onboarding/ibc_middleware.go                |   17 +-
 x/onboarding/ibc_module_test.go               |   14 +-
 x/onboarding/keeper/ibc_callbacks.go          |    8 +-
 x/onboarding/keeper/ibc_callbacks_test.go     |   38 +-
 x/onboarding/keeper/keeper.go                 |   20 +-
 x/onboarding/keeper/utils_test.go             |    4 +-
 x/onboarding/testutil/helpers.go              |    2 +-
 x/onboarding/types/interfaces.go              |    8 +-
 56 files changed, 1978 insertions(+), 887 deletions(-)
 create mode 100644 app/upgrades/neutrino/README.md
 create mode 100644 app/upgrades/neutrino/handler.go
 create mode 100644 app/upgrades/register.go
 create mode 100644 integration_tests/test_runner/src/tests/upgrade.rs
 create mode 100755 tests/container-scripts/manual-upgrade-test-internal.sh
 create mode 100755 tests/container-scripts/run-testnet.sh
 create mode 100755 tests/container-scripts/start-dlv.sh
 create mode 100755 tests/container-scripts/upgrade-test-internal.sh
 create mode 100755 tests/manual-upgrade-test.sh
 create mode 100755 tests/run-upgrade-test.sh
 create mode 100644 x/nativedex/client/proposal_handler.go

diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
index 013a9f44..2ade30ea 100644
--- a/.github/workflows/integration-tests.yml
+++ b/.github/workflows/integration-tests.yml
@@ -216,3 +216,19 @@ jobs:
         run: tests/all-up-test.sh DEX_OPS_PROPOSAL
         env:
           NO_IMAGE_BUILD: True
+  UPGRADE:
+    needs: native_token
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: jpribyl/action-docker-layer-caching@v0.1.1
+        with:
+          key: integration-test-cache-{hash}
+          restore-keys: |
+            integration-test-cache-
+      - name: Tests the nativedex OpsProposal function
+        run: tests/all-up-test.sh DEX_OPS_PROPOSAL
+      - name: Tests the Neutrino
+        run: tests/run-upgrade-test.sh v1.4.0
+        env:
+          NO_IMAGE_BUILD: True
diff --git a/app/ante/handler_options.go b/app/ante/handler_options.go
index 65202cfe..226c15cb 100644
--- a/app/ante/handler_options.go
+++ b/app/ante/handler_options.go
@@ -1,7 +1,10 @@
 package ante
 
 import (
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/cosmos/cosmos-sdk/codec"
+	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	"github.com/cosmos/cosmos-sdk/types/tx/signing"
@@ -9,12 +12,14 @@ import (
 	authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
 	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
 
-	ibcante "github.com/cosmos/ibc-go/v4/modules/core/ante"
-	ibckeeper "github.com/cosmos/ibc-go/v4/modules/core/keeper"
+	ibcante "github.com/cosmos/ibc-go/v6/modules/core/ante"
+	ibckeeper "github.com/cosmos/ibc-go/v6/modules/core/keeper"
 
 	ethante "github.com/evmos/ethermint/app/ante"
+	ethtypes "github.com/evmos/ethermint/types"
 	evmkeeper "github.com/evmos/ethermint/x/evm/keeper"
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
+	feemarketkeeper "github.com/evmos/ethermint/x/feemarket/keeper"
 
 	"github.com/AltheaFoundation/althea-L1/x/gasfree"
 	gasfreekeeper "github.com/AltheaFoundation/althea-L1/x/gasfree/keeper"
@@ -24,42 +29,42 @@ import (
 // HandlerOptions defines the list of module keepers required to run the canto
 // AnteHandler decorators.
 type HandlerOptions struct {
-	AccountKeeper   AccountKeeper
-	BankKeeper      evmtypes.BankKeeper
-	IBCKeeper       *ibckeeper.Keeper
-	FeeMarketKeeper evmtypes.FeeMarketKeeper
-	EvmKeeper       *evmkeeper.Keeper
-	FeegrantKeeper  ante.FeegrantKeeper
-	SignModeHandler authsigning.SignModeHandler
-	SigGasConsumer  func(meter sdk.GasMeter, sig signing.SignatureV2, params authtypes.Params) error
-	Cdc             codec.BinaryCodec
-	MaxTxGasWanted  uint64
-	GasfreeKeeper   *gasfreekeeper.Keeper
-	MicrotxKeeper   *microtxkeeper.Keeper
+	AccountKeeper          AccountKeeper
+	BankKeeper             evmtypes.BankKeeper
+	IBCKeeper              *ibckeeper.Keeper
+	FeeMarketKeeper        feemarketkeeper.Keeper
+	EvmKeeper              *evmkeeper.Keeper
+	FeegrantKeeper         ante.FeegrantKeeper
+	SignModeHandler        authsigning.SignModeHandler
+	SigGasConsumer         func(meter sdk.GasMeter, sig signing.SignatureV2, params authtypes.Params) error
+	MaxTxGasWanted         uint64
+	ExtensionOptionChecker ante.ExtensionOptionChecker
+	TxFeeChecker           ante.TxFeeChecker
+	DisabledAuthzMsgs      []string
+	Cdc                    codec.BinaryCodec
+	GasfreeKeeper          *gasfreekeeper.Keeper
+	MicrotxKeeper          *microtxkeeper.Keeper
 }
 
 // Validate checks if the keepers are defined
 func (options HandlerOptions) Validate() error {
 	if options.AccountKeeper == nil {
-		return sdkerrors.Wrap(sdkerrors.ErrLogic, "account keeper is required for AnteHandler")
+		return errorsmod.Wrap(sdkerrors.ErrLogic, "account keeper is required for AnteHandler")
 	}
 	if options.BankKeeper == nil {
-		return sdkerrors.Wrap(sdkerrors.ErrLogic, "bank keeper is required for AnteHandler")
+		return errorsmod.Wrap(sdkerrors.ErrLogic, "bank keeper is required for AnteHandler")
 	}
 	if options.SignModeHandler == nil {
-		return sdkerrors.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for ante builder")
-	}
-	if options.FeeMarketKeeper == nil {
-		return sdkerrors.Wrap(sdkerrors.ErrLogic, "fee market keeper is required for AnteHandler")
+		return errorsmod.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for ante builder")
 	}
 	if options.EvmKeeper == nil {
-		return sdkerrors.Wrap(sdkerrors.ErrLogic, "evm keeper is required for AnteHandler")
+		return errorsmod.Wrap(sdkerrors.ErrLogic, "evm keeper is required for AnteHandler")
 	}
 	if options.GasfreeKeeper == nil {
-		return sdkerrors.Wrap(sdkerrors.ErrLogic, "gasfree keeper is required for AnteHandler")
+		return errorsmod.Wrap(sdkerrors.ErrLogic, "gasfree keeper is required for AnteHandler")
 	}
 	if options.MicrotxKeeper == nil {
-		return sdkerrors.Wrap(sdkerrors.ErrLogic, "microtx keeper is required for AnteHandler")
+		return errorsmod.Wrap(sdkerrors.ErrLogic, "microtx keeper is required for AnteHandler")
 	}
 	return nil
 }
@@ -83,22 +88,33 @@ func newEthAnteHandler(options HandlerOptions) sdk.AnteHandler {
 	)
 }
 
+func CosmosExtensionOptionChecker(any *codectypes.Any) bool {
+	a := ethtypes.HasDynamicFeeExtensionOption(any)
+	b := hasEIP712ExtensionOption(any)
+
+	return a || b
+}
+
+func hasEIP712ExtensionOption(any *codectypes.Any) bool {
+	_, ok := any.GetCachedValue().(*ethtypes.ExtensionOptionsWeb3Tx)
+	return ok
+}
+
 // newCosmosAnteHandler creates the default ante handler for Cosmos transactions
 func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler {
 	return sdk.ChainAnteDecorators(
 		ethante.RejectMessagesDecorator{}, // reject MsgEthereumTxs
+		ethante.NewAuthzLimiterDecorator(options.DisabledAuthzMsgs),
 		ante.NewSetUpContextDecorator(),
-		ante.NewRejectExtensionOptionsDecorator(),
+		ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker),
 		ante.NewValidateBasicDecorator(),
-		// Gasfree txs ignore the mempool fee requirement
-		gasfree.NewSelectiveBypassDecorator(*options.GasfreeKeeper, ante.NewMempoolFeeDecorator()),
+		ante.NewTxTimeoutHeightDecorator(),
 		// Gasfree txs ignore the min gas price requirement
 		gasfree.NewSelectiveBypassDecorator(*options.GasfreeKeeper, ethante.NewMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper)),
-		ante.NewTxTimeoutHeightDecorator(),
 		ante.NewValidateMemoDecorator(options.AccountKeeper),
 		ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
 		// Gasfree txs do not have fees deducted the normal way, their fees will be deducted separately
-		gasfree.NewSelectiveBypassDecorator(*options.GasfreeKeeper, ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper)),
+		gasfree.NewSelectiveBypassDecorator(*options.GasfreeKeeper, ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, nil)),
 		// Charge gas fees for gasfree messages
 		NewChargeGasfreeFeesDecorator(options.AccountKeeper, *options.GasfreeKeeper, *options.MicrotxKeeper),
 		NewValidatorCommissionDecorator(options.Cdc),
@@ -108,7 +124,7 @@ func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler {
 		ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer),
 		ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
 		ante.NewIncrementSequenceDecorator(options.AccountKeeper),
-		ibcante.NewAnteDecorator(options.IBCKeeper),
+		ibcante.NewRedundantRelayDecorator(options.IBCKeeper),
 		ethante.NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper),
 		NewSetAccountTypeDecorator(options.AccountKeeper, options.EvmKeeper.AccountProtoFn),
 	)
@@ -118,23 +134,24 @@ func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler {
 func newCosmosAnteHandlerEip712(options HandlerOptions) sdk.AnteHandler {
 	return sdk.ChainAnteDecorators(
 		ethante.RejectMessagesDecorator{}, // reject MsgEthereumTxs
+		ethante.NewAuthzLimiterDecorator(options.DisabledAuthzMsgs),
 		ante.NewSetUpContextDecorator(),
-		ante.NewMempoolFeeDecorator(),
+		ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker),
 		ante.NewValidateBasicDecorator(),
-		ethante.NewMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper),
 		ante.NewTxTimeoutHeightDecorator(),
+		ethante.NewMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper),
 		ante.NewValidateMemoDecorator(options.AccountKeeper),
 		ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
-		ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper),
+		ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, nil),
 		NewValidatorCommissionDecorator(options.Cdc),
 		// SetPubKeyDecorator must be called before all signature verification decorators
 		ante.NewSetPubKeyDecorator(options.AccountKeeper),
 		ante.NewValidateSigCountDecorator(options.AccountKeeper),
 		ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer),
 		// Note: signature verification uses EIP instead of the cosmos signature validator
-		ethante.NewEip712SigVerificationDecorator(options.AccountKeeper, options.SignModeHandler, ""), // Pass no chain id to have it parsed from the Cosmos chain id
+		ethante.NewLegacyEip712SigVerificationDecorator(options.AccountKeeper, options.SignModeHandler, ""), // Pass no chain id to have it parsed from the Cosmos chain id
 		ante.NewIncrementSequenceDecorator(options.AccountKeeper),
-		ibcante.NewAnteDecorator(options.IBCKeeper),
+		ibcante.NewRedundantRelayDecorator(options.IBCKeeper),
 		ethante.NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper),
 		NewSetAccountTypeDecorator(options.AccountKeeper, options.EvmKeeper.AccountProtoFn),
 	)
diff --git a/app/ante/utils_test.go b/app/ante/utils_test.go
index faf18774..34ab2289 100644
--- a/app/ante/utils_test.go
+++ b/app/ante/utils_test.go
@@ -10,6 +10,7 @@ import (
 	"github.com/ethereum/go-ethereum/common"
 	ethtypes "github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/crypto"
+	apitypes "github.com/ethereum/go-ethereum/signer/core/apitypes"
 	"github.com/stretchr/testify/suite"
 
 	client "github.com/cosmos/cosmos-sdk/client"
@@ -22,7 +23,7 @@ import (
 	"github.com/cosmos/cosmos-sdk/testutil/testdata"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	"github.com/cosmos/cosmos-sdk/types/tx/signing"
-	"github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx"
+	"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
 	authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
 	authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
 	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
@@ -39,7 +40,7 @@ import (
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
 	feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"
 
-	cantoante "github.com/Canto-Network/Canto/v5/app/ante"
+	cantoante "github.com/Canto-Network/Canto/v6/app/ante"
 
 	althea "github.com/AltheaFoundation/althea-L1/app"
 	ante "github.com/AltheaFoundation/althea-L1/app/ante"
@@ -119,12 +120,12 @@ func (suite *AnteTestSuite) SetupTest() {
 	// Also make a copy of the old Canto antehandler we were using to ensure that our changes fix the problem
 	// nolint: exhaustruct
 	oldAnteHandler := cantoante.NewAnteHandler(cantoante.HandlerOptions{
-		AccountKeeper:   suite.app.AccountKeeper,
+		AccountKeeper:   *suite.app.AccountKeeper,
 		BankKeeper:      suite.app.BankKeeper,
 		EvmKeeper:       suite.app.EvmKeeper,
 		FeegrantKeeper:  nil,
 		IBCKeeper:       suite.app.IbcKeeper,
-		FeeMarketKeeper: suite.app.FeemarketKeeper,
+		FeeMarketKeeper: *suite.app.FeemarketKeeper,
 		SignModeHandler: encodingConfig.TxConfig.SignModeHandler(),
 		SigGasConsumer:  althea.SigVerificationGasConsumer,
 	})
@@ -390,13 +391,12 @@ func (suite *AnteTestSuite) CreateTestEIP712CosmosTxBuilder(
 	fee := legacytx.NewStdFee(gas, gasAmount)
 	accNumber := suite.app.AccountKeeper.GetAccount(suite.ctx, from).GetAccountNumber()
 
-	data := legacytx.StdSignBytes(chainId, accNumber, nonce, 0, fee, []sdk.Msg{msg}, "")
-	typedData, err := eip712.WrapTxToTypedData(ethermintCodec, ethChainId, msg, data, &eip712.FeeDelegationOptions{
+	data := legacytx.StdSignBytes(chainId, accNumber, nonce, 0, fee, []sdk.Msg{msg}, "", nil)
+	typedData, err := eip712.LegacyWrapTxToTypedData(ethermintCodec, ethChainId, msg, data, &eip712.FeeDelegationOptions{
 		FeePayer: from,
 	})
 	suite.Require().NoError(err)
-
-	sigHash, err := eip712.ComputeTypedDataHash(typedData)
+	sigHash, _, err := apitypes.TypedDataAndHash(typedData)
 	suite.Require().NoError(err)
 
 	// Sign typedData
diff --git a/app/app.go b/app/app.go
index 2ae94dc7..212951e9 100644
--- a/app/app.go
+++ b/app/app.go
@@ -21,23 +21,28 @@ import (
 	// Cosmos SDK
 	"github.com/cosmos/cosmos-sdk/baseapp"
 	"github.com/cosmos/cosmos-sdk/client"
+	nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node"
 	"github.com/cosmos/cosmos-sdk/client/grpc/tmservice"
-	"github.com/cosmos/cosmos-sdk/client/rpc"
 	"github.com/cosmos/cosmos-sdk/codec"
 	"github.com/cosmos/cosmos-sdk/codec/types"
 	"github.com/cosmos/cosmos-sdk/server/api"
 	"github.com/cosmos/cosmos-sdk/server/config"
 	servertypes "github.com/cosmos/cosmos-sdk/server/types"
 	"github.com/cosmos/cosmos-sdk/simapp"
+	simappparams "github.com/cosmos/cosmos-sdk/simapp/params"
+	"github.com/cosmos/cosmos-sdk/store/streaming"
+	storetypes "github.com/cosmos/cosmos-sdk/store/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	"github.com/cosmos/cosmos-sdk/types/module"
 	"github.com/cosmos/cosmos-sdk/version"
 	"github.com/cosmos/cosmos-sdk/x/auth"
 	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"
 	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
 	"github.com/cosmos/cosmos-sdk/x/auth/vesting"
+	vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/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"
@@ -60,8 +65,14 @@ import (
 	"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"
+	govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
+	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
+	"github.com/cosmos/cosmos-sdk/x/group"
+	groupkeeper "github.com/cosmos/cosmos-sdk/x/group/keeper"
+	groupmodule "github.com/cosmos/cosmos-sdk/x/group/module"
 	"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"
@@ -82,35 +93,38 @@ import (
 	upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
 
 	// Cosmos IBC-Go
-	ica "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts"
-	icahost "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host"
-	icahostkeeper "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/keeper"
-	icahosttypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/types"
-	icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types"
-	transfer "github.com/cosmos/ibc-go/v4/modules/apps/transfer"
-	ibctransferkeeper "github.com/cosmos/ibc-go/v4/modules/apps/transfer/keeper"
-	ibctransfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
-	ibc "github.com/cosmos/ibc-go/v4/modules/core"
-	ibcclient "github.com/cosmos/ibc-go/v4/modules/core/02-client"
-	ibcclientclient "github.com/cosmos/ibc-go/v4/modules/core/02-client/client"
-	ibcclienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
-	porttypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types"
-	ibchost "github.com/cosmos/ibc-go/v4/modules/core/24-host"
-	ibckeeper "github.com/cosmos/ibc-go/v4/modules/core/keeper"
+	ica "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts"
+	icahost "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/host"
+	icahostkeeper "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/host/keeper"
+	icahosttypes "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/host/types"
+	icatypes "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/types"
+	transfer "github.com/cosmos/ibc-go/v6/modules/apps/transfer"
+	ibctransferkeeper "github.com/cosmos/ibc-go/v6/modules/apps/transfer/keeper"
+	ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
+	ibc "github.com/cosmos/ibc-go/v6/modules/core"
+	ibcclient "github.com/cosmos/ibc-go/v6/modules/core/02-client"
+	ibcclientclient "github.com/cosmos/ibc-go/v6/modules/core/02-client/client"
+	ibcclienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
+	porttypes "github.com/cosmos/ibc-go/v6/modules/core/05-port/types"
+	ibchost "github.com/cosmos/ibc-go/v6/modules/core/24-host"
+	ibckeeper "github.com/cosmos/ibc-go/v6/modules/core/keeper"
+	ibctesting "github.com/cosmos/ibc-go/v6/testing/types"
 
 	// EVM + ERC20
 
-	"github.com/Canto-Network/Canto/v5/x/erc20"
-	erc20client "github.com/Canto-Network/Canto/v5/x/erc20/client"
-	erc20keeper "github.com/Canto-Network/Canto/v5/x/erc20/keeper"
-	erc20types "github.com/Canto-Network/Canto/v5/x/erc20/types"
+	"github.com/Canto-Network/Canto/v6/x/erc20"
+	erc20client "github.com/Canto-Network/Canto/v6/x/erc20/client"
+	erc20keeper "github.com/Canto-Network/Canto/v6/x/erc20/keeper"
+	erc20types "github.com/Canto-Network/Canto/v6/x/erc20/types"
 
+	ethante "github.com/evmos/ethermint/app/ante"
+	"github.com/evmos/ethermint/ethereum/eip712"
 	ethermintsrvflags "github.com/evmos/ethermint/server/flags"
 	ethtypes "github.com/evmos/ethermint/types"
 	"github.com/evmos/ethermint/x/evm"
-	evmrest "github.com/evmos/ethermint/x/evm/client/rest"
 	evmkeeper "github.com/evmos/ethermint/x/evm/keeper"
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
+	"github.com/evmos/ethermint/x/evm/vm/geth"
 	"github.com/evmos/ethermint/x/feemarket"
 	feemarketkeeper "github.com/evmos/ethermint/x/feemarket/keeper"
 	feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"
@@ -120,6 +134,8 @@ import (
 
 	"github.com/AltheaFoundation/althea-L1/app/ante"
 	altheaappparams "github.com/AltheaFoundation/althea-L1/app/params"
+	"github.com/AltheaFoundation/althea-L1/app/upgrades"
+	"github.com/AltheaFoundation/althea-L1/app/upgrades/neutrino"
 	altheacfg "github.com/AltheaFoundation/althea-L1/config"
 	"github.com/AltheaFoundation/althea-L1/x/gasfree"
 	gasfreekeeper "github.com/AltheaFoundation/althea-L1/x/gasfree/keeper"
@@ -175,20 +191,23 @@ var (
 		mint.AppModuleBasic{},
 		distr.AppModuleBasic{},
 		gov.NewAppModuleBasic(
-			paramsclient.ProposalHandler,
-			distrclient.ProposalHandler,
-			upgradeclient.ProposalHandler,
-			upgradeclient.CancelProposalHandler,
-			ibcclientclient.UpdateClientProposalHandler,
-			ibcclientclient.UpgradeProposalHandler,
-			erc20client.RegisterCoinProposalHandler,
-			erc20client.RegisterERC20ProposalHandler,
-			erc20client.ToggleTokenConversionProposalHandler,
+			[]govclient.ProposalHandler{
+				paramsclient.ProposalHandler,
+				distrclient.ProposalHandler,
+				upgradeclient.LegacyProposalHandler,
+				upgradeclient.LegacyCancelProposalHandler,
+				ibcclientclient.UpdateClientProposalHandler,
+				ibcclientclient.UpgradeProposalHandler,
+				erc20client.RegisterCoinProposalHandler,
+				erc20client.RegisterERC20ProposalHandler,
+				erc20client.ToggleTokenConversionProposalHandler,
+			},
 		),
 		params.AppModuleBasic{},
 		crisis.AppModuleBasic{},
 		slashing.AppModuleBasic{},
 		ibc.AppModuleBasic{},
+		// TODO: Add feegrant
 		upgrade.AppModuleBasic{},
 		evidence.AppModuleBasic{},
 		transfer.AppModuleBasic{},
@@ -201,6 +220,7 @@ var (
 		erc20.AppModuleBasic{},
 		feemarket.AppModuleBasic{},
 		ica.AppModuleBasic{},
+		groupmodule.AppModuleBasic{},
 	)
 
 	// module account permissions
@@ -249,24 +269,25 @@ type AltheaApp struct { // nolint: golint
 	invCheckPeriod uint
 
 	// keys to access the substores
-	keys    map[string]*sdk.KVStoreKey
-	tKeys   map[string]*sdk.TransientStoreKey
-	memKeys map[string]*sdk.MemoryStoreKey
+	keys    map[string]*storetypes.KVStoreKey
+	tKeys   map[string]*storetypes.TransientStoreKey
+	memKeys map[string]*storetypes.MemoryStoreKey
 
 	// keepers
 	// NOTE: If you add anything to this struct, add a nil check to ValidateMembers below!
-	AccountKeeper     *authkeeper.AccountKeeper
-	AuthzKeeper       *authzkeeper.Keeper
-	BankKeeper        *bankkeeper.BaseKeeper
-	CapabilityKeeper  *capabilitykeeper.Keeper
-	StakingKeeper     *stakingkeeper.Keeper
-	SlashingKeeper    *slashingkeeper.Keeper
-	MintKeeper        *mintkeeper.Keeper
-	DistrKeeper       *distrkeeper.Keeper
-	GovKeeper         *govkeeper.Keeper
-	CrisisKeeper      *crisiskeeper.Keeper
-	UpgradeKeeper     *upgradekeeper.Keeper
-	ParamsKeeper      *paramskeeper.Keeper
+	AccountKeeper    *authkeeper.AccountKeeper
+	AuthzKeeper      *authzkeeper.Keeper
+	BankKeeper       *bankkeeper.BaseKeeper
+	CapabilityKeeper *capabilitykeeper.Keeper
+	StakingKeeper    *stakingkeeper.Keeper
+	SlashingKeeper   *slashingkeeper.Keeper
+	MintKeeper       *mintkeeper.Keeper
+	DistrKeeper      *distrkeeper.Keeper
+	GovKeeper        *govkeeper.Keeper
+	CrisisKeeper     *crisiskeeper.Keeper
+	UpgradeKeeper    *upgradekeeper.Keeper
+	ParamsKeeper     *paramskeeper.Keeper
+	// TODO: Add feegrant
 	IbcKeeper         *ibckeeper.Keeper
 	EvidenceKeeper    *evidencekeeper.Keeper
 	IbcTransferKeeper *ibctransferkeeper.Keeper
@@ -274,6 +295,7 @@ type AltheaApp struct { // nolint: golint
 	Erc20Keeper       *erc20keeper.Keeper
 	FeemarketKeeper   *feemarketkeeper.Keeper
 	IcaHostKeeper     *icahostkeeper.Keeper
+	GroupKeeper       *groupkeeper.Keeper
 
 	LockupKeeper     *lockupkeeper.Keeper
 	MicrotxKeeper    *microtxkeeper.Keeper
@@ -364,6 +386,9 @@ func (app AltheaApp) ValidateMembers() {
 	if app.IcaHostKeeper == nil {
 		panic("Nil IcaHostKeeper!")
 	}
+	if app.GroupKeeper == nil {
+		panic("Nil GroupKeeper!")
+	}
 
 	if app.LockupKeeper == nil {
 		panic("Nil LockupKeeper!")
@@ -417,6 +442,8 @@ func NewAltheaApp(
 	legacyAmino := encodingConfig.Amino
 	interfaceRegistry := encodingConfig.InterfaceRegistry
 
+	eip712.SetEncodingConfig(simappparams.EncodingConfig(encodingConfig))
+
 	// Baseapp initialization, provides correct implementation of ABCI layer, I/O services, state storage, and more
 	bApp := *baseapp.NewBaseApp(Name, logger, db, encodingConfig.TxConfig.TxDecoder(), baseAppOptions...)
 	bApp.SetCommitMultiStoreTracer(traceStore)
@@ -431,7 +458,8 @@ func NewAltheaApp(
 		ibchost.StoreKey, upgradetypes.StoreKey, evidencetypes.StoreKey,
 		ibctransfertypes.StoreKey, capabilitytypes.StoreKey,
 		erc20types.StoreKey, evmtypes.StoreKey, feemarkettypes.StoreKey,
-		icahosttypes.StoreKey,
+		icahosttypes.StoreKey, group.StoreKey,
+		// TODO: Add feegrant
 
 		lockuptypes.StoreKey, microtxtypes.StoreKey, gasfreetypes.StoreKey,
 		onboardingtypes.StoreKey, nativedextypes.StoreKey,
@@ -444,6 +472,12 @@ func NewAltheaApp(
 	// bidirectional capability references for efficient lookup, but the KV store only contains a one-way mapping
 	memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey)
 
+	// load state streaming if enabled
+	if _, _, err := streaming.LoadStreamingServices(&bApp, appOpts, appCodec, keys); err != nil {
+		fmt.Printf("failed to load state streaming: %s", err)
+		os.Exit(1)
+	}
+
 	// nolint: exhaustruct
 	app := &AltheaApp{
 		BaseApp:           &bApp,
@@ -466,7 +500,7 @@ func NewAltheaApp(
 	paramsKeeper := initParamsKeeper(appCodec, legacyAmino, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey])
 	app.ParamsKeeper = &paramsKeeper
 
-	bApp.SetParamStore(paramsKeeper.Subspace(baseapp.Paramspace).WithKeyTable(paramskeeper.ConsensusParamsKeyTable()))
+	bApp.SetParamStore(paramsKeeper.Subspace(baseapp.Paramspace).WithKeyTable(paramstypes.ConsensusParamsKeyTable()))
 
 	// Capability keeper has the function to create "Scoped Keepers" which partition the capabilities a module is aware of
 	// for security. Create all Scoped Keepers between here and the capabilityKeeper.Seal() call below.
@@ -495,6 +529,7 @@ func NewAltheaApp(
 		app.GetSubspace(authtypes.ModuleName),
 		authtypes.ProtoBaseAccount,
 		maccPerms,
+		altheacfg.Bech32PrefixAccAddr,
 	)
 	app.AccountKeeper = &accountKeeper
 
@@ -502,6 +537,7 @@ func NewAltheaApp(
 		keys[authzkeeper.StoreKey],
 		appCodec,
 		bApp.MsgServiceRouter(),
+		accountKeeper,
 	)
 	app.AuthzKeeper = &authzKeeper
 
@@ -531,7 +567,6 @@ func NewAltheaApp(
 		bankKeeper,
 		stakingKeeper,
 		authtypes.FeeCollectorName,
-		app.ModuleAccountAddrs(),
 	)
 	app.DistrKeeper = &distrKeeper
 
@@ -549,6 +584,7 @@ func NewAltheaApp(
 		appCodec,
 		homePath,
 		&bApp,
+		authtypes.NewModuleAddress(govtypes.ModuleName).String(),
 	)
 	app.UpgradeKeeper = &upgradeKeeper
 
@@ -566,23 +602,29 @@ func NewAltheaApp(
 	tracer := cast.ToString(appOpts.Get(ethermintsrvflags.EVMTracer))
 
 	// Feemarket implements EIP-1559 (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) on Cosmos
+	fmSs := app.GetSubspace(feemarkettypes.ModuleName)
 	feemarketKeeper := feemarketkeeper.NewKeeper(
-		appCodec, app.GetSubspace(feemarkettypes.ModuleName),
+		appCodec,
+		authtypes.NewModuleAddress(govtypes.ModuleName),
 		keys[feemarkettypes.StoreKey], tkeys[feemarkettypes.TransientKey],
+		fmSs,
 	)
 	app.FeemarketKeeper = &feemarketKeeper
 
 	// EVM calls the go-ethereum source code within ABCI to implement an EVM within Althea Chain
+	evmSs := app.GetSubspace(evmtypes.ModuleName)
 	evmKeeper := *evmkeeper.NewKeeper(
 		appCodec,
 		keys[evmtypes.StoreKey],
 		tkeys[evmtypes.TransientKey],
-		app.GetSubspace(evmtypes.ModuleName),
+		authtypes.NewModuleAddress(govtypes.ModuleName),
 		accountKeeper,
 		bankKeeper,
 		stakingKeeper,
 		feemarketKeeper,
+		nil, geth.NewEVM,
 		tracer,
+		evmSs,
 		ethtypes.ProtoAccountWithAddress,
 	)
 
@@ -619,7 +661,7 @@ func NewAltheaApp(
 
 	icaHostKeeper := icahostkeeper.NewKeeper(
 		appCodec, keys[icahosttypes.StoreKey], app.GetSubspace(icahosttypes.SubModuleName),
-		ibcKeeper.ChannelKeeper, &ibcKeeper.PortKeeper,
+		ibcKeeper.ChannelKeeper, ibcKeeper.ChannelKeeper, &ibcKeeper.PortKeeper,
 		accountKeeper, scopedICAHostKeeper, app.MsgServiceRouter(),
 	)
 	app.IcaHostKeeper = &icaHostKeeper
@@ -669,6 +711,8 @@ func NewAltheaApp(
 	)
 	app.CrisisKeeper = &crisisKeeper
 
+	// TODO: add feegrant keeper
+
 	// Nativedex allows management of the native DEX instance from the Cosmos side
 	nativedexKeeper := nativedexkeeper.NewKeeper(
 		keys[nativedextypes.StoreKey], appCodec, app.GetSubspace(nativedextypes.ModuleName),
@@ -677,8 +721,8 @@ func NewAltheaApp(
 	app.NativedexKeeper = &nativedexKeeper
 
 	// Register custom governance proposal logic via router keys and handler functions
-	govRouter := govtypes.NewRouter()
-	govRouter.AddRoute(govtypes.RouterKey, govtypes.ProposalHandler).
+	govRouter := govv1beta1.NewRouter()
+	govRouter.AddRoute(govtypes.RouterKey, govv1beta1.ProposalHandler).
 		AddRoute(paramsproposal.RouterKey, params.NewParamChangeProposalHandler(paramsKeeper)).
 		AddRoute(distrtypes.RouterKey, distr.NewCommunityPoolSpendProposalHandler(distrKeeper)).
 		AddRoute(upgradetypes.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(upgradeKeeper)).
@@ -686,6 +730,7 @@ func NewAltheaApp(
 		AddRoute(erc20types.RouterKey, erc20.NewErc20ProposalHandler(&erc20Keeper)).
 		AddRoute(nativedextypes.RouterKey, nativedex.NewNativeDexProposalHandler(&nativedexKeeper))
 
+	govConfig := govtypes.DefaultConfig()
 	govKeeper := govkeeper.NewKeeper(
 		appCodec,
 		keys[govtypes.StoreKey],
@@ -694,7 +739,13 @@ func NewAltheaApp(
 		bankKeeper,
 		stakingKeeper,
 		govRouter,
+		app.MsgServiceRouter(),
+		govConfig,
 	)
+	govKeeper = *govKeeper.SetHooks(govtypes.NewMultiGovHooks(
+	// Register any governance hooks here
+	))
+
 	app.GovKeeper = &govKeeper
 
 	evidenceKeeper := *evidencekeeper.NewKeeper(
@@ -705,6 +756,10 @@ func NewAltheaApp(
 	)
 	app.EvidenceKeeper = &evidenceKeeper
 
+	groupConfig := group.DefaultConfig()
+	groupKeeper := groupkeeper.NewKeeper(keys[group.StoreKey], appCodec, app.MsgServiceRouter(), app.AccountKeeper, groupConfig)
+	app.GroupKeeper = &groupKeeper
+
 	// Althea custom modules
 
 	// Lockup locks the chain at genesis to prevent native token transfers before the chain is sufficiently decentralized
@@ -745,6 +800,7 @@ func NewAltheaApp(
 			accountKeeper,
 			nil,
 		),
+		vesting.NewAppModule(accountKeeper, bankKeeper),
 		authzmodule.NewAppModule(
 			appCodec,
 			authzKeeper,
@@ -765,6 +821,7 @@ func NewAltheaApp(
 			&crisisKeeper,
 			skipGenesisInvariants,
 		),
+		// TODO: Add feegrant app module
 		gov.NewAppModule(
 			appCodec,
 			govKeeper,
@@ -775,6 +832,7 @@ func NewAltheaApp(
 			appCodec,
 			mintKeeper,
 			accountKeeper,
+			nil,
 		),
 		slashing.NewAppModule(
 			appCodec,
@@ -790,7 +848,8 @@ func NewAltheaApp(
 			bankKeeper,
 			stakingKeeper,
 		),
-		staking.NewAppModule(appCodec,
+		staking.NewAppModule(
+			appCodec,
 			stakingKeeper,
 			accountKeeper,
 			bankKeeper,
@@ -800,15 +859,16 @@ func NewAltheaApp(
 		ibc.NewAppModule(&ibcKeeper),
 		params.NewAppModule(paramsKeeper),
 		ibcTransferAppModule,
-		evm.NewAppModule(&evmKeeper, accountKeeper),
+		feemarket.NewAppModule(feemarketKeeper, fmSs),
+		evm.NewAppModule(&evmKeeper, accountKeeper, evmSs),
 		erc20.NewAppModule(erc20Keeper, accountKeeper),
-		feemarket.NewAppModule(feemarketKeeper),
 		icaAppModule,
 		gasfree.NewAppModule(gasfreeKeeper),
 		lockup.NewAppModule(lockupKeeper, bankKeeper),
 		microtx.NewAppModule(microtxKeeper, accountKeeper),
 		onboarding.NewAppModule(onboardingKeeper),
 		nativedex.NewAppModule(nativedexKeeper, accountKeeper),
+		groupmodule.NewAppModule(appCodec, groupKeeper, accountKeeper, bankKeeper, interfaceRegistry),
 	)
 	app.MM = &mm
 
@@ -830,14 +890,16 @@ func NewAltheaApp(
 		evidencetypes.ModuleName,
 		stakingtypes.ModuleName,
 		ibchost.ModuleName,
+		ibctransfertypes.ModuleName,
+		authtypes.ModuleName,
 		banktypes.ModuleName,
+		govtypes.ModuleName,
 		crisistypes.ModuleName,
-		authtypes.ModuleName,
-		ibctransfertypes.ModuleName,
 		genutiltypes.ModuleName,
 		authz.ModuleName,
-		govtypes.ModuleName,
+		//TODO: Add feegrant
 		paramstypes.ModuleName,
+		vestingtypes.ModuleName,
 		gasfreetypes.ModuleName,
 		lockuptypes.ModuleName,
 		microtxtypes.ModuleName,
@@ -845,6 +907,7 @@ func NewAltheaApp(
 		onboardingtypes.ModuleName,
 		nativedextypes.ModuleName,
 		icatypes.ModuleName,
+		group.ModuleName,
 	)
 
 	// Determine the order in which modules' EndBlock() functions are called each block
@@ -853,28 +916,31 @@ func NewAltheaApp(
 		crisistypes.ModuleName,
 		govtypes.ModuleName,
 		stakingtypes.ModuleName,
-		icatypes.ModuleName,
-		evmtypes.ModuleName,
 		feemarkettypes.ModuleName,
-		upgradetypes.ModuleName,
+		evmtypes.ModuleName,
+		erc20types.ModuleName,
+		ibchost.ModuleName,
+		ibctransfertypes.ModuleName,
+		icatypes.ModuleName,
 		capabilitytypes.ModuleName,
-		minttypes.ModuleName,
+		authtypes.ModuleName,
+		banktypes.ModuleName,
 		distrtypes.ModuleName,
 		slashingtypes.ModuleName,
-		evidencetypes.ModuleName,
-		onboardingtypes.ModuleName,
-		ibchost.ModuleName,
-		banktypes.ModuleName,
-		authtypes.ModuleName,
-		ibctransfertypes.ModuleName,
+		minttypes.ModuleName,
 		genutiltypes.ModuleName,
+		evidencetypes.ModuleName,
 		authz.ModuleName,
+		// TODO: add feegrant
 		paramstypes.ModuleName,
+		upgradetypes.ModuleName,
+		vestingtypes.ModuleName,
+		onboardingtypes.ModuleName,
 		gasfreetypes.ModuleName,
 		lockuptypes.ModuleName,
 		microtxtypes.ModuleName,
 		nativedextypes.ModuleName,
-		erc20types.ModuleName,
+		group.ModuleName,
 	)
 
 	// Determine the order in which modules' InitGenesis() functions are called at chain genesis
@@ -888,23 +954,26 @@ func NewAltheaApp(
 		slashingtypes.ModuleName,
 		govtypes.ModuleName,
 		minttypes.ModuleName,
-		upgradetypes.ModuleName,
 		ibchost.ModuleName,
 		evmtypes.ModuleName,
 		feemarkettypes.ModuleName,
 		genutiltypes.ModuleName,
 		evidencetypes.ModuleName,
 		ibctransfertypes.ModuleName,
+		icatypes.ModuleName,
 		authz.ModuleName,
+		// TODO: add feegrant
 		paramstypes.ModuleName,
+		upgradetypes.ModuleName,
+		vestingtypes.ModuleName,
 		gasfreetypes.ModuleName,
 		lockuptypes.ModuleName,
 		microtxtypes.ModuleName,
 		erc20types.ModuleName,
 		onboardingtypes.ModuleName,
 		nativedextypes.ModuleName,
+		group.ModuleName,
 		crisistypes.ModuleName,
-		icatypes.ModuleName,
 	)
 
 	// --------------------------------------------------------------------------
@@ -923,7 +992,7 @@ func NewAltheaApp(
 		bank.NewAppModule(appCodec, bankKeeper, accountKeeper),
 		capability.NewAppModule(appCodec, capabilityKeeper),
 		gov.NewAppModule(appCodec, govKeeper, accountKeeper, bankKeeper),
-		mint.NewAppModule(appCodec, mintKeeper, accountKeeper),
+		mint.NewAppModule(appCodec, mintKeeper, accountKeeper, nil),
 		staking.NewAppModule(appCodec, stakingKeeper, accountKeeper, bankKeeper),
 		distr.NewAppModule(appCodec, distrKeeper, accountKeeper, bankKeeper, stakingKeeper),
 		slashing.NewAppModule(appCodec, slashingKeeper, accountKeeper, bankKeeper, stakingKeeper),
@@ -931,8 +1000,8 @@ func NewAltheaApp(
 		evidence.NewAppModule(evidenceKeeper),
 		ibc.NewAppModule(&ibcKeeper),
 		ibcTransferAppModule,
-		evm.NewAppModule(&evmKeeper, accountKeeper),
-		feemarket.NewAppModule(feemarketKeeper),
+		evm.NewAppModule(&evmKeeper, accountKeeper, evmSs),
+		feemarket.NewAppModule(feemarketKeeper, fmSs),
 	)
 	app.sm = &sm
 
@@ -950,18 +1019,12 @@ func NewAltheaApp(
 
 	// Create the chain of mempool Tx filter functions, aka the AnteHandler
 
-	options := app.NewAnteHandlerOptions(appOpts)
-	if err := options.Validate(); err != nil {
-		panic(fmt.Errorf("invalid antehandler options: %v", err))
-	}
-	ah := ante.NewAnteHandler(options)
-
-	// Create the lockup AnteHandler, to ensure sufficient decentralization before funds may be transferred
-	lockupAnteHandler := lockup.NewWrappedLockupAnteHandler(ah, lockupKeeper, appCodec)
-	app.SetAnteHandler(lockupAnteHandler)
+	app.setAnteHandler(appOpts)
+	app.setPostHandler()
 
 	// Register the configured upgrades for the upgrade module
 	app.registerUpgradeHandlers()
+	app.registerStoreLoaders()
 
 	if loadLatest {
 		if err := app.LoadLatestVersion(); err != nil {
@@ -1012,6 +1075,29 @@ func (app *AltheaApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) ab
 	return app.MM.InitGenesis(ctx, app.appCodec, genesisState)
 }
 
+func (app *AltheaApp) setAnteHandler(appOpts servertypes.AppOptions) {
+	options := app.NewAnteHandlerOptions(appOpts)
+	if err := options.Validate(); err != nil {
+		panic(fmt.Errorf("invalid antehandler options: %v", err))
+	}
+	ah := ante.NewAnteHandler(options)
+
+	// Create the lockup AnteHandler, to ensure sufficient decentralization before funds may be transferred
+	lockupAnteHandler := lockup.NewWrappedLockupAnteHandler(ah, *app.LockupKeeper, app.AppCodec())
+	app.SetAnteHandler(lockupAnteHandler)
+}
+
+func (app *AltheaApp) setPostHandler() {
+	postHandler, err := posthandler.NewPostHandler(
+		posthandler.HandlerOptions{},
+	)
+	if err != nil {
+		panic(err)
+	}
+
+	app.SetPostHandler(postHandler)
+}
+
 // LoadHeight loads the blockchain a particular height
 func (app *AltheaApp) LoadHeight(height int64) error {
 	return app.LoadVersion(height)
@@ -1063,21 +1149,21 @@ func (app *AltheaApp) InterfaceRegistry() types.InterfaceRegistry {
 // GetKey returns the KVStoreKey for the provided store key.
 //
 // NOTE: This is solely to be used for testing purposes.
-func (app *AltheaApp) GetKey(storeKey string) *sdk.KVStoreKey {
+func (app *AltheaApp) 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 *AltheaApp) GetTKey(storeKey string) *sdk.TransientStoreKey {
+func (app *AltheaApp) 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 *AltheaApp) GetMemKey(storeKey string) *sdk.MemoryStoreKey {
+func (app *AltheaApp) GetMemKey(storeKey string) *storetypes.MemoryStoreKey {
 	return app.memKeys[storeKey]
 }
 
@@ -1085,7 +1171,7 @@ func (app *AltheaApp) GetMemKey(storeKey string) *sdk.MemoryStoreKey {
 func (app *AltheaApp) GetBaseApp() *baseapp.BaseApp { return app.BaseApp }
 
 // GetStakingKeeper returns the staking Keeper, used for testing
-func (app *AltheaApp) GetStakingKeeper() stakingkeeper.Keeper { return *app.StakingKeeper }
+func (app *AltheaApp) GetStakingKeeper() ibctesting.StakingKeeper { return *app.StakingKeeper }
 
 // GetIBCKeeper returns the IBC Keeper, used for testing
 func (app *AltheaApp) GetIBCKeeper() *ibckeeper.Keeper { return app.IbcKeeper }
@@ -1112,14 +1198,6 @@ func (app *AltheaApp) SimulationManager() *module.SimulationManager {
 // API server.
 func (app *AltheaApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) {
 	clientCtx := apiSvr.ClientCtx
-	// SDK /node_info, /syncing, /blocks, and /validatorsets REST endpoints
-	rpc.RegisterRoutes(clientCtx, apiSvr.Router)
-
-	// Note: Delegates requests to the EVM if given a hash variable with a leading "0x"
-	evmrest.RegisterTxRoutes(clientCtx, apiSvr.Router) // Cosmos and EVM /txs REST endpoints
-
-	// Note: The Cosmos REST registration has been replaced by evmrest's
-	// authrest.RegisterTxRoutes(clientCtx, apiSvr.Router) // Cosmos /txs REST endpoints
 
 	// GRPC endpoints under /cosmos.base.tendermint.v1beta1.Service
 	// including GetNodeInfo, GetSyncing, GetLatestBlock, GetBlockByHeight, GetLatestValidatorSet, GetValidatorSetByHeight
@@ -1129,17 +1207,33 @@ func (app *AltheaApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.API
 	// including Simulate, GetTx, BroadcastTx, GetTxsEvent, GetBlockWithTxs
 	authtx.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter)
 
-	// Register all REST routes declared by modules in the ModuleBasics
-	ModuleBasics.RegisterRESTRoutes(clientCtx, apiSvr.Router)
 	// Register all GRPC routes declared by modules in the ModuleBasics
 	ModuleBasics.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter)
 
+	nodeservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter)
+
 	// TODO: build the custom swagger files and add here?
 	if apiConfig.Swagger {
 		RegisterSwaggerAPI(clientCtx, apiSvr.Router)
 	}
 }
 
+// RegisterTxService registers all Protobuf-based Tx receiving gRPC services based on what is registered in the
+// interface registry. These are stapled on to the baseapp's gRPC Query router
+func (app *AltheaApp) RegisterTxService(clientCtx client.Context) {
+	authtx.RegisterTxService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.BaseApp.Simulate, app.interfaceRegistry)
+}
+
+// RegisterTendermintService registers the /cosmos.base.tendermint.v1beta1.Service query endpoints on the baseapp's
+// gRPC query router
+func (app *AltheaApp) RegisterTendermintService(clientCtx client.Context) {
+	tmservice.RegisterTendermintService(clientCtx, app.BaseApp.GRPCQueryRouter(), app.interfaceRegistry, app.Query)
+}
+
+func (app *AltheaApp) RegisterNodeService(clientCtx client.Context) {
+	nodeservice.RegisterNodeService(clientCtx, app.GRPCQueryRouter())
+}
+
 // RegisterSwaggerAPI registers swagger route with API Server
 // TODO: build the custom swagger files and add here?
 func RegisterSwaggerAPI(ctx client.Context, rtr *mux.Router) {
@@ -1152,18 +1246,6 @@ func RegisterSwaggerAPI(ctx client.Context, rtr *mux.Router) {
 	rtr.PathPrefix("/swagger/").Handler(http.StripPrefix("/swagger/", staticServer))
 }
 
-// RegisterTxService registers all Protobuf-based Tx receiving gRPC services based on what is registered in the
-// interface registry. These are stapled on to the baseapp's gRPC Query router
-func (app *AltheaApp) RegisterTxService(clientCtx client.Context) {
-	authtx.RegisterTxService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.BaseApp.Simulate, app.interfaceRegistry)
-}
-
-// RegisterTendermintService registers the /cosmos.base.tendermint.v1beta1.Service query endpoints on the baseapp's
-// gRPC query router
-func (app *AltheaApp) RegisterTendermintService(clientCtx client.Context) {
-	tmservice.RegisterTendermintService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.interfaceRegistry)
-}
-
 // GetMaccPerms returns a copy of the module account permissions
 func GetMaccPerms() map[string][]string {
 	dupMaccPerms := make(map[string][]string)
@@ -1174,7 +1256,7 @@ func GetMaccPerms() map[string][]string {
 }
 
 // initParamsKeeper constructs params' keeper and all module param subspaces
-func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey sdk.StoreKey) paramskeeper.Keeper {
+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)
@@ -1183,16 +1265,16 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino
 	paramsKeeper.Subspace(minttypes.ModuleName)
 	paramsKeeper.Subspace(distrtypes.ModuleName)
 	paramsKeeper.Subspace(slashingtypes.ModuleName)
-	paramsKeeper.Subspace(govtypes.ModuleName).WithKeyTable(govtypes.ParamKeyTable())
+	paramsKeeper.Subspace(govtypes.ModuleName).WithKeyTable(govv1.ParamKeyTable())
 	paramsKeeper.Subspace(crisistypes.ModuleName)
 	paramsKeeper.Subspace(ibctransfertypes.ModuleName)
 	paramsKeeper.Subspace(ibchost.ModuleName)
 	paramsKeeper.Subspace(lockuptypes.ModuleName)
 	paramsKeeper.Subspace(microtxtypes.ModuleName)
 	paramsKeeper.Subspace(gasfreetypes.ModuleName)
-	paramsKeeper.Subspace(evmtypes.ModuleName)
+	paramsKeeper.Subspace(evmtypes.ModuleName).WithKeyTable(evmtypes.ParamKeyTable())
 	paramsKeeper.Subspace(erc20types.ModuleName)
-	paramsKeeper.Subspace(feemarkettypes.ModuleName)
+	paramsKeeper.Subspace(feemarkettypes.ModuleName).WithKeyTable(feemarkettypes.ParamKeyTable())
 	paramsKeeper.Subspace(icahosttypes.SubModuleName)
 	paramsKeeper.Subspace(onboardingtypes.ModuleName)
 	paramsKeeper.Subspace(nativedextypes.ModuleName)
@@ -1202,7 +1284,37 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino
 
 // registerUpgradeHandlers registers in-place upgrades, which are faster and easier than genesis-based upgrades
 func (app *AltheaApp) registerUpgradeHandlers() {
-	// No op
+	upgrades.RegisterUpgradeHandlers(
+		app.MM, app.Configurator, app.UpgradeKeeper, app.CrisisKeeper,
+	)
+}
+
+// registerStoreLoaders handles special upgrades where module stores are added, removed, or renamed
+func (app *AltheaApp) registerStoreLoaders() {
+	upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk()
+	if err != nil {
+		panic(fmt.Sprintf("failed to read upgrade info from disk %s", err))
+	}
+	if app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) {
+		return
+	}
+
+	// STORE LOADER CONFIGURATION:
+	// Added: []string{"newmodule"}, // We are adding these modules
+	// Renamed: []storetypes.StoreRename{{"foo", "bar"}}, example foo to bar rename
+	// Deleted: []string{"bazmodule"}, example deleted bazmodule
+
+	// <name> Group module store loader setup
+	if upgradeInfo.Name == neutrino.PlanName {
+		// Register the Group module as a new module that needs a new store allocated
+		storeUpgrades := storetypes.StoreUpgrades{
+			Added:   []string{group.StoreKey},
+			Renamed: nil,
+			Deleted: nil,
+		}
+
+		app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades))
+	}
 }
 
 func (app *AltheaApp) NewAnteHandlerOptions(appOpts servertypes.AppOptions) ante.HandlerOptions {
@@ -1210,15 +1322,22 @@ func (app *AltheaApp) NewAnteHandlerOptions(appOpts servertypes.AppOptions) ante
 	return ante.HandlerOptions{
 		AccountKeeper:   app.AccountKeeper,
 		BankKeeper:      app.BankKeeper,
-		IBCKeeper:       app.IbcKeeper,
-		FeeMarketKeeper: app.FeemarketKeeper,
-		EvmKeeper:       app.EvmKeeper,
-		FeegrantKeeper:  nil,
 		SignModeHandler: app.EncodingConfig.TxConfig.SignModeHandler(),
-		SigGasConsumer:  SigVerificationGasConsumer,
-		Cdc:             app.AppCodec(),
-		MaxTxGasWanted:  maxGasWanted,
-		GasfreeKeeper:   app.GasfreeKeeper,
-		MicrotxKeeper:   app.MicrotxKeeper,
+		// TODO: Add feegrant
+		FeegrantKeeper:         nil,
+		SigGasConsumer:         SigVerificationGasConsumer,
+		IBCKeeper:              app.IbcKeeper,
+		EvmKeeper:              app.EvmKeeper,
+		FeeMarketKeeper:        *app.FeemarketKeeper,
+		MaxTxGasWanted:         maxGasWanted,
+		ExtensionOptionChecker: ante.CosmosExtensionOptionChecker,
+		TxFeeChecker:           ethante.NewDynamicFeeChecker(app.EvmKeeper),
+		DisabledAuthzMsgs: []string{
+			sdk.MsgTypeURL((&evmtypes.MsgEthereumTx{})),
+			sdk.MsgTypeURL((&vestingtypes.MsgCreateVestingAccount{})),
+		},
+		Cdc:           app.AppCodec(),
+		GasfreeKeeper: app.GasfreeKeeper,
+		MicrotxKeeper: app.MicrotxKeeper,
 	}
 }
diff --git a/app/migrate.go b/app/migrate.go
index 0d417836..db34f55e 100644
--- a/app/migrate.go
+++ b/app/migrate.go
@@ -21,10 +21,10 @@ import (
 	"github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
 	"github.com/cosmos/cosmos-sdk/x/genutil/types"
 	staking "github.com/cosmos/cosmos-sdk/x/staking/types"
-	ibcxfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
-	host "github.com/cosmos/ibc-go/v4/modules/core/24-host"
-	"github.com/cosmos/ibc-go/v4/modules/core/exported"
-	ibccoretypes "github.com/cosmos/ibc-go/v4/modules/core/types"
+	ibcxfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
+	host "github.com/cosmos/ibc-go/v6/modules/core/24-host"
+	"github.com/cosmos/ibc-go/v6/modules/core/exported"
+	ibccoretypes "github.com/cosmos/ibc-go/v6/modules/core/types"
 	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 	tmjson "github.com/tendermint/tendermint/libs/json"
diff --git a/app/sigverify.go b/app/sigverify.go
index a424b773..130703dd 100644
--- a/app/sigverify.go
+++ b/app/sigverify.go
@@ -6,7 +6,7 @@ import (
 	"github.com/cosmos/cosmos-sdk/types/tx/signing"
 	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
 
-	canto "github.com/Canto-Network/Canto/v5/app"
+	canto "github.com/Canto-Network/Canto/v6/app"
 )
 
 const (
diff --git a/app/upgrades/neutrino/README.md b/app/upgrades/neutrino/README.md
new file mode 100644
index 00000000..c71b0539
--- /dev/null
+++ b/app/upgrades/neutrino/README.md
@@ -0,0 +1,9 @@
+# Neutrino UPGRADE
+
+The *Neutrino* upgrade contains the following changes.
+
+## Summary of Changes
+
+* Update the Cosmos SDK to v0.46.16, IBC to v6.3.1
+* Update Ethermint to 0.22
+* Update CometBFT to v0.34.35 to address multiple CVEs
\ No newline at end of file
diff --git a/app/upgrades/neutrino/handler.go b/app/upgrades/neutrino/handler.go
new file mode 100644
index 00000000..fa9d9a13
--- /dev/null
+++ b/app/upgrades/neutrino/handler.go
@@ -0,0 +1,32 @@
+package neutrino
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/module"
+	crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper"
+	upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
+)
+
+var PlanName = "neutrino"
+
+func GetNeutrinoUpgradeHandler(
+	mm *module.Manager, configurator *module.Configurator, crisisKeeper *crisiskeeper.Keeper,
+) func(
+	ctx sdk.Context, plan upgradetypes.Plan, vmap module.VersionMap,
+) (module.VersionMap, error) {
+	if mm == nil {
+		panic("Nil argument to GetNeutrinoUpgradeHandler")
+	}
+	return func(ctx sdk.Context, plan upgradetypes.Plan, vmap module.VersionMap) (module.VersionMap, error) {
+		ctx.Logger().Info("Module Consensus Version Map", "vmap", vmap)
+
+		ctx.Logger().Info("Neutrino Upgrade: Running any configured module migrations")
+		out, outErr := mm.RunMigrations(ctx, *configurator, vmap)
+
+		ctx.Logger().Info("Asserting invariants after upgrade")
+		crisisKeeper.AssertInvariants(ctx)
+
+		ctx.Logger().Info("Neutrino Upgrade Successful")
+		return out, outErr
+	}
+}
diff --git a/app/upgrades/register.go b/app/upgrades/register.go
new file mode 100644
index 00000000..cc4bc419
--- /dev/null
+++ b/app/upgrades/register.go
@@ -0,0 +1,26 @@
+package upgrades
+
+import (
+	"github.com/cosmos/cosmos-sdk/types/module"
+	crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper"
+	upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper"
+
+	"github.com/AltheaFoundation/althea-L1/app/upgrades/neutrino"
+)
+
+// RegisterUpgradeHandlers registers handlers for all upgrades
+// Note: This method has crazy parameters because of circular import issues, I didn't want to make an AltheaApp struct
+// along with an interface
+func RegisterUpgradeHandlers(
+	mm *module.Manager, configurator *module.Configurator, upgradeKeeper *upgradekeeper.Keeper,
+	crisisKeeper *crisiskeeper.Keeper,
+) {
+	if mm == nil || configurator == nil || crisisKeeper == nil || upgradeKeeper == nil {
+		panic("Nil argument to RegisterUpgradeHandlers()!")
+	}
+	// Neutrino upgrade
+	upgradeKeeper.SetUpgradeHandler(
+		neutrino.PlanName,
+		neutrino.GetNeutrinoUpgradeHandler(mm, configurator, crisisKeeper),
+	)
+}
diff --git a/cmd/althea/genaccounts.go b/cmd/althea/genaccounts.go
index 8bff951a..1ce21ef4 100644
--- a/cmd/althea/genaccounts.go
+++ b/cmd/althea/genaccounts.go
@@ -40,7 +40,7 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa
 		Args: cobra.ExactArgs(2),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			clientCtx := client.GetClientContextFromCmd(cmd)
-			depCdc := clientCtx.JSONCodec
+			depCdc := clientCtx.Codec
 			cdc := depCdc.(codec.Codec)
 
 			serverCtx := server.GetServerContextFromCmd(cmd)
@@ -57,7 +57,7 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa
 				}
 
 				// attempt to lookup address from Keybase if no address was provided
-				kb, err := keyring.New(sdk.KeyringServiceName(), keyringBackend, clientCtx.HomeDir, inBuf)
+				kb, err := keyring.New(sdk.KeyringServiceName(), keyringBackend, clientCtx.HomeDir, inBuf, cdc)
 				if err != nil {
 					return err
 				}
@@ -67,7 +67,10 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa
 					return fmt.Errorf("failed to get address from Keybase: %w", err)
 				}
 
-				addr = info.GetAddress()
+				addr, err = info.GetAddress()
+				if err != nil {
+					return fmt.Errorf("failed to parse address: %w", err)
+				}
 			}
 
 			coins, err := sdk.ParseCoinsNormalized(args[1])
diff --git a/cmd/althea/root.go b/cmd/althea/root.go
index da4fc4d8..8f4b1641 100644
--- a/cmd/althea/root.go
+++ b/cmd/althea/root.go
@@ -18,7 +18,6 @@ import (
 	tmtypes "github.com/tendermint/tendermint/types"
 	dbm "github.com/tendermint/tm-db"
 
-	"github.com/cosmos/cosmos-sdk/baseapp"
 	"github.com/cosmos/cosmos-sdk/client"
 	"github.com/cosmos/cosmos-sdk/client/config"
 	"github.com/cosmos/cosmos-sdk/client/debug"
@@ -27,8 +26,6 @@ import (
 	"github.com/cosmos/cosmos-sdk/client/rpc"
 	"github.com/cosmos/cosmos-sdk/server"
 	servertypes "github.com/cosmos/cosmos-sdk/server/types"
-	"github.com/cosmos/cosmos-sdk/snapshots"
-	"github.com/cosmos/cosmos-sdk/store"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	"github.com/cosmos/cosmos-sdk/types/module"
 	authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
@@ -44,6 +41,7 @@ import (
 	"github.com/spf13/cobra"
 
 	// EVM
+
 	ethermintclient "github.com/evmos/ethermint/client"
 	ethermintserver "github.com/evmos/ethermint/server"
 	ethermintserverconfig "github.com/evmos/ethermint/server/config"
@@ -117,7 +115,12 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) {
 			cmd.SetOut(cmd.OutOrStdout())
 			cmd.SetErr(cmd.ErrOrStderr())
 
-			initClientCtx, err := config.ReadFromClientConfig(initClientCtx)
+			initClientCtx, err := client.ReadPersistentCommandFlags(initClientCtx, cmd.Flags())
+			if err != nil {
+				return err
+			}
+
+			initClientCtx, err = config.ReadFromClientConfig(initClientCtx)
 			if err != nil {
 				return fmt.Errorf("unable to update context with client.toml config: %v", err)
 			}
@@ -131,7 +134,7 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) {
 
 			// Takes all these configurations and applies them, additionally configuring Tendermint
 			// to adhere to these desires
-			return server.InterceptConfigsPreRunHandler(cmd, altheaAppTemplate, altheaAppConfig)
+			return server.InterceptConfigsPreRunHandler(cmd, altheaAppTemplate, altheaAppConfig, initTendermintConfig())
 		},
 	}
 
@@ -149,6 +152,18 @@ func initAppConfig() (string, interface{}) {
 	return appTempl, appCfg
 }
 
+// initTendermintConfig helps to override default Tendermint Config values.
+// return tmcfg.DefaultConfig if no custom configuration is required for the application.
+func initTendermintConfig() *cfg.Config {
+	cfg := cfg.DefaultConfig()
+
+	// these values put a higher strain on node memory
+	// cfg.P2P.MaxNumInboundPeers = 100
+	// cfg.P2P.MaxNumOutboundPeers = 40
+
+	return cfg
+}
+
 // Execute executes the root command.
 func Execute(rootCmd *cobra.Command, defaultHome string) error {
 	// Create and set a client.Context on the command's Context. During the pre-run
@@ -187,7 +202,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig *params.EncodingConfig)
 	ac := appCreator{encodingConfig}
 	// The ethermint server commands perform a lot of modifications on top of the base ones, notably setting up the
 	// EVM JSONRPC server, tx indexer, and some various improvements like closing the DB automatically
-	ethermintserver.AddCommands(rootCmd, althea.DefaultNodeHome, ac.newApp, ac.createSimappAndExport, addModuleInitFlags)
+	ethermintserver.AddCommands(rootCmd, ethermintserver.NewDefaultStartOptions(ac.newApp, althea.DefaultNodeHome), ac.createSimappAndExport, addModuleInitFlags)
 
 	rootCmd.AddCommand(ethermintserver.NewIndexTxCmd())
 
@@ -356,6 +371,7 @@ func txCommand() *cobra.Command {
 		authcmd.GetBroadcastCommand(),
 		authcmd.GetEncodeCommand(),
 		authcmd.GetDecodeCommand(),
+		authcmd.GetAuxToFeeCommand(),
 		flags.LineBreak,
 		vestingcli.GetTxCmd(),
 	)
@@ -374,49 +390,20 @@ type appCreator struct {
 // newApp is an AppCreator used for the start command, anything which must be passed to NewAltheaApp (in app.go)
 // can be fetched and added here
 func (a appCreator) newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application {
-	var cache sdk.MultiStorePersistentCache
-
-	if cast.ToBool(appOpts.Get(server.FlagInterBlockCache)) {
-		cache = store.NewCommitKVStoreCacheManager()
-	}
+	baseappOptions := server.DefaultBaseappOptions(appOpts)
 
 	skipUpgradeHeights := make(map[int64]bool)
 	for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) {
 		skipUpgradeHeights[int64(h)] = true
 	}
 
-	pruningOpts, err := server.GetPruningOptionsFromFlags(appOpts)
-	if err != nil {
-		panic(err)
-	}
-
-	snapshotDir := filepath.Join(cast.ToString(appOpts.Get(flags.FlagHome)), "data", "snapshots")
-	snapshotDB, err := sdk.NewLevelDB("metadata", snapshotDir)
-	if err != nil {
-		panic(err)
-	}
-	snapshotStore, err := snapshots.NewStore(snapshotDB, snapshotDir)
-	if err != nil {
-		panic(err)
-	}
-
 	return althea.NewAltheaApp(
 		logger, db, traceStore, true, skipUpgradeHeights,
 		cast.ToString(appOpts.Get(flags.FlagHome)),
 		cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)),
 		*a.encCfg,
 		appOpts,
-		baseapp.SetPruning(pruningOpts),
-		baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(server.FlagMinGasPrices))),
-		baseapp.SetHaltHeight(cast.ToUint64(appOpts.Get(server.FlagHaltHeight))),
-		baseapp.SetHaltTime(cast.ToUint64(appOpts.Get(server.FlagHaltTime))),
-		baseapp.SetMinRetainBlocks(cast.ToUint64(appOpts.Get(server.FlagMinRetainBlocks))),
-		baseapp.SetInterBlockCache(cache),
-		baseapp.SetTrace(cast.ToBool(appOpts.Get(server.FlagTrace))),
-		baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(server.FlagIndexEvents))),
-		baseapp.SetSnapshotStore(snapshotStore),
-		baseapp.SetSnapshotInterval(cast.ToUint64(appOpts.Get(server.FlagStateSyncSnapshotInterval))),
-		baseapp.SetSnapshotKeepRecent(cast.ToUint32(appOpts.Get(server.FlagStateSyncSnapshotKeepRecent))),
+		baseappOptions...,
 	)
 }
 
diff --git a/cmd/althea/testnet.go b/cmd/althea/testnet.go
index 2291cfa9..075cecd0 100644
--- a/cmd/althea/testnet.go
+++ b/cmd/althea/testnet.go
@@ -25,6 +25,7 @@ import (
 	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
 	"github.com/cosmos/cosmos-sdk/server"
 	srvconfig "github.com/cosmos/cosmos-sdk/server/config"
+	"github.com/cosmos/cosmos-sdk/testutil"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	"github.com/cosmos/cosmos-sdk/types/module"
 	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
@@ -166,7 +167,7 @@ func InitTestnet(
 		memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip)
 		genFiles = append(genFiles, nodeConfig.GenesisFile())
 
-		kb, err := keyring.New(sdk.KeyringServiceName(), keyringBackend, nodeDir, inBuf)
+		kb, err := keyring.New(sdk.KeyringServiceName(), keyringBackend, nodeDir, inBuf, clientCtx.Codec)
 		if err != nil {
 			return err
 		}
@@ -177,7 +178,7 @@ func InitTestnet(
 			return err
 		}
 
-		addr, secret, err := server.GenerateSaveCoinKey(kb, nodeDirName, true, algo)
+		addr, secret, err := testutil.GenerateSaveCoinKey(kb, nodeDirName, "", true, algo)
 		if err != nil {
 			_ = os.RemoveAll(outputDir)
 			return err
@@ -269,11 +270,11 @@ func initGenFiles(
 	genFiles []string, numValidators int,
 ) error {
 
-	appGenState := mbm.DefaultGenesis(clientCtx.JSONCodec)
+	appGenState := mbm.DefaultGenesis(clientCtx.Codec)
 
 	// set the accounts in the genesis state
 	var authGenState authtypes.GenesisState
-	clientCtx.JSONCodec.MustUnmarshalJSON(appGenState[authtypes.ModuleName], &authGenState)
+	clientCtx.Codec.MustUnmarshalJSON(appGenState[authtypes.ModuleName], &authGenState)
 
 	accounts, err := authtypes.PackAccounts(genAccounts)
 	if err != nil {
@@ -281,14 +282,14 @@ func initGenFiles(
 	}
 
 	authGenState.Accounts = accounts
-	appGenState[authtypes.ModuleName] = clientCtx.JSONCodec.MustMarshalJSON(&authGenState)
+	appGenState[authtypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(&authGenState)
 
 	// set the balances in the genesis state
 	var bankGenState banktypes.GenesisState
-	clientCtx.JSONCodec.MustUnmarshalJSON(appGenState[banktypes.ModuleName], &bankGenState)
+	clientCtx.Codec.MustUnmarshalJSON(appGenState[banktypes.ModuleName], &bankGenState)
 
 	bankGenState.Balances = genBalances
-	appGenState[banktypes.ModuleName] = clientCtx.JSONCodec.MustMarshalJSON(&bankGenState)
+	appGenState[banktypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(&bankGenState)
 
 	appGenStateJSON, err := json.MarshalIndent(appGenState, "", "  ")
 	if err != nil {
@@ -335,7 +336,7 @@ func collectGenFiles(
 			return err
 		}
 
-		nodeAppState, err := genutil.GenAppStateFromConfig(clientCtx.JSONCodec, clientCtx.TxConfig, nodeConfig, initCfg, *genDoc, genBalIterator)
+		nodeAppState, err := genutil.GenAppStateFromConfig(clientCtx.Codec, clientCtx.TxConfig, nodeConfig, initCfg, *genDoc, genBalIterator)
 		if err != nil {
 			return err
 		}
diff --git a/go.mod b/go.mod
index 9cc2a061..658cbde3 100644
--- a/go.mod
+++ b/go.mod
@@ -1,97 +1,143 @@
 module github.com/AltheaFoundation/althea-L1
 
-go 1.19
+go 1.22.0
+
+toolchain go1.22.7
 
 require (
-	github.com/Canto-Network/Canto/v5 v5.2.0
-	github.com/cosmos/cosmos-sdk v0.45.16
-	github.com/cosmos/ibc-go/v4 v4.3.1
-	github.com/evmos/ethermint v0.19.9
+	github.com/Canto-Network/Canto/v6 v6.0.1
+	github.com/cosmos/cosmos-sdk v0.46.17
+	github.com/cosmos/ibc-go/v6 v6.3.1
+	github.com/evmos/ethermint v0.22.3
+	github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
+	github.com/tendermint/tendermint v0.35.9
+	golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
 )
 
 replace (
-	// Canto unfortunately has their module listed as .../Canto/v2 @ versions v3.0.0, v4.0.0, and v5.0.0
-	// and the v5.x.x releases declare the version as .../Canto/v6
-	github.com/Canto-Network/Canto/v5 => github.com/AltheaFoundation/canto/v5 v5.2.0
-	github.com/btcsuite/btcutil => github.com/btcsuite/btcd/btcutil v1.1.3
-	github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23 v0.0.0-20221014140410-2582f0aab7b2
-	github.com/cosmos/cosmos-sdk => github.com/cosmos/cosmos-sdk v0.45.16
+	github.com/Canto-Network/Canto/v6 => github.com/AltheaFoundation/canto/v6 v6.0.1
+
+	github.com/cosmos/cosmos-sdk => github.com/althea-net/cosmos-sdk v0.46.17
+	github.com/cosmos/ibc-go/v6 => github.com/cosmos/ibc-go/v6 v6.3.1
 
-	github.com/cosmos/cosmos-sdk/simapp => cosmossdk.io/simapp v0.0.0-20230531154223-5097b0c22672
-	// Ethermint's older versions are not compatible with the end of line sdk v0.45.x
-	github.com/evmos/ethermint => github.com/AltheaFoundation/ethermint v0.19.9
+	github.com/evmos/ethermint => github.com/AltheaFoundation/ethermint v0.22.3
 
-	github.com/gogo/grpc => google.golang.org/grpc v1.33.2
 	github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1
-	github.com/tendermint/tendermint => github.com/cometbft/cometbft v0.34.27
+
+	// Fix node query errors like "failed to load state at height 5; version does not exist (latest height: 5)"
+	github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
+
+	// v0.34.35 resolves multiple CVEs
+	github.com/tendermint/tendermint => github.com/cometbft/cometbft v0.34.35
 
 	// Canto forked github.com/evmos/evmos, which was originally github.com/tharsis/evmos
-	github.com/tharsis/evmos => github.com/Canto-Network/Canto/v5 v5.0.1
-	google.golang.org/grpc => google.golang.org/grpc v1.33.2
+	github.com/tharsis/evmos => github.com/Canto-Network/Canto/v6 v6.0.1
+
+	// Fix error "github.com/cosmos/gogoproto@v1.4.7/proto/merge.go:123:28: in call to slices.SortFunc, type func(x *descriptorpb.FileDescriptorProto, y *descriptorpb.FileDescriptorProto) bool of func(x, y *descriptorpb.FileDescriptor Proto) bool {…} does not match inferred type func(a *descriptorpb.FileDescriptorProto, b *descriptorpb.FileDescriptorProto) int for func(a E, b E) int"
+	golang.org/x/exp => golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb
+
 )
 
 require (
 	github.com/cosmos/go-bip39 v1.0.0
-	github.com/ethereum/go-ethereum v1.10.19
+	github.com/ethereum/go-ethereum v1.10.26
 	github.com/gogo/protobuf v1.3.3
-	github.com/golang/protobuf v1.5.3
+	github.com/golang/protobuf v1.5.4
 	github.com/gorilla/mux v1.8.0
 	github.com/grpc-ecosystem/grpc-gateway v1.16.0
 	github.com/pkg/errors v0.9.1
 	github.com/rakyll/statik v0.1.7
-	github.com/spf13/cast v1.5.0
-	github.com/spf13/cobra v1.6.1
-	github.com/stretchr/testify v1.8.2
-	github.com/tendermint/tendermint v0.34.27
+	github.com/spf13/cast v1.7.0
+	github.com/spf13/cobra v1.8.1
+	github.com/stretchr/testify v1.9.0
 	github.com/tendermint/tm-db v0.6.7
-	google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44
-	google.golang.org/grpc v1.54.0
+	google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117
+	google.golang.org/grpc v1.66.1
 )
 
-require github.com/stretchr/objx v0.5.0 // indirect
+require (
+	cloud.google.com/go v0.112.1 // indirect
+	cloud.google.com/go/compute/metadata v0.3.0 // indirect
+	cloud.google.com/go/iam v1.1.6 // indirect
+	cloud.google.com/go/storage v1.38.0 // indirect
+	github.com/aws/aws-sdk-go v1.44.122 // indirect
+	github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
+	github.com/bufbuild/protocompile v0.14.1 // indirect
+	github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
+	github.com/cockroachdb/apd/v2 v2.0.2 // indirect
+	github.com/cosmos/gogoproto v1.4.7 // indirect
+	github.com/go-logr/logr v1.4.1 // indirect
+	github.com/go-logr/stdr v1.2.2 // indirect
+	github.com/go-playground/universal-translator v0.18.0 // indirect
+	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
+	github.com/google/go-cmp v0.6.0 // indirect
+	github.com/google/s2a-go v0.1.7 // indirect
+	github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
+	github.com/googleapis/gax-go/v2 v2.12.3 // indirect
+	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
+	github.com/hashicorp/go-getter v1.7.0 // indirect
+	github.com/hashicorp/go-safetemp v1.0.0 // indirect
+	github.com/hashicorp/go-version v1.6.0 // indirect
+	github.com/jmespath/go-jmespath v0.4.0 // indirect
+	github.com/leodido/go-urn v1.2.1 // indirect
+	github.com/manifoldco/promptui v0.9.0 // indirect
+	github.com/mitchellh/go-homedir v1.1.0 // indirect
+	github.com/mitchellh/go-testing-interface v1.14.1 // indirect
+	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+	github.com/sagikazarmark/locafero v0.6.0 // indirect
+	github.com/sagikazarmark/slog-shim v0.1.0 // indirect
+	github.com/sourcegraph/conc v0.3.0 // indirect
+	github.com/stretchr/objx v0.5.2 // indirect
+	github.com/tidwall/gjson v1.14.4 // 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/ugorji/go/codec v1.2.7 // indirect
+	github.com/ulikunitz/xz v0.5.10 // 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.uber.org/multierr v1.11.0 // indirect
+	golang.org/x/oauth2 v0.22.0 // indirect
+	google.golang.org/api v0.171.0 // indirect
+	google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
+	sigs.k8s.io/yaml v1.3.0 // indirect
+)
 
 require (
-	cosmossdk.io/api v0.2.6 // indirect
-	cosmossdk.io/core v0.5.1 // indirect
-	cosmossdk.io/depinject v1.0.0-alpha.3 // indirect
 	cosmossdk.io/errors v1.0.0-beta.7
-	cosmossdk.io/math v1.0.0 // indirect
+	cosmossdk.io/math v1.0.0
 	filippo.io/edwards25519 v1.0.0 // indirect
 	github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
 	github.com/99designs/keyring v1.2.1 // indirect
-	github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect
-	github.com/DataDog/zstd v1.5.0 // indirect
-	github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
+	github.com/ChainSafe/go-schnorrkel v1.1.0 // indirect
 	github.com/StackExchange/wmi v1.2.1 // indirect
 	github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
-	github.com/Workiva/go-datastructures v1.0.53 // indirect
+	github.com/Workiva/go-datastructures v1.1.5 // indirect
 	github.com/armon/go-metrics v0.4.1 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect
-	github.com/btcsuite/btcd v0.23.4 // indirect
-	github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
-	github.com/btcsuite/btcd/btcutil v1.1.2 // indirect
-	github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
-	github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect
+	github.com/btcsuite/btcd v0.24.2 // indirect
+	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/cenkalti/backoff/v4 v4.1.3 // indirect
 	github.com/cespare/xxhash v1.1.0 // indirect
-	github.com/cespare/xxhash/v2 v2.2.0 // indirect
-	github.com/cockroachdb/errors v1.9.1 // indirect
-	github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
-	github.com/cockroachdb/pebble v0.0.0-20220817183557-09c6e030a677 // indirect
-	github.com/cockroachdb/redact v1.1.3 // indirect
+	github.com/cespare/xxhash/v2 v2.3.0 // indirect
 	github.com/coinbase/rosetta-sdk-go v0.7.9 // indirect
-	github.com/cometbft/cometbft-db v0.7.0 // indirect
+	github.com/cometbft/cometbft-db v0.9.5 // indirect
 	github.com/confio/ics23/go v0.9.0 // indirect
-	github.com/cosmos/btcutil v1.0.4 // indirect
-	github.com/cosmos/cosmos-db v0.0.0-20221226095112-f3c38ecb5e32 // indirect
-	github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect
+	github.com/cosmos/btcutil v1.0.5 // indirect
+	github.com/cosmos/cosmos-proto v1.0.0-beta.3 // indirect
 	github.com/cosmos/gorocksdb v1.2.0 // indirect
-	github.com/cosmos/iavl v0.19.5 // indirect
+	github.com/cosmos/iavl v0.19.6 // indirect
 	github.com/cosmos/ledger-cosmos-go v0.12.4 // indirect
 	github.com/creachadair/taskgroup v0.3.2 // indirect
 	github.com/danieljoos/wincred v1.1.2 // indirect
-	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 	github.com/deckarep/golang-set v1.8.0 // indirect
 	github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
 	github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
@@ -103,27 +149,26 @@ require (
 	github.com/dustin/go-humanize v1.0.1 // indirect
 	github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
 	github.com/edsrzf/mmap-go v1.0.0 // indirect
-	github.com/felixge/httpsnoop v1.0.2 // indirect
-	github.com/fsnotify/fsnotify v1.6.0 // indirect
+	github.com/felixge/httpsnoop v1.0.4 // indirect
+	github.com/fsnotify/fsnotify v1.7.0 // indirect
 	github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
-	github.com/getsentry/sentry-go v0.17.0 // indirect
-	github.com/go-kit/kit v0.12.0 // indirect
+	github.com/go-kit/kit v0.13.0 // indirect
 	github.com/go-kit/log v0.2.1 // indirect
-	github.com/go-logfmt/logfmt v0.5.1 // indirect
+	github.com/go-logfmt/logfmt v0.6.0 // indirect
 	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.0 // indirect
+	github.com/go-stack/stack v1.8.1 // indirect
 	github.com/gobwas/ws v1.1.0 // indirect
 	github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
 	github.com/gogo/gateway v1.1.0 // indirect
-	github.com/golang/glog v1.1.0 // indirect
+	github.com/golang/glog v1.2.2 // indirect
 	github.com/golang/snappy v0.0.4 // indirect
-	github.com/google/btree v1.1.2 // indirect
+	github.com/google/btree v1.1.3 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
 	github.com/google/orderedcode v0.0.1 // indirect
-	github.com/google/uuid v1.3.0 // indirect
+	github.com/google/uuid v1.6.0 // indirect
 	github.com/gorilla/handlers v1.5.1 // indirect
-	github.com/gorilla/websocket v1.5.0 // indirect
+	github.com/gorilla/websocket v1.5.3 // indirect
 	github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
 	github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
 	github.com/gtank/merlin v0.1.1 // indirect
@@ -134,55 +179,45 @@ require (
 	github.com/hashicorp/hcl v1.0.0 // indirect
 	github.com/hdevalence/ed25519consensus v0.1.0 // indirect
 	github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
-	github.com/holiman/uint256 v1.2.1 // indirect
+	github.com/holiman/uint256 v1.2.2 // indirect
 	github.com/huin/goupnp v1.0.3 // indirect
 	github.com/improbable-eng/grpc-web v0.15.0 // indirect
-	github.com/inconshreveable/mousetrap v1.0.1 // indirect
+	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/jackpal/go-nat-pmp v1.0.2 // indirect
 	github.com/jmhodges/levigo v1.0.0 // indirect
-	github.com/klauspost/compress v1.16.3 // 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/klauspost/compress v1.17.9 // indirect
+	github.com/lib/pq v1.10.9 // indirect
 	github.com/libp2p/go-buffer-pool v0.1.0 // indirect
-	github.com/linxGnu/grocksdb v1.7.16 // indirect
-	github.com/magiconair/properties v1.8.6 // indirect
+	github.com/linxGnu/grocksdb v1.9.3 // indirect
+	github.com/magiconair/properties v1.8.7 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
-	github.com/mattn/go-isatty v0.0.16 // indirect
+	github.com/mattn/go-isatty v0.0.18 // indirect
 	github.com/mattn/go-runewidth v0.0.9 // indirect
-	github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
-	github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect
-	github.com/minio/highwayhash v1.0.2 // indirect
+	github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect
+	github.com/minio/highwayhash v1.0.3 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/mtibben/percent v0.2.1 // indirect
 	github.com/olekukonko/tablewriter v0.0.5 // indirect
-	github.com/onsi/ginkgo v1.16.5 // indirect
-	github.com/onsi/gomega v1.20.0 // indirect
-	github.com/pelletier/go-toml v1.9.5 // indirect
-	github.com/pelletier/go-toml/v2 v2.0.5 // indirect
-	github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
-	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/prometheus/client_golang v1.14.0 // indirect
-	github.com/prometheus/client_model v0.3.0 // indirect
-	github.com/prometheus/common v0.37.0 // indirect
-	github.com/prometheus/procfs v0.8.0 // indirect
+	github.com/pelletier/go-toml/v2 v2.2.2 // indirect
+	github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
+	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+	github.com/prometheus/client_golang v1.20.3 // indirect
+	github.com/prometheus/client_model v0.6.1 // indirect
+	github.com/prometheus/common v0.59.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
 	github.com/regen-network/cosmos-proto v0.3.1
 	github.com/rjeczalik/notify v0.9.2 // indirect
-	github.com/rogpeppe/go-internal v1.9.0 // indirect
-	github.com/rs/cors v1.8.3 // indirect
-	github.com/rs/zerolog v1.27.0 // indirect
-	github.com/sasha-s/go-deadlock v0.3.1 // indirect
+	github.com/rs/cors v1.11.1 // indirect
+	github.com/rs/zerolog v1.29.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/spf13/afero v1.9.2 // indirect
-	github.com/spf13/jwalterweatherman v1.1.0 // indirect
+	github.com/spf13/afero v1.11.0 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
-	github.com/spf13/viper v1.14.0 // indirect
-	github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 // indirect
-	github.com/subosito/gotenv v1.4.1 // indirect
-	github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
-	github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
+	github.com/spf13/viper v1.19.0 // indirect
+	github.com/status-im/keycard-go v0.2.0 // indirect
+	github.com/subosito/gotenv v1.6.0 // indirect
 	github.com/tendermint/go-amino v0.16.0 // indirect
 	github.com/tidwall/btree v1.5.0 // indirect
 	github.com/tklauser/go-sysconf v0.3.10 // indirect
@@ -190,17 +225,16 @@ require (
 	github.com/tyler-smith/go-bip39 v1.1.0 // indirect
 	github.com/zondax/hid v0.9.2 // indirect
 	github.com/zondax/ledger-go v0.14.3 // indirect
-	go.etcd.io/bbolt v1.3.7 // indirect
+	go.etcd.io/bbolt v1.3.11 // indirect
 	go.opencensus.io v0.24.0 // indirect
-	golang.org/x/crypto v0.7.0 // indirect
-	golang.org/x/exp v0.0.0-20230321023759-10a507213a29
-	golang.org/x/net v0.9.0 // indirect
-	golang.org/x/sync v0.1.0 // indirect
-	golang.org/x/sys v0.7.0 // indirect
-	golang.org/x/term v0.7.0 // indirect
-	golang.org/x/text v0.9.0 // indirect
-	golang.org/x/time v0.1.0 // indirect
-	google.golang.org/protobuf v1.30.0
+	golang.org/x/crypto v0.27.0 // indirect
+	golang.org/x/net v0.29.0 // indirect
+	golang.org/x/sync v0.8.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/time v0.5.0 // indirect
+	google.golang.org/protobuf v1.34.2
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
diff --git a/go.sum b/go.sum
index 9e852ebc..df18a798 100644
--- a/go.sum
+++ b/go.sum
@@ -1,9 +1,9 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
 cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
 cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
 cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
 cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
 cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
 cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
@@ -17,38 +17,179 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY
 cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
 cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
 cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
-cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
+cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
+cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
+cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
+cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
+cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
+cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
+cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
+cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
+cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
+cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
+cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
+cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
+cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
+cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU=
+cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
+cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM=
+cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4=
+cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw=
+cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY=
+cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI=
+cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4=
+cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4=
+cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0=
+cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ=
+cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk=
+cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o=
+cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s=
+cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0=
+cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY=
+cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw=
+cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI=
+cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0=
+cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8=
 cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
 cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
 cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
 cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
 cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
+cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA=
 cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o=
+cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY=
+cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s=
+cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM=
+cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI=
+cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY=
+cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI=
+cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
+cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
+cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
+cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
+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.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
+cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
+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=
+cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs=
+cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc=
+cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM=
+cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ=
+cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo=
+cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE=
+cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I=
+cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ=
+cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo=
+cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA=
 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo=
+cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ=
+cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4=
+cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0=
+cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8=
+cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU=
+cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU=
+cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y=
+cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg=
+cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk=
+cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w=
+cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk=
+cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg=
+cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM=
+cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA=
+cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o=
+cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A=
+cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0=
+cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0=
+cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc=
+cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
+cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc=
+cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc=
+cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI=
+cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic=
+cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI=
+cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8=
+cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08=
+cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4=
+cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w=
+cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE=
+cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM=
+cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY=
+cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s=
+cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA=
+cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o=
+cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ=
+cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU=
+cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY=
+cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34=
+cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs=
+cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg=
+cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E=
+cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU=
+cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0=
+cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA=
+cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0=
+cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI=
 cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
 cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
 cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
 cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
+cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4=
+cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o=
+cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk=
+cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo=
+cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg=
+cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4=
+cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg=
+cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c=
+cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y=
+cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A=
+cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4=
+cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY=
+cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s=
+cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI=
+cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA=
+cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4=
+cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0=
+cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU=
+cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU=
+cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc=
+cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs=
+cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg=
+cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM=
+cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ=
 cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
 cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
 cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
 cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
-cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
+cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
+cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc=
+cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s=
+cloud.google.com/go/storage v1.38.0 h1:Az68ZRGlnNTpIBbLjSMIV2BDcwwXYlRlQzis0llkpJg=
+cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY=
+cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw=
+cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g=
+cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU=
+cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4=
+cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0=
+cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo=
+cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo=
+cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE=
+cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg=
+cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0=
+cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
 collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
-cosmossdk.io/api v0.2.6 h1:AoNwaLLapcLsphhMK6+o0kZl+D6MMUaHVqSdwinASGU=
-cosmossdk.io/api v0.2.6/go.mod h1:u/d+GAxil0nWpl1XnQL8nkziQDIWuBDhv8VnDm/s6dI=
-cosmossdk.io/core v0.5.1 h1:vQVtFrIYOQJDV3f7rw4pjjVqc1id4+mE0L9hHP66pyI=
-cosmossdk.io/core v0.5.1/go.mod h1:KZtwHCLjcFuo0nmDc24Xy6CRNEL9Vl/MeimQ2aC7NLE=
-cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw=
-cosmossdk.io/depinject v1.0.0-alpha.3/go.mod h1:eRbcdQ7MRpIPEM5YUJh8k97nxHpYbc3sMUnEtt8HPWU=
 cosmossdk.io/errors v1.0.0-beta.7 h1:gypHW76pTQGVnHKo6QBkb4yFOJjC+sUGRc5Al3Odj1w=
 cosmossdk.io/errors v1.0.0-beta.7/go.mod h1:mz6FQMJRku4bY7aqS/Gwfcmr/ue91roMEKAmDUDpBfE=
 cosmossdk.io/math v1.0.0 h1:ro9w7eKx23om2tZz/VM2Pf+z2WAbGX1yDQQOJ6iGeJw=
 cosmossdk.io/math v1.0.0/go.mod h1:Ygz4wBHrgc7g0N+8+MrnTfS9LLn9aaTGa9hKopuym5k=
-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
 filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
 filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
@@ -58,38 +199,28 @@ github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMb
 github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
 github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o=
 github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA=
-github.com/AltheaFoundation/canto/v5 v5.2.0 h1:8/JY1HnGUW4LTPRDQhXp3o7SCXBppcyLhzYM6SR6fb4=
-github.com/AltheaFoundation/canto/v5 v5.2.0/go.mod h1:ycgU4yxtNf9kFVaMY7IsQ094kgBK3T+DSkNEhaIA/z8=
-github.com/AltheaFoundation/ethermint v0.19.9 h1:Z5cAgNPwV3luTseFxJF2scBsk2YgtreTD7jOb24gWdA=
-github.com/AltheaFoundation/ethermint v0.19.9/go.mod h1:D1Jfhqtrb0q12IQLLB6w0jfjcSl/EMRCR3VKJVVPXSM=
-github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
+github.com/AltheaFoundation/canto/v6 v6.0.1 h1:3NaThdYw6AZD2bf12bz8Qa3beXDbe0a5pNDB0lAitv4=
+github.com/AltheaFoundation/canto/v6 v6.0.1/go.mod h1:CaaiFJ+WEL09j7JTI58JNBrd9p0d7a54eTt61a+nEDk=
+github.com/AltheaFoundation/ethermint v0.22.3 h1:NlGZmFveXsFtiOFiTXiEwyA5qTbatJtleWL2yx5h/Ow=
+github.com/AltheaFoundation/ethermint v0.22.3/go.mod h1:Pu0VyapSeMO2o7LlvHVn2ir+P9RuUxhIiIv6VYaFivw=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM=
 github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I=
 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo=
-github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
+github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
+github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg=
-github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4=
-github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw=
-github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
-github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=
-github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
+github.com/ChainSafe/go-schnorrkel v1.1.0 h1:rZ6EU+CZFCjB4sHUE1jIu8VDoB/wRKZxoe1tkcO71Wk=
+github.com/ChainSafe/go-schnorrkel v1.1.0/go.mod h1:ABkENxiP+cvjFiByMIZ9LYbRoNNLeBLiakC1XeTFxfE=
 github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
-github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
-github.com/DataDog/zstd v1.5.0 h1:+K/VEwIAaPcHiMtQvpLD4lqW7f0Gk3xdYZmI1hD+CXo=
 github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
-github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
-github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
-github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
-github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM=
 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
-github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
+github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
+github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
 github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
+github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
 github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
 github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
 github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
@@ -99,15 +230,14 @@ 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/Workiva/go-datastructures v1.0.53 h1:J6Y/52yX10Xc5JjXmGtWoSSxs3mZnGSaq37xZZh7Yig=
-github.com/Workiva/go-datastructures v1.0.53/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A=
+github.com/Workiva/go-datastructures v1.1.5 h1:5YfhQ4ry7bZc2Mc7R0YZyYwpf5c6t1cEFvdAhd6Mkf4=
+github.com/Workiva/go-datastructures v1.1.5/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A=
 github.com/Zilliqa/gozilliqa-sdk v1.2.1-0.20201201074141-dd0ecada1be6/go.mod h1:eSYp2T6f0apnuW8TzhV3f6Aff2SE8Dwio++U4ha4yEM=
-github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I=
+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/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
 github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
-github.com/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -115,6 +245,9 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
 github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
 github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
 github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc=
+github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
+github.com/althea-net/cosmos-sdk v0.46.17 h1:SG2dTF7CT1M2RB6cDJZijACuRj9m9p8DWcMpG8DVLJY=
+github.com/althea-net/cosmos-sdk v0.46.17/go.mod h1:/iMlaT3rCQ9RS5M/40MfrM6lRwiwhAgx87nzMmYXxI8=
 github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
 github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0=
@@ -129,6 +262,8 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
 github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
 github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
 github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/aws/aws-sdk-go v1.44.122 h1:p6mw01WBaNpbdP2xrisz5tIkcNwzj/HysobNoaAHjgo=
+github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
 github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
 github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo=
 github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y=
@@ -139,11 +274,12 @@ github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7
 github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0=
 github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM=
 github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw=
-github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
+github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s=
 github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
@@ -154,24 +290,28 @@ github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13P
 github.com/btcsuite/btcd v0.21.0-beta.0.20201114000516-e9c7a5ac6401/go.mod h1:Sv4JPQ3/M+teHz9Bo5jBpkNcP0x6r7rdihlNL/7tTAs=
 github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
 github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y=
-github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=
-github.com/btcsuite/btcd v0.23.4 h1:IzV6qqkfwbItOS/sg/aDfPDsjPP8twrCOE2R93hxMlQ=
-github.com/btcsuite/btcd v0.23.4/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=
+github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A=
+github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY=
+github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg=
 github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA=
 github.com/btcsuite/btcd/btcec/v2 v2.1.2/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
 github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
-github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U=
-github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
+github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
+github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
 github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A=
 github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE=
-github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ=
-github.com/btcsuite/btcd/btcutil v1.1.2/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0=
-github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ=
-github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0=
+github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00=
+github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/AYFd6c=
+github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE=
 github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
-github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
 github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
+github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ=
+github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
 github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
+github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
+github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
+github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
+github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o=
 github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
 github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
 github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
@@ -179,6 +319,8 @@ github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku
 github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
 github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
 github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
+github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
+github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
 github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
 github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
 github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
@@ -193,93 +335,88 @@ github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW
 github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
-github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
+github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
 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/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cockroachdb/apd/v3 v3.1.0 h1:MK3Ow7LH0W8zkd5GMKA1PvS9qG3bWFI95WaVNfyZJ/w=
+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=
+github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+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/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
-github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=
-github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
-github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM=
-github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac=
-github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8=
-github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk=
-github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
-github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
-github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
-github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
-github.com/cockroachdb/pebble v0.0.0-20220817183557-09c6e030a677 h1:qbb/AE938DFhOajUYh9+OXELpSF9KZw2ZivtmW6eX1Q=
-github.com/cockroachdb/pebble v0.0.0-20220817183557-09c6e030a677/go.mod h1:890yq1fUb9b6dGNwssgeUO5vQV9qfXnCPxAJhBQfXw0=
-github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
-github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ=
-github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
-github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ=
 github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
-github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
 github.com/coinbase/kryptology v1.8.0/go.mod h1:RYXOAPdzOGUe3qlSFkMGn58i3xUA8hmxYHksuq+8ciI=
 github.com/coinbase/rosetta-sdk-go v0.7.9 h1:lqllBjMnazTjIqYrOGv8h8jxjg9+hJazIGZr9ZvoCcA=
 github.com/coinbase/rosetta-sdk-go v0.7.9/go.mod h1:0/knutI7XGVqXmmH4OQD8OckFrbQ8yMsUZTG7FXCR2M=
-github.com/cometbft/cometbft v0.34.27 h1:ri6BvmwjWR0gurYjywcBqRe4bbwc3QVs9KRcCzgh/J0=
-github.com/cometbft/cometbft v0.34.27/go.mod h1:BcCbhKv7ieM0KEddnYXvQZR+pZykTKReJJYf7YC7qhw=
-github.com/cometbft/cometbft-db v0.7.0 h1:uBjbrBx4QzU0zOEnU8KxoDl18dMNgDh+zZRUE0ucsbo=
-github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0=
+github.com/cometbft/cometbft v0.34.35 h1:GOjfoIDFwIGVS1BEKbxb9N/ppIuLrwnzcauLLV1i0B4=
+github.com/cometbft/cometbft v0.34.35/go.mod h1:qrHyZj3Efj8Hw8EHslaVYtTwXpF2BBZfk+/tgdlJee8=
+github.com/cometbft/cometbft-db v0.9.5 h1:ZlIm/peuB9BlRuK01/b/hIIWH2U2m2Q0DNfZ7JmCvhY=
+github.com/cometbft/cometbft-db v0.9.5/go.mod h1:Sr3SrYWcAyGvL0HzZMaSJOGMWDEIyiXV1QjCMxM/HNk=
+github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4=
+github.com/confio/ics23/go v0.9.0/go.mod h1:4LPZ2NYqnYIVRklaozjNR1FScgDJ2s5Xrp+e/mYVRak=
 github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ=
 github.com/consensys/bavard v0.1.8-0.20210915155054-088da2f7f54a/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
 github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q=
 github.com/consensys/gnark-crypto v0.5.3/go.mod h1:hOdPlWQV1gDLp7faZVeg8Y0iEPFaOUnCc4XeCCk96p0=
-github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
+github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8=
+github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/cosmos/btcutil v1.0.4 h1:n7C2ngKXo7UC9gNyMNLbzqz7Asuf+7Qv4gnX/rOdQ44=
-github.com/cosmos/btcutil v1.0.4/go.mod h1:Ffqc8Hn6TJUdDgHBwIZLtrLQC1KdJ9jGJl/TvgUaxbU=
-github.com/cosmos/cosmos-db v0.0.0-20221226095112-f3c38ecb5e32 h1:zlCp9n3uwQieELltZWHRmwPmPaZ8+XoL2Sj+A2YJlr8=
-github.com/cosmos/cosmos-db v0.0.0-20221226095112-f3c38ecb5e32/go.mod h1:kwMlEC4wWvB48zAShGKVqboJL6w4zCLesaNQ3YLU2BQ=
-github.com/cosmos/cosmos-proto v1.0.0-beta.2 h1:X3OKvWgK9Gsejo0F1qs5l8Qn6xJV/AzgIWR2wZ8Nua8=
-github.com/cosmos/cosmos-proto v1.0.0-beta.2/go.mod h1:+XRCLJ14pr5HFEHIUcn51IKXD1Fy3rkEQqt4WqmN4V0=
-github.com/cosmos/cosmos-sdk v0.45.16 h1:5ba/Bh5/LE55IwHQuCU4fiG4eXeDKtSWzehXRpaKDcw=
-github.com/cosmos/cosmos-sdk v0.45.16/go.mod h1:bScuNwWAP0TZJpUf+SHXRU3xGoUPp+X9nAzfeIXts40=
-github.com/cosmos/cosmos-sdk/ics23 v0.0.0-20221014140410-2582f0aab7b2 h1:/ml8JZ7wuhQqa2ArobstL7DVaLyOYnUnE7ZdZG5OAb0=
-github.com/cosmos/cosmos-sdk/ics23 v0.0.0-20221014140410-2582f0aab7b2/go.mod h1:2a4dBq88TUoqoWAU5eu0lGvpFP3wWDPgdHPargtyw30=
-github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
+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-proto v1.0.0-beta.3 h1:VitvZ1lPORTVxkmF2fAp3IiA61xVwArQYKXTdEcpW6o=
+github.com/cosmos/cosmos-proto v1.0.0-beta.3/go.mod h1:t8IASdLaAq+bbHbjq4p960BvcTqtwuAxid3b/2rOD6I=
 github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY=
 github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw=
+github.com/cosmos/gogoproto v1.4.7 h1:RzYKVnsEC7UIkDnhTIkqEB7LnIQbsySvmNEqPCiPevk=
+github.com/cosmos/gogoproto v1.4.7/go.mod h1:gxGePp9qedovvl/StQL2BIJ6qlIBn1+9YxR0IulGBKA=
 github.com/cosmos/gorocksdb v1.2.0 h1:d0l3jJG8M4hBouIZq0mDUHZ+zjOx044J3nGRskwTb4Y=
 github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw=
-github.com/cosmos/iavl v0.19.5 h1:rGA3hOrgNxgRM5wYcSCxgQBap7fW82WZgY78V9po/iY=
-github.com/cosmos/iavl v0.19.5/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw=
-github.com/cosmos/ibc-go/v4 v4.3.1 h1:xbg0CaCdxK3lvgGvSaI91ROOLd7s30UqEcexH6Ba4Ys=
-github.com/cosmos/ibc-go/v4 v4.3.1/go.mod h1:89E+K9CxpkS/etLEcG026jPM/RSnVMcfesvRYp/0aKI=
+github.com/cosmos/iavl v0.19.6 h1:XY78yEeNPrEYyNCKlqr9chrwoeSDJ0bV2VjocTk//OU=
+github.com/cosmos/iavl v0.19.6/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw=
+github.com/cosmos/ibc-go/v6 v6.3.1 h1:/5ur3AsmNW8WuOevfODHlaY5Ze236PBNE3vVo9o3fQA=
+github.com/cosmos/ibc-go/v6 v6.3.1/go.mod h1:Dm14j9s094bGyCEE8W4fD+2t8IneHv+cz+80Mvwjr1w=
 github.com/cosmos/ledger-cosmos-go v0.12.4 h1:drvWt+GJP7Aiw550yeb3ON/zsrgW0jgh5saFCr7pDnw=
 github.com/cosmos/ledger-cosmos-go v0.12.4/go.mod h1:fjfVWRf++Xkygt9wzCsjEBdjcf7wiiY35fv3ctT+k4M=
+github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
+github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creachadair/taskgroup v0.3.2 h1:zlfutDS+5XG40AOxcHDSThxKzns8Tnr9jnr6VqkYlkM=
 github.com/creachadair/taskgroup v0.3.2/go.mod h1:wieWwecHVzsidg2CsUnFinW1faVN4+kq+TDlRJQ0Wbk=
 github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/cucumber/common/gherkin/go/v22 v22.0.0 h1:4K8NqptbvdOrjL9DEea6HFjSpbdT9+Q5kgLpmmsHYl0=
-github.com/cucumber/common/messages/go/v17 v17.1.1 h1:RNqopvIFyLWnKv0LfATh34SWBhXeoFTJnSrgm9cT/Ts=
 github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
 github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
 github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
 github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
 github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
 github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
 github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
@@ -292,7 +429,6 @@ github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE
 github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw=
 github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
 github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
-github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
 github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
 github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
 github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
@@ -310,8 +446,10 @@ github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwu
 github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
 github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
 github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
+github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
+github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
 github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
+github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
 github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf h1:Yt+4K30SdjOkRoRRm3vYNQgR+/ZIy0RmeUDZo7Y8zeQ=
 github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
@@ -328,75 +466,74 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP
 github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
 github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
 github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
-github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
+github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
+github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
+github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
 github.com/ethereum/go-ethereum v1.10.17/go.mod h1:Lt5WzjM07XlXc95YzrhosmR4J9Ahd6X2wyEV2SvGhk0=
-github.com/ethereum/go-ethereum v1.10.19 h1:EOR5JbL4MD5yeOqv8W2iC1s4NximrTjqFccUz8lyBRA=
-github.com/ethereum/go-ethereum v1.10.19/go.mod h1:IJBNMtzKcNHPtllYihy6BL2IgK1u+32JriaTbdt4v+w=
+github.com/ethereum/go-ethereum v1.10.26 h1:i/7d9RBBwiXCEuyduBQzJw/mKmnvzsN14jqBmytw72s=
+github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg=
 github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0=
+github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
 github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
+github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
 github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk=
-github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
+github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
 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/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
 github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
-github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=
-github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c=
 github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
-github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
 github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
 github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
+github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
 github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
 github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
-github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
-github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
-github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
+github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
+github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
 github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
 github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
 github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
 github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
-github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c=
-github.com/getsentry/sentry-go v0.17.0 h1:UustVWnOoDFHBS7IJUB2QK/nB5pap748ZEp0swnQJak=
-github.com/getsentry/sentry-go v0.17.0/go.mod h1:B82dxtBvxG0KaPD8/hfSV+VcHD+Lg/xUS4JuQn1P4cM=
-github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
 github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
-github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
 github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
 github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
+github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
 github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
 github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
-github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
 github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
-github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
-github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
-github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
-github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4=
-github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
-github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
-github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
+github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU=
+github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg=
 github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
 github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
-github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
-github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
-github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
+github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
+github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
+github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
 github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
@@ -406,17 +543,22 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
 github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
+github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
 github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
+github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
 github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
 github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
+github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
 github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
 github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
 github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
-github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
+github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
+github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
+github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
+github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
 github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
 github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
 github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
@@ -427,37 +569,37 @@ github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/E
 github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA=
 github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0=
 github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
+github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0=
 github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
-github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc=
 github.com/gogo/gateway v1.1.0 h1:u0SuhL9+Il+UbjM9VIE3ntfRujKbvVpFvNB4HbjeVQ0=
 github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/mbic=
-github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
-github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
-github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=
-github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
-github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
+github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
 github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog=
 github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
 github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
-github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
+github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY=
+github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
 github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
 github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
 github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
 github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
 github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
 github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
+github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -474,20 +616,20 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
-github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
 github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
-github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
-github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
+github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
+github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
 github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -500,16 +642,24 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
+github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
 github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
 github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
+github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
+github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
 github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us=
 github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -521,17 +671,39 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
 github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+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 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
+github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 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=
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
+github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
+github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
+github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
+github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
+github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
+github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
+github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
+github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
+github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
+github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo=
+github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY=
+github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA=
+github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4=
+github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
 github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
@@ -541,11 +713,11 @@ github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z
 github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
 github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
 github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
 github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
@@ -558,7 +730,6 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
 github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU=
 github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=
-github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
 github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
 github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
 github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc=
@@ -570,6 +741,10 @@ github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpx
 github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
 github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
 github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
+github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
+github.com/hashicorp/go-getter v1.7.0 h1:bzrYP+qu/gMrL1au7/aDvkoOVGUJpeKBgbqRHACAFDY=
+github.com/hashicorp/go-getter v1.7.0/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744=
 github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
 github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
 github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
@@ -577,6 +752,8 @@ github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iP
 github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
 github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
 github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
+github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
 github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
 github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
 github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
@@ -584,6 +761,7 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
 github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
 github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
 github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
 github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -601,24 +779,21 @@ github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3s
 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.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
-github.com/holiman/uint256 v1.2.1 h1:XRtyuda/zw2l+Bq/38n5XUoEF72aSOu/77Thd9pPp2o=
-github.com/holiman/uint256 v1.2.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
+github.com/holiman/uint256 v1.2.2 h1:TXKcSGc2WaxPD2+bmzAsVthL4+pEN0YwXcL5qED83vk=
+github.com/holiman/uint256 v1.2.2/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
 github.com/huin/goupnp v1.0.3-0.20220313090229-ca81a64b4204/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y=
 github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ=
 github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y=
 github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
-github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
-github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
 github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ=
 github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
-github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY=
 github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI=
 github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8=
@@ -631,20 +806,17 @@ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19y
 github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE=
 github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0=
 github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po=
-github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
-github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
-github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI=
-github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
-github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
-github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
 github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
 github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
 github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU=
 github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
-github.com/jhump/protoreflect v1.13.1-0.20220928232736-101791cb1b4c h1:XImQJfpJLmGEEd8ll5yPVyL/aEvmgGHW4WYTyNseLOM=
+github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
+github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
 github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
 github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
 github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U=
 github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
@@ -656,127 +828,106 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
 github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
 github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o=
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
-github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
-github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
-github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
 github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
 github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=
-github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
 github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
-github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk=
-github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8=
-github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U=
-github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE=
-github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=
-github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE=
-github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=
-github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro=
-github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
 github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
-github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY=
-github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+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 v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
-github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
 github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
 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/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 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/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
 github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
-github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
 github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
 github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
 github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
 github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
+github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
-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/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
 github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
 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.7.16 h1:Q2co1xrpdkr5Hx3Fp+f+f7fRGhQFQhvi/+226dtLmA8=
-github.com/linxGnu/grocksdb v1.7.16/go.mod h1:JkS7pl5qWpGpuVb3bPqTz8nC12X3YtPZT+Xq7+QfQo4=
+github.com/linxGnu/grocksdb v1.9.3 h1:s1cbPcOd0cU2SKXRG1nEqCOWYAELQjdqg3RVI2MH9ik=
+github.com/linxGnu/grocksdb v1.9.3/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA=
 github.com/lucasjones/reggen v0.0.0-20180717132126-cdb49ff09d77/go.mod h1:5ELEyG+X8f+meRWHuqUOewBOhvHkl7M76pdGEansxW4=
+github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
-github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
+github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
+github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
 github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
+github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
 github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
 github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
 github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
-github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
+github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
-github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
-github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
-github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
-github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
-github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
-github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/miguelmota/go-ethereum-hdwallet v0.1.1 h1:zdXGlHao7idpCBjEGTXThVAtMKs+IxAgivZ75xqkWK0=
 github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
-github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94=
-github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
-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/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b h1:QrHweqAtyJ9EwCaGHBu1fghwxIPiopAHV06JlXrMHjk=
+github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b/go.mod h1:xxLb2ip6sSUts3g1irPVHyk/DGslwQsNOo9I7smJfNU=
+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=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
+github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
 github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
 github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@@ -787,6 +938,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
 github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
 github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
+github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
+github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -795,10 +948,11 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
 github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
-github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
 github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
 github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs=
 github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -808,17 +962,14 @@ github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo
 github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
 github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
 github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
-github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
 github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
-github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
 github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
 github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
 github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
 github.com/neilotoole/errgroup v0.1.6/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
-github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
-github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
 github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
 github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
@@ -827,23 +978,24 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N
 github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
+github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
 github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
-github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
-github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
-github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY=
+github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
+github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
 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.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q=
-github.com/onsi/gomega v1.20.0/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
+github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
+github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
 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/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034=
-github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b+d8w=
+github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
+github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
+github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
+github.com/opencontainers/runc v1.2.0-rc.2 h1:5P32s2x9w1gAk20jbkwbQCZCfqVFashCwjD1UL2Ykc4=
+github.com/opencontainers/runc v1.2.0-rc.2/go.mod h1:H8njh/SD+WY9bYMmVsEEWDJgJdviOSDjNeXMjeNbYCE=
 github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
 github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
 github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
@@ -854,7 +1006,7 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ
 github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
 github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
 github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA=
-github.com/otiai10/copy v1.6.0 h1:IinKAryFFuPONZ7cm6T6E2QX/vcJwSnlaA5lfoaXIiQ=
+github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
 github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
@@ -862,31 +1014,26 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T
 github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE=
 github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
-github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
-github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
-github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
+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/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
 github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
-github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
-github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
+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/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
 github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
 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=
-github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
-github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
-github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
 github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
@@ -894,18 +1041,16 @@ 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.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
-github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
-github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
-github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
+github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4=
+github.com/prometheus/client_golang v1.20.3/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=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
-github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
+github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
+github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
 github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
@@ -914,20 +1059,16 @@ 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.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
-github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
-github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
-github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
+github.com/prometheus/common v0.59.1 h1:LXb1quJHWm1P6wq/U824uxYi4Sg0oGvNeUm1z5dJoX0=
+github.com/prometheus/common v0.59.1/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0=
 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=
 github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
 github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
 github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
-github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
-github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
-github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
-github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
+github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
+github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic=
 github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4=
@@ -938,7 +1079,6 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5X
 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 github.com/regen-network/cosmos-proto v0.3.1 h1:rV7iM4SSFAagvy8RiyhiACbWEGotmqzywPxOvwMdxcg=
 github.com/regen-network/cosmos-proto v0.3.1/go.mod h1:jO0sVX6a1B36nmE8C9xBFXpNwWejXC7QqCOnH3O0+YM=
-github.com/regen-network/gocuke v0.6.2 h1:pHviZ0kKAq2U2hN2q3smKNxct6hS0mGByFMHGnWA97M=
 github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4=
 github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI=
 github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc=
@@ -948,32 +1088,32 @@ github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
-github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
-github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
+github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
+github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
 github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
-github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo=
-github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
-github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
-github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs=
-github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U=
+github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
+github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
+github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
+github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc=
+github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU=
+github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
+github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
+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/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
-github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
+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/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY=
 github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
 github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
-github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 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=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@@ -981,45 +1121,47 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
+github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
+github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
 github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw=
-github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
+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.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
-github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
+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/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
-github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
-github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
+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=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
-github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
-github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU=
-github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As=
+github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
+github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
 github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
-github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 h1:Oo2KZNP70KE0+IUJSidPj/BFS/RXNHmKIJOdckzml2E=
-github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
+github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
+github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
 github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
 github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
 github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -1030,14 +1172,13 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 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.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
-github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
-github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
+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/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=
 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
-github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok=
-github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8=
 github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E=
 github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
 github.com/tendermint/tm-db v0.6.7 h1:fE00Cbl0jayAoqlExN6oyQJ7fR/ZtoVOmvPJ//+shu8=
@@ -1046,9 +1187,16 @@ github.com/tidwall/btree v1.5.0 h1:iV0yVY/frd7r6qGBXfEYs7DH0gTDgrKTrDjS7xt/IyQ=
 github.com/tidwall/btree v1.5.0/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE=
 github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
 github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
+github.com/tidwall/gjson v1.14.4/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=
 github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
 github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM=
+github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
+github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
 github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
 github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg=
 github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
@@ -1064,47 +1212,45 @@ github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:s
 github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
 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.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
-github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
 github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
 github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
+github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
+github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
+github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
 github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
 github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
-github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
+github.com/urfave/cli/v2 v2.10.2 h1:x3p8awjp/2arX+Nl/G2040AZpOCHS/eMJJ1/a+mye4Y=
+github.com/urfave/cli/v2 v2.10.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo=
 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
 github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
 github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
-github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
 github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
 github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
 github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
-github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
-github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
-github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
-github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
+github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
+github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
 github.com/ybbus/jsonrpc v2.1.2+incompatible/go.mod h1:XJrh1eMSzdIYFbM08flv0wp5G35eRniyeGut1z+LSiE=
-github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
-github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
-github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U=
 github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
 github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw=
 github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI=
 go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
-go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
+go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
+go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
 go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
 go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
@@ -1114,13 +1260,29 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
+go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
 go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
 go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
+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.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
+go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
+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/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/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.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
 go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
 go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
@@ -1136,8 +1298,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -1145,32 +1305,15 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
-golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
-golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/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=
-golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
-golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
-golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
-golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
-golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
-golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
-golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
+golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
+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/exp v0.0.0-20230711153332-06a737ee72cb h1:xIApU0ow1zwMa2uL1VDNeQlNVFTWMQxZUZCMDy0Q4Us=
+golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
 golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
-golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
-golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -1182,20 +1325,19 @@ golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPI
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
-golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
+golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1205,7 +1347,6 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r
 golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -1215,7 +1356,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -1238,20 +1378,29 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
-golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
+golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+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/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=
@@ -1261,8 +1410,24 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
 golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
+golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
+golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
+golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
+golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
+golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
+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/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=
@@ -1274,9 +1439,13 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+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/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=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1285,10 +1454,8 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h
 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-20181205085412-a5c9d58dba9a/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-20190222072716-a9d3bda3a223/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=
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1301,7 +1468,6 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1336,42 +1502,60 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210909193231-528a39cd75f3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
-golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+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/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 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.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
-golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
+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/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=
@@ -1381,25 +1565,25 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
-golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+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/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=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA=
-golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@@ -1407,9 +1591,7 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
 golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -1425,7 +1607,6 @@ golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapK
 golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -1445,23 +1626,31 @@ golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 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.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
 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=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
+golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
+golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
+golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
+golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
 gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
 gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
 gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
-gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM=
-gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
 gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
 gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
 gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
@@ -1485,6 +1674,38 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
 google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
 google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
+google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
+google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
+google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
+google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
+google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
+google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
+google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
+google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
+google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
+google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
+google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
+google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
+google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
+google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
+google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
+google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
+google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
+google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
+google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
+google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
+google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
+google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g=
+google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
+google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
+google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI=
+google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
+google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
+google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
+google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70=
+google.golang.org/api v0.171.0 h1:w174hnBPqut76FzW5Qaupt7zY8Kql6fiVjgys4f58sU=
+google.golang.org/api v0.171.0/go.mod h1:Hnq5AHm4OTMt2BUVjael2CWZFD6vksJdWCWiUAmjC9o=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1492,7 +1713,6 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww
 google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -1534,14 +1754,124 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D
 google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
+google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
+google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
+google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
 google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
-google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44 h1:EfLuoKW5WfkgVdDy7dTK8qSbH37AX5mj/MFh+bGPz14=
-google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA=
-google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o=
+google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
+google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
+google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
+google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
+google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
+google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
+google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
+google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
+google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
+google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
+google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
+google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
+google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
+google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
+google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
+google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
+google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
+google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
+google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
+google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
+google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE=
+google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=
+google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
+google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
+google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
+google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
+google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
+google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
+google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
+google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
+google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
+google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
+google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw=
+google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
+google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
+google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U=
+google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
+google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
+google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=
+google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
+google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
+google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU=
+google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
+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.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
 google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
+google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
+google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
+google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
+google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
+google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
+google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+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.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM=
+google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
+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=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1554,33 +1884,31 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
-google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+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.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
+google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
 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=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
+gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
-gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
-gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
-gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
 gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
 gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
 gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
 gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
 gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
 gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
@@ -1593,14 +1921,12 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
 gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
-gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1613,6 +1939,7 @@ nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0
 nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
 nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
 pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA=
+pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
@@ -1620,4 +1947,5 @@ rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
+sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
 sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
diff --git a/ibcutils/testing/app.go b/ibcutils/testing/app.go
index 395f0fab..c41e9156 100644
--- a/ibcutils/testing/app.go
+++ b/ibcutils/testing/app.go
@@ -18,16 +18,17 @@ import (
 	"github.com/cosmos/cosmos-sdk/codec"
 	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
 	cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
+	storetypes "github.com/cosmos/cosmos-sdk/store/types"
 	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"
 	capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/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"
 
-	"github.com/cosmos/ibc-go/v4/modules/core/keeper"
-	"github.com/cosmos/ibc-go/v4/testing/simapp"
+	"github.com/cosmos/ibc-go/v6/modules/core/keeper"
+	"github.com/cosmos/ibc-go/v6/testing/simapp"
+	ibctesting "github.com/cosmos/ibc-go/v6/testing/types"
 
 	ethermint "github.com/evmos/ethermint/types"
 	feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"
@@ -45,7 +46,7 @@ type TestingApp interface {
 
 	// ibc-go additions
 	GetBaseApp() *baseapp.BaseApp
-	GetStakingKeeper() stakingkeeper.Keeper
+	GetStakingKeeper() ibctesting.StakingKeeper
 	GetIBCKeeper() *keeper.Keeper
 	GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper
 	GetTxConfig() client.TxConfig
@@ -54,7 +55,7 @@ type TestingApp interface {
 	AppCodec() codec.Codec
 
 	// Implemented by BaseApp
-	LastCommitID() sdk.CommitID
+	LastCommitID() storetypes.CommitID
 	LastBlockHeight() int64
 }
 
diff --git a/ibcutils/testing/chain.go b/ibcutils/testing/chain.go
index 8574451d..426726d4 100644
--- a/ibcutils/testing/chain.go
+++ b/ibcutils/testing/chain.go
@@ -19,15 +19,15 @@ import (
 	"github.com/cosmos/cosmos-sdk/x/staking/teststaking"
 	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
 
-	clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
-	commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types"
-	host "github.com/cosmos/ibc-go/v4/modules/core/24-host"
-	"github.com/cosmos/ibc-go/v4/modules/core/exported"
-	"github.com/cosmos/ibc-go/v4/modules/core/types"
-	ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types"
-	"github.com/cosmos/ibc-go/v4/testing/mock"
-	"github.com/cosmos/ibc-go/v4/testing/simapp"
-	"github.com/cosmos/ibc-go/v4/testing/simapp/helpers"
+	clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
+	commitmenttypes "github.com/cosmos/ibc-go/v6/modules/core/23-commitment/types"
+	host "github.com/cosmos/ibc-go/v6/modules/core/24-host"
+	"github.com/cosmos/ibc-go/v6/modules/core/exported"
+	"github.com/cosmos/ibc-go/v6/modules/core/types"
+	ibctmtypes "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint/types"
+	"github.com/cosmos/ibc-go/v6/testing/mock"
+	"github.com/cosmos/ibc-go/v6/testing/simapp"
+	"github.com/cosmos/ibc-go/v6/testing/simapp/helpers"
 
 	abci "github.com/tendermint/tendermint/abci/types"
 	"github.com/tendermint/tendermint/crypto/tmhash"
@@ -555,7 +555,7 @@ func SignAndDeliver(
 	// Simulate a sending a transaction and committing a block
 	// nolint: exhaustruct
 	app.BeginBlock(abci.RequestBeginBlock{Header: header})
-	gInfo, res, err := app.Deliver(txCfg.TxEncoder(), tx)
+	gInfo, res, err := app.SimDeliver(txCfg.TxEncoder(), tx)
 
 	if expPass {
 		require.NoError(t, err)
diff --git a/ibcutils/testing/config.go b/ibcutils/testing/config.go
index b75ecc8d..8bcc92aa 100644
--- a/ibcutils/testing/config.go
+++ b/ibcutils/testing/config.go
@@ -3,11 +3,11 @@ package ibctesting
 import (
 	"time"
 
-	connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types"
-	channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
-	"github.com/cosmos/ibc-go/v4/modules/core/exported"
-	ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types"
-	"github.com/cosmos/ibc-go/v4/testing/mock"
+	connectiontypes "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types"
+	channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
+	"github.com/cosmos/ibc-go/v6/modules/core/exported"
+	ibctmtypes "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint/types"
+	"github.com/cosmos/ibc-go/v6/testing/mock"
 )
 
 type ClientConfig interface {
diff --git a/ibcutils/testing/endpoint.go b/ibcutils/testing/endpoint.go
index 409aa81f..dc3d620d 100644
--- a/ibcutils/testing/endpoint.go
+++ b/ibcutils/testing/endpoint.go
@@ -6,13 +6,13 @@ import (
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	"github.com/stretchr/testify/require"
 
-	clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
-	connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types"
-	channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
-	commitmenttypes "github.com/cosmos/ibc-go/v4/modules/core/23-commitment/types"
-	host "github.com/cosmos/ibc-go/v4/modules/core/24-host"
-	"github.com/cosmos/ibc-go/v4/modules/core/exported"
-	ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types"
+	clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
+	connectiontypes "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types"
+	channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
+	commitmenttypes "github.com/cosmos/ibc-go/v6/modules/core/23-commitment/types"
+	host "github.com/cosmos/ibc-go/v6/modules/core/24-host"
+	"github.com/cosmos/ibc-go/v6/modules/core/exported"
+	ibctmtypes "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint/types"
 )
 
 // Endpoint is a which represents a channel endpoint and its associated
diff --git a/ibcutils/testing/events.go b/ibcutils/testing/events.go
index a1ba73c1..8df9f000 100644
--- a/ibcutils/testing/events.go
+++ b/ibcutils/testing/events.go
@@ -6,9 +6,9 @@ import (
 
 	sdk "github.com/cosmos/cosmos-sdk/types"
 
-	clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
-	connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types"
-	channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
+	clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
+	connectiontypes "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types"
+	channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
 )
 
 // ParseClientIDFromEvents parses events emitted from a MsgCreateClient and returns the
diff --git a/ibcutils/testing/path.go b/ibcutils/testing/path.go
index eb9c94f5..2b4b9f6b 100644
--- a/ibcutils/testing/path.go
+++ b/ibcutils/testing/path.go
@@ -4,7 +4,7 @@ import (
 	"bytes"
 	"fmt"
 
-	channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
+	channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
 )
 
 // Path contains two endpoints representing two chains connected over IBC
diff --git a/ibcutils/testing/values.go b/ibcutils/testing/values.go
index 283eb69e..0627ab99 100644
--- a/ibcutils/testing/values.go
+++ b/ibcutils/testing/values.go
@@ -7,10 +7,10 @@ package ibctesting
 import (
 	"time"
 
-	ibctransfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
-	connectiontypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types"
-	ibctmtypes "github.com/cosmos/ibc-go/v4/modules/light-clients/07-tendermint/types"
-	"github.com/cosmos/ibc-go/v4/testing/mock"
+	ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
+	connectiontypes "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types"
+	ibctmtypes "github.com/cosmos/ibc-go/v6/modules/light-clients/07-tendermint/types"
+	"github.com/cosmos/ibc-go/v6/testing/mock"
 )
 
 const (
diff --git a/ibcutils/utils.go b/ibcutils/utils.go
index 523a2dab..364726f1 100644
--- a/ibcutils/utils.go
+++ b/ibcutils/utils.go
@@ -6,8 +6,8 @@ import (
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 
-	transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
-	channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
+	transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
+	channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
 )
 
 // GetTransferSenderRecipient returns the sender and recipient sdk.AccAddresses
diff --git a/ibcutils/utils_test.go b/ibcutils/utils_test.go
index a295fd0e..0e109938 100644
--- a/ibcutils/utils_test.go
+++ b/ibcutils/utils_test.go
@@ -7,9 +7,9 @@ import (
 
 	sdk "github.com/cosmos/cosmos-sdk/types"
 
-	transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
-	channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
-	ibctesting "github.com/cosmos/ibc-go/v4/testing"
+	transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
+	channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
+	ibctesting "github.com/cosmos/ibc-go/v6/testing"
 )
 
 func init() {
diff --git a/integration_tests/test_runner/src/bin/main.rs b/integration_tests/test_runner/src/bin/main.rs
index 3636aea4..965fd6b7 100644
--- a/integration_tests/test_runner/src/bin/main.rs
+++ b/integration_tests/test_runner/src/bin/main.rs
@@ -4,6 +4,7 @@
 #[macro_use]
 extern crate log;
 
+use althea_proto::cosmos_sdk_proto::ibc;
 use deep_space::Coin;
 use deep_space::Contact;
 use deep_space::PrivateKey;
@@ -29,6 +30,8 @@ use test_runner::tests::onboarding::onboarding_default_params;
 use test_runner::tests::onboarding::onboarding_delist_after;
 use test_runner::tests::onboarding::onboarding_disable_after;
 use test_runner::tests::onboarding::onboarding_disabled_whitelisted;
+use test_runner::tests::upgrade::upgrade_part_1;
+use test_runner::tests::upgrade::upgrade_part_2;
 use test_runner::utils::one_atom;
 use test_runner::utils::one_hundred_eth;
 use test_runner::utils::send_funds_bulk;
@@ -268,6 +271,26 @@ pub async fn main() {
             )
             .await;
             return;
+        } else if test_type == "UPGRADE_PART_1" {
+            upgrade_part_1(
+                &web30,
+                &contact,
+                &ibc_contact,
+                keys,
+                ibc_keys,
+                erc20_addresses,
+            )
+            .await;
+        } else if test_type == "UPGRADE_PART_2" {
+            upgrade_part_2(
+                &web30,
+                &contact,
+                &ibc_contact,
+                keys,
+                ibc_keys,
+                erc20_addresses,
+            )
+            .await;
         } else {
             panic!("Unknown test type: {:?}", test_type);
         }
diff --git a/integration_tests/test_runner/src/tests/mod.rs b/integration_tests/test_runner/src/tests/mod.rs
index 323bf475..1ebfcd3b 100644
--- a/integration_tests/test_runner/src/tests/mod.rs
+++ b/integration_tests/test_runner/src/tests/mod.rs
@@ -6,3 +6,4 @@ pub mod lockup;
 pub mod microtx_fees;
 pub mod native_token;
 pub mod onboarding;
+pub mod upgrade;
\ No newline at end of file
diff --git a/integration_tests/test_runner/src/tests/upgrade.rs b/integration_tests/test_runner/src/tests/upgrade.rs
new file mode 100644
index 00000000..32e68f99
--- /dev/null
+++ b/integration_tests/test_runner/src/tests/upgrade.rs
@@ -0,0 +1,169 @@
+use crate::utils::{
+    execute_upgrade_proposal, wait_for_block, UpgradeProposalParams, ValidatorKeys, EVM_USER_KEYS,
+};
+use clarity::Address as EthAddress;
+use deep_space::client::ChainStatus;
+use deep_space::{Contact, CosmosPrivateKey};
+use std::time::Duration;
+use tokio::time::sleep as delay_for;
+use web30::client::Web3;
+
+use super::erc20_conversion::erc20_conversion_test;
+use super::microtx_fees::microtx_fees_test;
+use super::native_token::native_token_test;
+
+pub const UPGRADE_NAME: &str = "neutrino";
+
+/// Perform a series of integration tests to seed the system with data, then submit and pass a chain
+/// upgrade proposal
+/// NOTE: To run this test, use the tests/run-upgrade-test.sh command with an old binary
+#[allow(clippy::too_many_arguments)]
+pub async fn upgrade_part_1(
+    web30: &Web3,
+    althea_contact: &Contact,
+    ibc_contact: &Contact,
+    keys: Vec<ValidatorKeys>,
+    ibc_keys: Vec<CosmosPrivateKey>,
+    erc20_addresses: Vec<EthAddress>,
+) {
+    info!("Starting upgrade test part 1");
+
+    run_all_recoverable_tests(web30, althea_contact, keys.clone(), erc20_addresses.clone()).await;
+    run_upgrade_specific_tests(
+        web30,
+        althea_contact,
+        ibc_contact,
+        keys.clone(),
+        ibc_keys.clone(),
+        erc20_addresses.clone(),
+        false,
+    )
+    .await;
+
+    let upgrade_height = run_upgrade(althea_contact, keys, UPGRADE_NAME.to_string(), false).await;
+
+    info!(
+        "Ready to run the new binary, waiting for chain panic at upgrade height of {}!",
+        upgrade_height
+    );
+    // Wait for the block before the upgrade height, we won't get a response from the chain
+    let res = wait_for_block(althea_contact, (upgrade_height - 1) as u64).await;
+    if res.is_err() {
+        panic!("Unable to wait for upgrade! {}", res.err().unwrap());
+    }
+
+    delay_for(Duration::from_secs(10)).await; // wait for the new block to halt the chain
+    let status = althea_contact.get_chain_status().await;
+    info!(
+        "Done waiting, chain should be halted, status response: {:?}",
+        status
+    );
+}
+
+/// Perform a series of integration tests after an upgrade has executed
+/// NOTE: To run this test, follow the instructions for v2_upgrade_part_1 and WAIT FOR CHAIN HALT,
+/// then finally run tests/run-tests.sh with V2_UPGRADE_PART_2 as the test type.
+#[allow(clippy::too_many_arguments)]
+pub async fn upgrade_part_2(
+    web30: &Web3,
+    althea_contact: &Contact,
+    ibc_contact: &Contact,
+    keys: Vec<ValidatorKeys>,
+    ibc_keys: Vec<CosmosPrivateKey>,
+    erc20_addresses: Vec<EthAddress>,
+) {
+    info!("Starting upgrade_part_2 test");
+
+    run_all_recoverable_tests(web30, althea_contact, keys.clone(), erc20_addresses.clone()).await;
+    run_upgrade_specific_tests(
+        web30,
+        althea_contact,
+        ibc_contact,
+        keys.clone(),
+        ibc_keys,
+        erc20_addresses.clone(),
+        true,
+    )
+    .await;
+}
+
+pub async fn run_upgrade(
+    contact: &Contact,
+    keys: Vec<ValidatorKeys>,
+    plan_name: String,
+    wait_for_upgrade: bool,
+) -> i64 {
+    let curr_height = contact.get_chain_status().await.unwrap();
+    let curr_height = if let ChainStatus::Moving { block_height } = curr_height {
+        block_height
+    } else {
+        panic!("Chain is not moving!");
+    };
+    let upgrade_height = (curr_height + 40) as i64;
+    let upgrade_prop_params = UpgradeProposalParams {
+        upgrade_height,
+        plan_name,
+        plan_info: "upgrade info here".to_string(),
+        proposal_title: "proposal title here".to_string(),
+        proposal_desc: "proposal description here".to_string(),
+    };
+    info!(
+        "Starting upgrade vote with params name: {}, height: {}",
+        upgrade_prop_params.plan_name.clone(),
+        upgrade_height
+    );
+    execute_upgrade_proposal(contact, &keys, None, upgrade_prop_params).await;
+
+    if wait_for_upgrade {
+        info!(
+            "Ready to run the new binary, waiting for chain panic at upgrade height of {}!",
+            upgrade_height
+        );
+        // Wait for the block before the upgrade height, we won't get a response from the chain
+        let res = wait_for_block(contact, (upgrade_height - 1) as u64).await;
+        if res.is_err() {
+            panic!("Unable to wait for upgrade! {}", res.err().unwrap());
+        }
+
+        delay_for(Duration::from_secs(10)).await; // wait for the new block to halt the chain
+        let status = contact.get_chain_status().await;
+        info!(
+            "Done waiting, chain should be halted, status response: {:?}",
+            status
+        );
+    }
+    upgrade_height
+}
+
+/// Runs many integration tests, but only the ones which DO NOT corrupt state
+pub async fn run_all_recoverable_tests(
+    web30: &Web3,
+    contact: &Contact,
+    keys: Vec<ValidatorKeys>,
+    erc20_addresses: Vec<EthAddress>,
+) {
+    native_token_test(contact, web30, keys.clone()).await;
+    erc20_conversion_test(
+        contact,
+        web30,
+        keys.clone(),
+        EVM_USER_KEYS.clone(),
+        erc20_addresses,
+    )
+    .await;
+    microtx_fees_test(contact, keys.clone()).await;
+}
+
+// These tests should fail in upgrade_part_1() but pass in upgrade_part_2()
+#[allow(clippy::too_many_arguments)]
+pub async fn run_upgrade_specific_tests(
+    _web30: &Web3,
+    _althea_contact: &Contact,
+    _ibc_contact: &Contact,
+    _keys: Vec<ValidatorKeys>,
+    _ibc_keys: Vec<CosmosPrivateKey>,
+    _erc20_addresses: Vec<EthAddress>,
+    _post_upgrade: bool,
+) {
+    // TODO: Add any tests here
+}
diff --git a/tests/container-scripts/manual-upgrade-test-internal.sh b/tests/container-scripts/manual-upgrade-test-internal.sh
new file mode 100755
index 00000000..7b80df4e
--- /dev/null
+++ b/tests/container-scripts/manual-upgrade-test-internal.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+set -eux
+# Number of validators to start
+NODES=$1
+# old binary version to run
+OLD_VERSION=$2
+
+chmod -R 777 /root/
+
+echo "Downloading old althea version at https://github.com/AltheaFoundation/althea-L1/releases/download/${OLD_VERSION}/althea-linux-amd64"
+wget https://github.com/AltheaFoundation/althea-L1/releases/download/${OLD_VERSION}/althea-linux-amd64
+mv althea-linux-amd64 oldalthea
+# Make old althea executable
+chmod +x oldalthea
+
+export OLD_BINARY_LOCATION=/oldalthea
+
+# Prepare the contracts for later deployment
+pushd /althea
+pushd solidity/
+HUSKY_SKIP_INSTALL=1 npm install
+npm run typechain
+popd
+pushd solidity-dex
+npx hardhat compile
+popd
+
+export PATH=$PATH:/usr/local/go/bin
+make
+make install
+cd /althea/
+tests/container-scripts/setup-validators.sh $NODES
+tests/container-scripts/setup-ibc-validators.sh $NODES
+
+# Run the old binary
+tests/container-scripts/run-testnet.sh $NODES
+
+
+# deploy the ethereum contracts
+pushd integration_tests/test_runner
+DEPLOY_CONTRACTS=1 RUST_BACKTRACE=full NO_GAS_OPT=1 RUST_LOG="INFO" PATH=$PATH:$HOME/.cargo/bin cargo run --release --bin test-runner
+popd
+
+# This allows the tester to run the first part of the test
+# immediately if the nodes are killed by a different process
+
+read -p "Old binary is running, use tests/run-tests.sh to run tests/populate pre-upgrade state! Hit Enter to continue to part 2..."
+
+
+unset OLD_BINARY_LOCATION
+# Run the new binary
+pkill oldalthea || true # allowed to fail
+tests/container-scripts/run-testnet.sh $NODES
+
+# This allows the tester to run the first part of the test
+# immediately if the nodes are killed by a different process
+
+read -p "New binary is running, use tests/run-tests.sh to run tests on the upgraded chain! Hit Enter to close the container and end all tests..."
\ No newline at end of file
diff --git a/tests/container-scripts/run-testnet.sh b/tests/container-scripts/run-testnet.sh
new file mode 100755
index 00000000..149346c5
--- /dev/null
+++ b/tests/container-scripts/run-testnet.sh
@@ -0,0 +1,95 @@
+#!/bin/bash
+set -eux
+# your gaiad binary name
+BIN=althea
+
+NODES=$1
+set +u
+TEST_TYPE=$2
+
+GITHUB_ACTIONS_PATH="/home/runner/work/AltheaFoundation/althea-L1/"
+DOCKER_PATH="/althea/"
+
+if [[ -d "$GITHUB_ACTIONS_PATH" ]]; then
+    FOLDER_PATH="$GITHUB_ACTIONS_PATH"
+elif [[ -d "$DOCKER_PATH" ]]; then
+    FOLDER_PATH="$DOCKER_PATH"
+else
+    echo "Error: Neither $GITHUB_ACTIONS_PATH nor $DOCKER_PATH exists."
+    exit 1
+fi
+
+if [[ ! -z ${OLD_BINARY_LOCATION} ]]; then
+    BIN=$OLD_BINARY_LOCATION
+fi
+set -u
+
+for i in $(seq 1 $NODES);
+do
+    # add this ip for loopback dialing
+    ip addr add 7.7.7.$i/32 dev eth0 || true # allowed to fail
+
+    GAIA_HOME="--home /validator$i"
+    # this implicitly caps us at ~6000 nodes for this sim
+    # note that we start on 26656 the idea here is that the first
+    # node (node 1) is at the expected contact address from the gentx
+    # faciliating automated peer exchange
+    if [[ "$i" -eq 1 ]]; then
+        # node one gets localhost so we can easily shunt these ports
+        # to the docker host
+        RPC_ADDRESS="--rpc.laddr tcp://0.0.0.0:26657"
+        GRPC_ADDRESS="--grpc.address 0.0.0.0:9090"
+        GRPC_WEB_ADDRESS="--grpc-web.address 0.0.0.0:9092"
+        ETH_RPC_ADDRESS="--json-rpc.address 0.0.0.0:8545"
+        ETH_RPC_WS_ADDRESS="--json-rpc.ws-address 0.0.0.0:8546"
+        sed -i 's/enable-unsafe-cors = false/enable-unsafe-cors = true/g' /validator$i/config/app.toml
+        sed -i 's/enabled-unsafe-cors = false/enabled-unsafe-cors = true/g' /validator$i/config/app.toml
+        sed -i 's/enable = false/enable = true/g' /validator$i/config/app.toml #enables more than we want, but will work for now
+    else
+        # move these to another port and address, not becuase they will
+        # be used there, but instead to prevent them from causing problems
+        # you also can't duplicate the port selection against localhost
+        # for reasons that are not clear to me right now.
+        RPC_ADDRESS="--rpc.laddr tcp://7.7.7.$i:26658"
+        GRPC_ADDRESS="--grpc.address 7.7.7.$i:9091"
+        GRPC_WEB_ADDRESS="--grpc-web.address 7.7.7.$i:9093"
+        ETH_RPC_ADDRESS="--json-rpc.address 7.7.7.$i:8545"
+        ETH_RPC_WS_ADDRESS="--json-rpc.address 7.7.7.$i:8546"
+    fi
+    LISTEN_ADDRESS="--address tcp://7.7.7.$i:26655"
+    P2P_ADDRESS="--p2p.laddr tcp://7.7.7.$i:26656"
+    LOG_LEVEL="--log_level info"
+    INVARIANTS_CHECK="--inv-check-period 1"
+    MIN_GAS_PRICES="--minimum-gas-prices 0stake"
+
+    ARGS="$GAIA_HOME $LISTEN_ADDRESS $RPC_ADDRESS $GRPC_ADDRESS $GRPC_WEB_ADDRESS $ETH_RPC_ADDRESS $ETH_RPC_WS_ADDRESS $INVARIANTS_CHECK $LOG_LEVEL $P2P_ADDRESS $MIN_GAS_PRICES"
+    $BIN $ARGS start &> /validator$i/logs &
+done
+
+# Setup the IBC test chain (chain id ibc-test-1) using gaiad as the binary
+# Creates the same number of validators as the althea chain above, with their home directories at /ibc-validator#
+BIN=gaiad
+for i in $(seq 1 $NODES);
+do
+    ip addr add 7.7.8.$i/32 dev eth0 || true # allowed to fail
+
+    GAIA_HOME="--home /ibc-validator$i"
+    if [[ "$i" -eq 1 ]]; then
+        # node one gets localhost so we can easily shunt these ports
+        # to the docker host
+        RPC_ADDRESS="--rpc.laddr tcp://0.0.0.0:27657"
+        GRPC_ADDRESS="--grpc.address 0.0.0.0:9190"
+        # Must remap the grpc-web address because it conflicts with what we want to use
+        GRPC_WEB_ADDRESS="--grpc-web.address 0.0.0.0:9192"
+    else
+        RPC_ADDRESS="--rpc.laddr tcp://7.7.8.$i:26658"
+        GRPC_ADDRESS="--grpc.address 7.7.8.$i:9091"
+        # Must remap the grpc-web address because it conflicts with what we want to use
+        GRPC_WEB_ADDRESS="--grpc-web.address 7.7.8.$i:9093"
+    fi
+    LISTEN_ADDRESS="--address tcp://7.7.8.$i:26655"
+    P2P_ADDRESS="--p2p.laddr tcp://7.7.8.$i:26656"
+    LOG_LEVEL="--log_level info"
+    ARGS="$GAIA_HOME $LISTEN_ADDRESS $RPC_ADDRESS $GRPC_ADDRESS $GRPC_WEB_ADDRESS $LOG_LEVEL $P2P_ADDRESS"
+    $BIN $ARGS start &> /ibc-validator$i/logs &
+done
\ No newline at end of file
diff --git a/tests/container-scripts/setup-validators.sh b/tests/container-scripts/setup-validators.sh
index 75325579..d804e68e 100755
--- a/tests/container-scripts/setup-validators.sh
+++ b/tests/container-scripts/setup-validators.sh
@@ -7,6 +7,16 @@ CHAIN_ID="althea_6633438-1"
 
 NODES=$1
 
+# When doing an upgrade test we need to run init using the old binary so we don't include newly added fields
+set +u
+if [[ ! -z ${OLD_BINARY_LOCATION} ]]; then
+  echo "Replacing althea with $OLD_BINARY_LOCATION"
+  BIN=$OLD_BINARY_LOCATION
+else
+  echo "Old binary not set, using regular althea"
+fi
+set -u
+
 STAKING_TOKEN="aalthea"
 ALLOC_18_DECIMALS="1000000000000000000000000"
 ALLOC_6_DECIMALS="1000000000000"
diff --git a/tests/container-scripts/start-dlv.sh b/tests/container-scripts/start-dlv.sh
new file mode 100755
index 00000000..544c4763
--- /dev/null
+++ b/tests/container-scripts/start-dlv.sh
@@ -0,0 +1,7 @@
+#! /bin/bash
+
+# Find the PID of the validator1 node
+VALIDATOR_1_PID=$(ps -eafww | grep /validator1 | head -n1 | awk '{print $2}')
+
+# Run dlv and attach to validator1
+/althea/tests/assets/dlv attach $VALIDATOR_1_PID --listen=:2345 --headless --api-version=2 --accept-multiclient --continue &
\ No newline at end of file
diff --git a/tests/container-scripts/upgrade-test-internal.sh b/tests/container-scripts/upgrade-test-internal.sh
new file mode 100755
index 00000000..da3fc95b
--- /dev/null
+++ b/tests/container-scripts/upgrade-test-internal.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+set -eux
+# Number of validators to start
+NODES=$1
+# old binary version to run
+OLD_VERSION=$2
+
+echo "Downloading old althea version at https://github.com/AltheaFoundation/althea-L1/releases/download/${OLD_VERSION}/althea-linux-amd64"
+wget https://github.com/AltheaFoundation/althea-L1/releases/download/${OLD_VERSION}/althea-linux-amd64
+mv althea-linux-amd64 oldalthea
+# Make old althea executable
+chmod +x oldalthea
+
+export OLD_BINARY_LOCATION=/oldalthea
+
+# Prepare the contracts for later deployment
+pushd /althea/solidity/
+HUSKY_SKIP_INSTALL=1 npm install
+npm run typechain
+popd
+
+pushd /althea/solidity-dex/
+npx hardhat compile
+popd
+
+pushd /althea/
+export PATH=$PATH:/usr/local/go/bin
+make
+make install
+tests/container-scripts/setup-validators.sh $NODES
+tests/container-scripts/setup-ibc-validators.sh $NODES
+
+# Run the old binary
+tests/container-scripts/run-testnet.sh $NODES
+popd
+
+# deploy the ethereum contracts
+pushd /althea/integration_tests/test_runner
+DEPLOY_CONTRACTS=1 RUST_BACKTRACE=full NO_GAS_OPT=1 RUST_LOG="INFO" PATH=$PATH:$HOME/.cargo/bin cargo run --release --bin test-runner
+popd
+
+# Run the pre-upgrade tests
+pushd /althea/
+tests/container-scripts/integration-tests.sh $NODES UPGRADE_PART_1
+
+unset OLD_BINARY_LOCATION
+# Run the new binary
+pkill oldalthea || true # allowed to fail
+tests/container-scripts/run-testnet.sh $NODES
+
+# Run the post-upgrade test
+tests/container-scripts/integration-tests.sh $NODES UPGRADE_PART_2
+popd
diff --git a/tests/manual-upgrade-test.sh b/tests/manual-upgrade-test.sh
new file mode 100755
index 00000000..0ed0a339
--- /dev/null
+++ b/tests/manual-upgrade-test.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+OLD_VERSION=$1
+set -eux
+
+if [[ -z "${OLD_VERSION}" ]]; then
+  echo "Must provide old althea version for upgrade test, make sure it matches a version at https://github.com/AltheaFoundation/althea-L1/releases"
+  exit 1
+fi
+
+# Remove existing container instance
+set +e
+docker rm -f althea_test_instance
+set -e
+
+# the directory of this script, useful for allowing this script
+# to be run with any PWD
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+NODES=4
+
+# setup for Mac apple silicon compatibility
+PLATFORM_CMD=""
+if [[ "$OSTYPE" == "darwin"* ]]; then
+    if [[ -n $(sysctl -a | grep brand | grep "Apple") ]]; then
+       echo "Setting --platform=linux/amd64 for Mac apple silicon compatibility"
+       PLATFORM_CMD="--platform=linux/amd64"; fi
+fi
+
+# Run new test container instance
+PORTS="-p 9090:9090 -p 26657:26657 -p 1317:1317 -p 8545:8545"
+docker run --name althea_test_instance --mount type=bind,source="$(pwd)"/,target=/althea $PLATFORM_CMD --cap-add=NET_ADMIN $PORTS -it althea-base /bin/bash /althea/tests/container-scripts/manual-upgrade-test-internal.sh $NODES $OLD_VERSION
diff --git a/tests/run-upgrade-test.sh b/tests/run-upgrade-test.sh
new file mode 100755
index 00000000..f4e3a43f
--- /dev/null
+++ b/tests/run-upgrade-test.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+OLD_VERSION=$1
+set -eux
+
+if [[ -z "${OLD_VERSION}" ]]; then
+  echo "Must provide old althea-l1 version for upgrade test, make sure it matches a version at https://github.com/AltheaFoundation/althea-L1/releases"
+  exit 1
+fi
+
+# Remove existing container instance
+set +e
+docker rm -f althea_all_up_test_instance
+set -e
+
+# the directory of this script, useful for allowing this script
+# to be run with any PWD
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+set +u
+if [[ -z ${NO_IMAGE_BUILD} ]]; then
+bash $DIR/build-container.sh
+fi
+set -u
+
+NODES=4
+
+# setup for Mac apple silicon compatibility
+PLATFORM_CMD=""
+if [[ "$OSTYPE" == "darwin"* ]]; then
+    if [[ -n $(sysctl -a | grep brand | grep "Apple") ]]; then
+       echo "Setting --platform=linux/amd64 for Mac apple silicon compatibility"
+       PLATFORM_CMD="--platform=linux/amd64"; fi
+fi
+
+# Run new test container instance
+PORTS="-p 9090:9090 -p 26657:26657 -p 1317:1317 -p 8545:8545"
+docker run --name althea_all_up_test_instance $PLATFORM_CMD --cap-add=NET_ADMIN $PORTS althea-base /bin/bash /althea/tests/container-scripts/upgrade-test-internal.sh $NODES $OLD_VERSION
diff --git a/x/gasfree/keeper/keeper.go b/x/gasfree/keeper/keeper.go
index 23b721de..7a32bbe7 100644
--- a/x/gasfree/keeper/keeper.go
+++ b/x/gasfree/keeper/keeper.go
@@ -2,6 +2,7 @@ package keeper
 
 import (
 	"github.com/cosmos/cosmos-sdk/codec"
+	storetypes "github.com/cosmos/cosmos-sdk/store/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	"github.com/cosmos/cosmos-sdk/x/authz"
@@ -11,12 +12,12 @@ import (
 )
 
 type Keeper struct {
-	storeKey   sdk.StoreKey
+	storeKey   storetypes.StoreKey
 	paramSpace paramstypes.Subspace
 	Cdc        codec.Codec
 }
 
-func NewKeeper(cdc codec.Codec, storeKey sdk.StoreKey, paramSpace paramstypes.Subspace) Keeper {
+func NewKeeper(cdc codec.Codec, storeKey storetypes.StoreKey, paramSpace paramstypes.Subspace) Keeper {
 	// set KeyTable if it has not already been set
 	if !paramSpace.HasKeyTable() {
 		paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable())
diff --git a/x/lockup/ante.go b/x/lockup/ante.go
index 974a7d53..5f869855 100644
--- a/x/lockup/ante.go
+++ b/x/lockup/ante.go
@@ -13,7 +13,7 @@ import (
 	authz "github.com/cosmos/cosmos-sdk/x/authz"
 	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 
-	ibctransfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
+	ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
 
 	microtxtypes "github.com/AltheaFoundation/althea-L1/x/microtx/types"
 
diff --git a/x/lockup/ante_test.go b/x/lockup/ante_test.go
index 12a26ae5..8b638750 100644
--- a/x/lockup/ante_test.go
+++ b/x/lockup/ante_test.go
@@ -10,8 +10,8 @@ import (
 	authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
 	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
-	ibctransfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
-	ibcclienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
+	ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
+	ibcclienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
 	"github.com/stretchr/testify/assert"
 
 	"github.com/AltheaFoundation/althea-L1/x/lockup/keeper"
@@ -166,7 +166,7 @@ func AnteHandlerUnlockedHappy(t *testing.T, handler sdk.AnteHandler, keeper keep
 
 func GetAllowedMsgSendTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Factory, txCfg client.TxConfig) sdk.Tx {
 	msgSend := GetAllowedMsgSend(keeper, ctx)
-	txBld, err := tx.BuildUnsignedTx(txFct, &msgSend)
+	txBld, err := txFct.BuildUnsignedTx(&msgSend)
 	if err != nil {
 		panic(fmt.Sprintf("Unable to build unsigned transaction containing %v: %v", msgSend, err))
 	}
@@ -191,7 +191,7 @@ func GetAllowedLargeTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Factory,
 	msgSend := GetAllowedMsgSend(keeper, ctx)
 	multiSend := GetAllowedMultiSendMsg(keeper, ctx)
 	unimportant := GetUnimportantMsg()
-	txBld, err := tx.BuildUnsignedTx(txFct, &msgSend, &multiSend, &msgSend, &multiSend, &unimportant, &unimportant)
+	txBld, err := txFct.BuildUnsignedTx(&msgSend, &multiSend, &msgSend, &multiSend, &unimportant, &unimportant)
 	if err != nil {
 		panic(fmt.Sprintf("Unable to build unsigned transaction containing %v: %v", msgSend, err))
 	}
@@ -201,7 +201,7 @@ func GetAllowedLargeTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Factory,
 
 func GetAllowedMultiSendTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Factory, txCfg client.TxConfig) sdk.Tx {
 	multiSend := GetAllowedMultiSendMsg(keeper, ctx)
-	txBld, err := tx.BuildUnsignedTx(txFct, &multiSend)
+	txBld, err := txFct.BuildUnsignedTx(&multiSend)
 	if err != nil {
 		panic(fmt.Sprintf("Unable to build unsigned transaction containing %v: %v", multiSend, err))
 	}
@@ -224,7 +224,7 @@ func GetAllowedMultiSendMsg(keeper keeper.Keeper, ctx sdk.Context) banktypes.Msg
 
 func GetUnimportantTx(txFct tx.Factory, txCfg client.TxConfig) sdk.Tx {
 	unimportantMsg := GetUnimportantMsg()
-	txBld, err := tx.BuildUnsignedTx(txFct, &unimportantMsg)
+	txBld, err := txFct.BuildUnsignedTx(&unimportantMsg)
 	if err != nil {
 		panic(fmt.Sprintf("Unable to build unsigned transaction containing %v: %v", unimportantMsg, err))
 	}
@@ -244,7 +244,7 @@ func GetUnimportantMsg() stakingtypes.MsgCreateValidator {
 
 func GetAllowedMsgTransferTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Factory, txCfg client.TxConfig) sdk.Tx {
 	msgTransfer := GetAllowedMsgTransfer(keeper, ctx)
-	txBld, err := tx.BuildUnsignedTx(txFct, &msgTransfer)
+	txBld, err := txFct.BuildUnsignedTx(&msgTransfer)
 	if err != nil {
 		panic(fmt.Sprintf("Unable to build unsigned transaction containing %v: %v", msgTransfer, err))
 	}
@@ -276,7 +276,7 @@ func GetAllowedMsgTransfer(keeper keeper.Keeper, ctx sdk.Context) ibctransfertyp
 
 func GetAllowedMsgMicrotxTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Factory, txCfg client.TxConfig) sdk.Tx {
 	msgMicrotx := GetAllowedMsgMicrotx(keeper, ctx)
-	txBld, err := tx.BuildUnsignedTx(txFct, &msgMicrotx)
+	txBld, err := txFct.BuildUnsignedTx(&msgMicrotx)
 	if err != nil {
 		panic(fmt.Sprintf("Unable to build unsigned transaction containing %v: %v", msgMicrotx, err))
 	}
@@ -309,7 +309,7 @@ func GetUnallowedMsgSendTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Facto
 	toAddr := "0x0000000000000000000000000000000000000000"
 	amount := sdk.NewCoins(sdk.NewCoin("aalthea", sdk.NewInt(1000000000000000000)))
 	msgSend := banktypes.MsgSend{FromAddress: fromAddr, ToAddress: toAddr, Amount: amount}
-	txBld, err := tx.BuildUnsignedTx(txFct, &msgSend)
+	txBld, err := txFct.BuildUnsignedTx(&msgSend)
 	if err != nil {
 		panic(fmt.Sprintf("Unable to build unsigned transaction containing %v: %v", msgSend, err))
 	}
@@ -319,7 +319,7 @@ func GetUnallowedMsgSendTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Facto
 
 func GetUnallowedMultiSendTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Factory, txCfg client.TxConfig) sdk.Tx {
 	multiSend := GetUnallowedMultiSendMsg(keeper, ctx)
-	txBld, err := tx.BuildUnsignedTx(txFct, &multiSend)
+	txBld, err := txFct.BuildUnsignedTx(&multiSend)
 	if err != nil {
 		panic(fmt.Sprintf("Unable to build unsigned transaction containing %v: %v", multiSend, err))
 	}
@@ -345,7 +345,7 @@ func GetUnallowedLargeTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Factory
 	multiSend := GetAllowedMultiSendMsg(keeper, ctx)
 	badMultiSend := GetUnallowedMultiSendMsg(keeper, ctx)
 	unimportant := GetUnimportantMsg()
-	txBld, err := tx.BuildUnsignedTx(txFct, &msgSend, &multiSend, &msgSend, &badMultiSend, &multiSend, &unimportant, &unimportant)
+	txBld, err := txFct.BuildUnsignedTx(&msgSend, &multiSend, &msgSend, &badMultiSend, &multiSend, &unimportant, &unimportant)
 	if err != nil {
 		panic(fmt.Sprintf("Unable to build unsigned transaction containing %v: %v", msgSend, err))
 	}
@@ -355,7 +355,7 @@ func GetUnallowedLargeTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Factory
 
 func GetUnallowedMsgTransferTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Factory, txCfg client.TxConfig) sdk.Tx {
 	msgTransfer := GetUnallowedMsgTransfer(keeper, ctx)
-	txBld, err := tx.BuildUnsignedTx(txFct, &msgTransfer)
+	txBld, err := txFct.BuildUnsignedTx(&msgTransfer)
 	if err != nil {
 		panic(fmt.Sprintf("Unable to build unsigned transaction containing %v: %v", msgTransfer, err))
 	}
@@ -387,7 +387,7 @@ func GetUnallowedMsgTransfer(keeper keeper.Keeper, ctx sdk.Context) ibctransfert
 
 func GetUnallowedMsgMicrotxTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Factory, txCfg client.TxConfig) sdk.Tx {
 	msgMicrotx := GetUnallowedMsgMicrotx(keeper, ctx)
-	txBld, err := tx.BuildUnsignedTx(txFct, &msgMicrotx)
+	txBld, err := txFct.BuildUnsignedTx(&msgMicrotx)
 	if err != nil {
 		panic(fmt.Sprintf("Unable to build unsigned transaction containing %v: %v", msgMicrotx, err))
 	}
diff --git a/x/lockup/keeper/keeper.go b/x/lockup/keeper/keeper.go
index f7e17145..7d4c5f29 100644
--- a/x/lockup/keeper/keeper.go
+++ b/x/lockup/keeper/keeper.go
@@ -2,6 +2,7 @@ package keeper
 
 import (
 	"github.com/cosmos/cosmos-sdk/codec"
+	storetypes "github.com/cosmos/cosmos-sdk/store/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	paramstypes "github.com/cosmos/cosmos-sdk/x/params/types"
@@ -10,12 +11,12 @@ import (
 )
 
 type Keeper struct {
-	storeKey   sdk.StoreKey
+	storeKey   storetypes.StoreKey
 	paramSpace paramstypes.Subspace
 	cdc        codec.BinaryCodec
 }
 
-func NewKeeper(cdc codec.BinaryCodec, storeKey sdk.StoreKey, paramSpace paramstypes.Subspace) Keeper {
+func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, paramSpace paramstypes.Subspace) Keeper {
 	// set KeyTable if it has not already been set
 	if !paramSpace.HasKeyTable() {
 		paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable())
diff --git a/x/lockup/keeper/test_common.go b/x/lockup/keeper/test_common.go
index d8a55b3d..4554a980 100644
--- a/x/lockup/keeper/test_common.go
+++ b/x/lockup/keeper/test_common.go
@@ -10,6 +10,7 @@ import (
 	ccodec "github.com/cosmos/cosmos-sdk/crypto/codec"
 	"github.com/cosmos/cosmos-sdk/std"
 	"github.com/cosmos/cosmos-sdk/store"
+	storetypes "github.com/cosmos/cosmos-sdk/store/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	"github.com/cosmos/cosmos-sdk/types/module"
 	"github.com/cosmos/cosmos-sdk/x/auth"
@@ -27,8 +28,11 @@ import (
 	"github.com/cosmos/cosmos-sdk/x/evidence"
 	"github.com/cosmos/cosmos-sdk/x/genutil"
 	"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"
+	govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
+	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
 	"github.com/cosmos/cosmos-sdk/x/mint"
 	"github.com/cosmos/cosmos-sdk/x/params"
 	paramsclient "github.com/cosmos/cosmos-sdk/x/params/client"
@@ -68,7 +72,7 @@ var (
 		staking.AppModuleBasic{},
 		mint.AppModuleBasic{},
 		distribution.AppModuleBasic{},
-		gov.NewAppModuleBasic(paramsclient.ProposalHandler),
+		gov.NewAppModuleBasic([]govclient.ProposalHandler{paramsclient.ProposalHandler}),
 		params.AppModuleBasic{},
 		crisis.AppModuleBasic{},
 		slashing.AppModuleBasic{},
@@ -104,13 +108,13 @@ func CreateTestEnv(t *testing.T) TestInput {
 	// Initialize memory database and mount stores on it
 	db := dbm.NewMemDB()
 	ms := store.NewCommitMultiStore(db)
-	ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db)
-	ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
-	ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db)
-	ms.MountStoreWithDB(keyBank, sdk.StoreTypeIAVL, db)
-	ms.MountStoreWithDB(keyDistro, sdk.StoreTypeIAVL, db)
-	ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db)
-	ms.MountStoreWithDB(keyGov, sdk.StoreTypeIAVL, db)
+	ms.MountStoreWithDB(keyAcc, storetypes.StoreTypeIAVL, db)
+	ms.MountStoreWithDB(keyParams, storetypes.StoreTypeIAVL, db)
+	ms.MountStoreWithDB(keyStaking, storetypes.StoreTypeIAVL, db)
+	ms.MountStoreWithDB(keyBank, storetypes.StoreTypeIAVL, db)
+	ms.MountStoreWithDB(keyDistro, storetypes.StoreTypeIAVL, db)
+	ms.MountStoreWithDB(tkeyParams, storetypes.StoreTypeTransient, db)
+	ms.MountStoreWithDB(keyGov, storetypes.StoreTypeIAVL, db)
 	err := ms.LoadLatestVersion()
 	require.Nil(t, err)
 
@@ -149,6 +153,7 @@ func CreateTestEnv(t *testing.T) TestInput {
 		getSubspace(paramsKeeper, authtypes.ModuleName),
 		authtypes.ProtoBaseAccount, // prototype
 		maccPerms,
+		"althea",
 	)
 
 	blockedAddr := make(map[string]bool, len(maccPerms))
@@ -169,7 +174,7 @@ func CreateTestEnv(t *testing.T) TestInput {
 	stakingKeeper := stakingkeeper.NewKeeper(protoCodec, keyStaking, accountKeeper, bankKeeper, getSubspace(paramsKeeper, stakingtypes.ModuleName))
 	stakingKeeper.SetParams(ctx, TestingStakeParams)
 
-	distKeeper := distrkeeper.NewKeeper(protoCodec, keyDistro, getSubspace(paramsKeeper, distrtypes.ModuleName), accountKeeper, bankKeeper, stakingKeeper, authtypes.FeeCollectorName, nil)
+	distKeeper := distrkeeper.NewKeeper(protoCodec, keyDistro, getSubspace(paramsKeeper, distrtypes.ModuleName), accountKeeper, bankKeeper, stakingKeeper, authtypes.FeeCollectorName)
 	distKeeper.SetParams(ctx, distrtypes.DefaultParams())
 
 	// set genesis items required for distribution
@@ -209,18 +214,20 @@ func CreateTestEnv(t *testing.T) TestInput {
 	// nolint: exhaustruct
 	router.AddRoute(distribution.AppModule{}.Route())
 
-	govRouter := govtypes.NewRouter().
+	govRouter := govv1beta1.NewRouter().
 		AddRoute(paramsproposal.RouterKey, params.NewParamChangeProposalHandler(paramsKeeper)).
-		AddRoute(govtypes.RouterKey, govtypes.ProposalHandler)
+		AddRoute(govtypes.RouterKey, govv1beta1.ProposalHandler)
 
+	govConfig := govtypes.DefaultConfig()
 	govKeeper := govkeeper.NewKeeper(
-		protoCodec, keyGov, getSubspace(paramsKeeper, govtypes.ModuleName).WithKeyTable(govtypes.ParamKeyTable()), accountKeeper, bankKeeper, stakingKeeper, govRouter,
+		protoCodec, keyGov, getSubspace(paramsKeeper, govtypes.ModuleName).WithKeyTable(govv1.ParamKeyTable()), accountKeeper, bankKeeper, stakingKeeper, govRouter,
+		baseapp.NewMsgServiceRouter(), govConfig,
 	)
 
-	govKeeper.SetProposalID(ctx, govtypes.DefaultStartingProposalID)
-	govKeeper.SetDepositParams(ctx, govtypes.DefaultDepositParams())
-	govKeeper.SetVotingParams(ctx, govtypes.DefaultVotingParams())
-	govKeeper.SetTallyParams(ctx, govtypes.DefaultTallyParams())
+	govKeeper.SetProposalID(ctx, govv1beta1.DefaultStartingProposalID)
+	govKeeper.SetDepositParams(ctx, govv1.DefaultDepositParams())
+	govKeeper.SetVotingParams(ctx, govv1.DefaultVotingParams())
+	govKeeper.SetTallyParams(ctx, govv1.DefaultTallyParams())
 	k := NewKeeper(protoCodec, lockupKey, getSubspace(paramsKeeper, types.ModuleName))
 
 	InitGenesis(ctx, k, *types.DefaultGenesisState())
diff --git a/x/lockup/types/genesis.go b/x/lockup/types/genesis.go
index fc5920c0..8b7c5d16 100644
--- a/x/lockup/types/genesis.go
+++ b/x/lockup/types/genesis.go
@@ -7,7 +7,7 @@ import (
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
-	ibctransfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
+	ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
 
 	"github.com/AltheaFoundation/althea-L1/config"
 	microtxtypes "github.com/AltheaFoundation/althea-L1/x/microtx/types"
diff --git a/x/microtx/keeper/keeper.go b/x/microtx/keeper/keeper.go
index 489431f0..9311ecee 100644
--- a/x/microtx/keeper/keeper.go
+++ b/x/microtx/keeper/keeper.go
@@ -6,13 +6,14 @@ import (
 	"github.com/tendermint/tendermint/libs/log"
 
 	"github.com/cosmos/cosmos-sdk/codec"
+	storetypes "github.com/cosmos/cosmos-sdk/store/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
 	bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
 	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
 
-	erc20keeper "github.com/Canto-Network/Canto/v5/x/erc20/keeper"
+	erc20keeper "github.com/Canto-Network/Canto/v6/x/erc20/keeper"
 	evmkeeper "github.com/evmos/ethermint/x/evm/keeper"
 
 	gasfreekeeper "github.com/AltheaFoundation/althea-L1/x/gasfree/keeper"
@@ -22,7 +23,7 @@ import (
 // Keeper maintains the link to storage and exposes getter/setter methods for the various parts of the state machine
 type Keeper struct {
 	// NOTE: If you add anything to this struct, add a nil check to ValidateMembers below!
-	storeKey   sdk.StoreKey // Unexposed key to access store from sdk.Context
+	storeKey   storetypes.StoreKey // Unexposed key to access store from sdk.Context
 	paramSpace paramtypes.Subspace
 
 	// NOTE: If you add anything to this struct, add a nil check to ValidateMembers below!
@@ -55,7 +56,7 @@ func (k Keeper) ValidateMembers() {
 
 // NewKeeper returns a new instance of the microtx keeper
 func NewKeeper(
-	storeKey sdk.StoreKey,
+	storeKey storetypes.StoreKey,
 	paramSpace paramtypes.Subspace,
 	cdc codec.BinaryCodec,
 	bankKeeper *bankkeeper.BaseKeeper,
diff --git a/x/microtx/keeper/liquid_account.go b/x/microtx/keeper/liquid_account.go
index 5f160038..53587297 100644
--- a/x/microtx/keeper/liquid_account.go
+++ b/x/microtx/keeper/liquid_account.go
@@ -9,7 +9,7 @@ import (
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/evmos/ethermint/crypto/ethsecp256k1"
 
-	erc20types "github.com/Canto-Network/Canto/v5/x/erc20/types"
+	erc20types "github.com/Canto-Network/Canto/v6/x/erc20/types"
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
 
 	"github.com/cosmos/cosmos-sdk/store/prefix"
diff --git a/x/microtx/types/msgs.go b/x/microtx/types/msgs.go
index 5885b032..172c7dc3 100644
--- a/x/microtx/types/msgs.go
+++ b/x/microtx/types/msgs.go
@@ -3,7 +3,7 @@ package types
 import (
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
-	authlegacy "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx"
+	authlegacy "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
 )
 
 const (
diff --git a/x/nativedex/client/cli/utils.go b/x/nativedex/client/cli/utils.go
index 06ad2c74..0b83c4d4 100644
--- a/x/nativedex/client/cli/utils.go
+++ b/x/nativedex/client/cli/utils.go
@@ -13,7 +13,7 @@ import (
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	"github.com/cosmos/cosmos-sdk/x/gov/client/cli"
-	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
+	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
 
 	"github.com/AltheaFoundation/althea-L1/x/nativedex/types"
 )
@@ -64,9 +64,9 @@ func GenericProposalCmdSetup(cmd *cobra.Command) (setup GenericProposalSetup, er
 	return
 }
 
-func GenericProposalCmdBroadcast(cmd *cobra.Command, clientCtx client.Context, content govtypes.Content, deposit sdk.Coins, from sdk.AccAddress) error {
+func GenericProposalCmdBroadcast(cmd *cobra.Command, clientCtx client.Context, content govv1beta1.Content, deposit sdk.Coins, from sdk.AccAddress) error {
 
-	msg, err := govtypes.NewMsgSubmitProposal(content, deposit, from)
+	msg, err := govv1beta1.NewMsgSubmitProposal(content, deposit, from)
 	if err != nil {
 		return err
 	}
diff --git a/x/nativedex/client/proposal_handler.go b/x/nativedex/client/proposal_handler.go
new file mode 100644
index 00000000..1ce0772b
--- /dev/null
+++ b/x/nativedex/client/proposal_handler.go
@@ -0,0 +1,17 @@
+package client
+
+import (
+	govclient "github.com/cosmos/cosmos-sdk/x/gov/client"
+
+	"github.com/AltheaFoundation/althea-L1/x/nativedex/client/cli"
+)
+
+var (
+	UpgradeProxyHandler       = govclient.NewProposalHandler(cli.NewUpgradeProxyProposalCmd)
+	CollectTreasuryHandler    = govclient.NewProposalHandler(cli.NewCollectTreasuryProposalCmd)
+	SetTreasuryHandler        = govclient.NewProposalHandler(cli.NewSetTreasuryProposalCmd)
+	AuthorityTransferHandler  = govclient.NewProposalHandler(cli.NewAuthorityTransferProposalCmd)
+	HotPathOpenHandler        = govclient.NewProposalHandler(cli.NewHotPathOpenProposalCmd)
+	SetSafeModeHandler        = govclient.NewProposalHandler(cli.NewSetSafeModeProposalCmd)
+	TransferGovernanceHandler = govclient.NewProposalHandler(cli.NewTransferGovernanceProposalCmd)
+)
diff --git a/x/nativedex/keeper/keeper.go b/x/nativedex/keeper/keeper.go
index 8a185f1b..61912e0f 100644
--- a/x/nativedex/keeper/keeper.go
+++ b/x/nativedex/keeper/keeper.go
@@ -7,6 +7,7 @@ import (
 	"github.com/tendermint/tendermint/libs/log"
 
 	"github.com/cosmos/cosmos-sdk/codec"
+	storetypes "github.com/cosmos/cosmos-sdk/store/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
 
@@ -15,7 +16,7 @@ import (
 
 type (
 	Keeper struct {
-		storeKey   sdk.StoreKey
+		storeKey   storetypes.StoreKey
 		cdc        codec.BinaryCodec
 		paramSpace paramtypes.Subspace
 
@@ -24,7 +25,7 @@ type (
 )
 
 func NewKeeper(
-	storeKey sdk.StoreKey,
+	storeKey storetypes.StoreKey,
 	cdc codec.BinaryCodec,
 	ps paramtypes.Subspace,
 
diff --git a/x/nativedex/proposal_handler.go b/x/nativedex/proposal_handler.go
index 8ddebf86..7ca4dba9 100644
--- a/x/nativedex/proposal_handler.go
+++ b/x/nativedex/proposal_handler.go
@@ -3,7 +3,7 @@ package nativedex
 import (
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
-	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
+	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
 	"github.com/ethereum/go-ethereum/common"
 
 	"github.com/AltheaFoundation/althea-L1/contracts"
@@ -26,8 +26,8 @@ const (
 )
 
 // Return governance handler to process dex governance proposals
-func NewNativeDexProposalHandler(k *keeper.Keeper) govtypes.Handler {
-	return func(ctx sdk.Context, content govtypes.Content) error {
+func NewNativeDexProposalHandler(k *keeper.Keeper) govv1beta1.Handler {
+	return func(ctx sdk.Context, content govv1beta1.Content) error {
 		switch c := content.(type) {
 		case *types.UpgradeProxyProposal:
 			return handleUpgradeProxyProposal(ctx, k, c)
diff --git a/x/nativedex/types/codec.go b/x/nativedex/types/codec.go
index 061def9a..97a62449 100644
--- a/x/nativedex/types/codec.go
+++ b/x/nativedex/types/codec.go
@@ -4,7 +4,7 @@ import (
 	"github.com/cosmos/cosmos-sdk/codec"
 	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
 	"github.com/cosmos/cosmos-sdk/types/msgservice"
-	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
+	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
 )
 
 func RegisterCodec(cdc *codec.LegacyAmino) {
@@ -13,7 +13,7 @@ func RegisterCodec(cdc *codec.LegacyAmino) {
 // nolint: exhaustruct
 func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
 	registry.RegisterImplementations(
-		(*govtypes.Content)(nil),
+		(*govv1beta1.Content)(nil),
 		&UpgradeProxyProposal{},
 		&CollectTreasuryProposal{},
 		&SetTreasuryProposal{},
diff --git a/x/nativedex/types/proposal.go b/x/nativedex/types/proposal.go
index 7ad423aa..70118850 100644
--- a/x/nativedex/types/proposal.go
+++ b/x/nativedex/types/proposal.go
@@ -6,6 +6,7 @@ import (
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 
 	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
+	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
 )
 
 const (
@@ -25,38 +26,28 @@ var AcceptableCallpathIndexes []uint64 = []uint64{1, 2, 3, 4, 5, 6, 7, 3500, 999
 
 // nolint: exhaustruct
 var (
-	_ govtypes.Content = &UpgradeProxyProposal{}
-	_ govtypes.Content = &CollectTreasuryProposal{}
-	_ govtypes.Content = &SetTreasuryProposal{}
-	_ govtypes.Content = &AuthorityTransferProposal{}
-	_ govtypes.Content = &HotPathOpenProposal{}
-	_ govtypes.Content = &SetSafeModeProposal{}
-	_ govtypes.Content = &TransferGovernanceProposal{}
-	_ govtypes.Content = &OpsProposal{}
+	_ govv1beta1.Content = &UpgradeProxyProposal{}
+	_ govv1beta1.Content = &CollectTreasuryProposal{}
+	_ govv1beta1.Content = &SetTreasuryProposal{}
+	_ govv1beta1.Content = &AuthorityTransferProposal{}
+	_ govv1beta1.Content = &HotPathOpenProposal{}
+	_ govv1beta1.Content = &SetSafeModeProposal{}
+	_ govv1beta1.Content = &TransferGovernanceProposal{}
 )
 
 // Register Compound Proposal type as a valid proposal type in goveranance module
 // nolint: exhaustruct
 func init() {
-	govtypes.RegisterProposalType(ProposalTypeUpgradeProxy)
-	govtypes.RegisterProposalTypeCodec(&UpgradeProxyProposal{}, "nativedex/UpgradeProxyProposal")
-	govtypes.RegisterProposalType(ProposalTypeCollectTreasury)
-	govtypes.RegisterProposalTypeCodec(&CollectTreasuryProposal{}, "nativedex/CollectTreasuryProposal")
-	govtypes.RegisterProposalType(ProposalTypeSetTreasury)
-	govtypes.RegisterProposalTypeCodec(&SetTreasuryProposal{}, "nativedex/SetTreasuryProposal")
-	govtypes.RegisterProposalType(ProposalTypeAuthorityTransfer)
-	govtypes.RegisterProposalTypeCodec(&AuthorityTransferProposal{}, "nativedex/AuthorityTransferProposal")
-	govtypes.RegisterProposalType(ProposalTypeHotPathOpen)
-	govtypes.RegisterProposalTypeCodec(&HotPathOpenProposal{}, "nativedex/HotPathOpenProposal")
-	govtypes.RegisterProposalType(ProposalTypeSetSafeMode)
-	govtypes.RegisterProposalTypeCodec(&SetSafeModeProposal{}, "nativedex/SetSafeModeProposal")
-	govtypes.RegisterProposalType(ProposalTypeTransferGovernance)
-	govtypes.RegisterProposalTypeCodec(&TransferGovernanceProposal{}, "nativedex/TransferGovernanceProposal")
-	govtypes.RegisterProposalType(ProposalTypeOps)
-	govtypes.RegisterProposalTypeCodec(&OpsProposal{}, "nativedex/OpsProposal")
+	govv1beta1.RegisterProposalType(ProposalTypeUpgradeProxy)
+	govv1beta1.RegisterProposalType(ProposalTypeCollectTreasury)
+	govv1beta1.RegisterProposalType(ProposalTypeSetTreasury)
+	govv1beta1.RegisterProposalType(ProposalTypeAuthorityTransfer)
+	govv1beta1.RegisterProposalType(ProposalTypeHotPathOpen)
+	govv1beta1.RegisterProposalType(ProposalTypeSetSafeMode)
+	govv1beta1.RegisterProposalType(ProposalTypeTransferGovernance)
 }
 
-func NewUpgradeProxyProposal(title, description string, md UpgradeProxyMetadata) govtypes.Content {
+func NewUpgradeProxyProposal(title, description string, md UpgradeProxyMetadata) govv1beta1.Content {
 	return &UpgradeProxyProposal{
 		Title:       title,
 		Description: description,
@@ -71,7 +62,7 @@ func (*UpgradeProxyProposal) ProposalType() string {
 }
 
 func (p *UpgradeProxyProposal) ValidateBasic() error {
-	if err := govtypes.ValidateAbstract(p); err != nil {
+	if err := govv1beta1.ValidateAbstract(p); err != nil {
 		return err
 	}
 
@@ -87,7 +78,7 @@ func (p *UpgradeProxyProposal) ValidateBasic() error {
 	return nil
 }
 
-func NewCollectTreasuryProposal(title, description string, md CollectTreasuryMetadata, inSafeMode bool) govtypes.Content {
+func NewCollectTreasuryProposal(title, description string, md CollectTreasuryMetadata, inSafeMode bool) govv1beta1.Content {
 	return &CollectTreasuryProposal{
 		Title:       title,
 		Description: description,
@@ -103,7 +94,7 @@ func (*CollectTreasuryProposal) ProposalType() string {
 }
 
 func (p *CollectTreasuryProposal) ValidateBasic() error {
-	if err := govtypes.ValidateAbstract(p); err != nil {
+	if err := govv1beta1.ValidateAbstract(p); err != nil {
 		return err
 	}
 
@@ -115,7 +106,7 @@ func (p *CollectTreasuryProposal) ValidateBasic() error {
 	return nil
 }
 
-func NewSetTreasuryProposal(title, description string, md SetTreasuryMetadata, inSafeMode bool) govtypes.Content {
+func NewSetTreasuryProposal(title, description string, md SetTreasuryMetadata, inSafeMode bool) govv1beta1.Content {
 	return &SetTreasuryProposal{
 		Title:       title,
 		Description: description,
@@ -131,7 +122,7 @@ func (*SetTreasuryProposal) ProposalType() string {
 }
 
 func (p *SetTreasuryProposal) ValidateBasic() error {
-	if err := govtypes.ValidateAbstract(p); err != nil {
+	if err := govv1beta1.ValidateAbstract(p); err != nil {
 		return err
 	}
 
@@ -143,7 +134,7 @@ func (p *SetTreasuryProposal) ValidateBasic() error {
 	return nil
 }
 
-func NewAuthorityTransferProposal(title, description string, md AuthorityTransferMetadata, inSafeMode bool) govtypes.Content {
+func NewAuthorityTransferProposal(title, description string, md AuthorityTransferMetadata, inSafeMode bool) govv1beta1.Content {
 	return &AuthorityTransferProposal{
 		Title:       title,
 		Description: description,
@@ -159,7 +150,7 @@ func (*AuthorityTransferProposal) ProposalType() string {
 }
 
 func (p *AuthorityTransferProposal) ValidateBasic() error {
-	if err := govtypes.ValidateAbstract(p); err != nil {
+	if err := govv1beta1.ValidateAbstract(p); err != nil {
 		return err
 	}
 
@@ -171,7 +162,7 @@ func (p *AuthorityTransferProposal) ValidateBasic() error {
 	return nil
 }
 
-func NewHotPathOpenProposal(title, description string, md HotPathOpenMetadata, inSafeMode bool) govtypes.Content {
+func NewHotPathOpenProposal(title, description string, md HotPathOpenMetadata, inSafeMode bool) govv1beta1.Content {
 	return &HotPathOpenProposal{
 		Title:       title,
 		Description: description,
@@ -187,7 +178,7 @@ func (*HotPathOpenProposal) ProposalType() string {
 }
 
 func (p *HotPathOpenProposal) ValidateBasic() error {
-	if err := govtypes.ValidateAbstract(p); err != nil {
+	if err := govv1beta1.ValidateAbstract(p); err != nil {
 		return err
 	}
 
@@ -197,7 +188,7 @@ func (p *HotPathOpenProposal) ValidateBasic() error {
 	return nil
 }
 
-func NewSetSafeModeProposal(title, description string, md SetSafeModeMetadata, inSafeMode bool) govtypes.Content {
+func NewSetSafeModeProposal(title, description string, md SetSafeModeMetadata, inSafeMode bool) govv1beta1.Content {
 	return &SetSafeModeProposal{
 		Title:       title,
 		Description: description,
@@ -213,7 +204,7 @@ func (*SetSafeModeProposal) ProposalType() string {
 }
 
 func (p *SetSafeModeProposal) ValidateBasic() error {
-	if err := govtypes.ValidateAbstract(p); err != nil {
+	if err := govv1beta1.ValidateAbstract(p); err != nil {
 		return err
 	}
 
@@ -223,7 +214,7 @@ func (p *SetSafeModeProposal) ValidateBasic() error {
 	return nil
 }
 
-func NewTransferGovernanceProposal(title, description string, md TransferGovernanceMetadata) govtypes.Content {
+func NewTransferGovernanceProposal(title, description string, md TransferGovernanceMetadata) govv1beta1.Content {
 	return &TransferGovernanceProposal{
 		Title:       title,
 		Description: description,
@@ -238,7 +229,7 @@ func (*TransferGovernanceProposal) ProposalType() string {
 }
 
 func (p *TransferGovernanceProposal) ValidateBasic() error {
-	if err := govtypes.ValidateAbstract(p); err != nil {
+	if err := govv1beta1.ValidateAbstract(p); err != nil {
 		return err
 	}
 
@@ -255,7 +246,7 @@ func (p *TransferGovernanceProposal) ValidateBasic() error {
 	return nil
 }
 
-func NewOpsProposal(title, description string, md OpsMetadata) govtypes.Content {
+func NewOpsProposal(title, description string, md OpsMetadata) govv1beta1.Content {
 	return &OpsProposal{
 		Title:       title,
 		Description: description,
@@ -270,7 +261,7 @@ func (*OpsProposal) ProposalType() string {
 }
 
 func (p *OpsProposal) ValidateBasic() error {
-	if err := govtypes.ValidateAbstract(p); err != nil {
+	if err := govv1beta1.ValidateAbstract(p); err != nil {
 		return err
 	}
 
diff --git a/x/onboarding/ibc_middleware.go b/x/onboarding/ibc_middleware.go
index b164ddc3..5eac99d3 100644
--- a/x/onboarding/ibc_middleware.go
+++ b/x/onboarding/ibc_middleware.go
@@ -4,9 +4,10 @@ import (
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
 
-	channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
-	porttypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types"
-	"github.com/cosmos/ibc-go/v4/modules/core/exported"
+	clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
+	channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
+	porttypes "github.com/cosmos/ibc-go/v6/modules/core/05-port/types"
+	"github.com/cosmos/ibc-go/v6/modules/core/exported"
 
 	"github.com/AltheaFoundation/althea-L1/x/onboarding/keeper"
 )
@@ -51,9 +52,13 @@ func (im IBCMiddleware) OnRecvPacket(
 func (im IBCMiddleware) SendPacket(
 	ctx sdk.Context,
 	chanCap *capabilitytypes.Capability,
-	packet exported.PacketI,
-) error {
-	return im.keeper.SendPacket(ctx, chanCap, packet)
+	sourcePort string,
+	sourceChannel string,
+	timeoutHeight clienttypes.Height,
+	timeoutTimestamp uint64,
+	data []byte,
+) (sequence uint64, err error) {
+	return im.keeper.SendPacket(ctx, chanCap, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, data)
 }
 
 // WriteAcknowledgement implements the ICS4 Wrapper interface
diff --git a/x/onboarding/ibc_module_test.go b/x/onboarding/ibc_module_test.go
index 23dc20f0..c3ea888f 100644
--- a/x/onboarding/ibc_module_test.go
+++ b/x/onboarding/ibc_module_test.go
@@ -10,13 +10,13 @@ import (
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 
-	transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
-	clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
-	ibcgotesting "github.com/cosmos/ibc-go/v4/testing"
+	transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
+	clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
+	ibcgotesting "github.com/cosmos/ibc-go/v6/testing"
 
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
 
-	"github.com/Canto-Network/Canto/v5/contracts"
+	"github.com/Canto-Network/Canto/v6/contracts"
 
 	althea "github.com/AltheaFoundation/althea-L1/app"
 	ibctesting "github.com/AltheaFoundation/althea-L1/ibcutils/testing"
@@ -98,7 +98,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() {
 
 	// send coins from chainA to chainB
 	// auto swap and auto convert should happen
-	msg := transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coinToSendToB, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0)
+	msg := transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coinToSendToB, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, "")
 	res, err := suite.chainA.SendMsgs(msg)
 	suite.Require().NoError(err) // message committed
 
@@ -132,7 +132,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() {
 	// IBC transfer to blocked address
 	blockedAddr := "althea10d07y265gmmuvt4z0w9aw880jnsr700jwqkt6k"
 	coinToSendToB = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), sdk.DefaultBondDenom)
-	msg = transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coinToSendToB, suite.chainA.SenderAccount.GetAddress().String(), blockedAddr, timeoutHeight, 0)
+	msg = transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coinToSendToB, suite.chainA.SenderAccount.GetAddress().String(), blockedAddr, timeoutHeight, 0, "")
 
 	res, err = suite.chainA.SendMsgs(msg)
 	suite.Require().NoError(err) // message committed
@@ -154,7 +154,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() {
 	balanceVoucherBefore = suite.chainB.App.(*althea.AltheaApp).BankKeeper.GetBalance(suite.chainB.GetContext(), suite.chainB.SenderAccount.GetAddress(), voucherDenomTrace.IBCDenom())
 	balanceErc20Before = erc20Keeper.BalanceOf(suite.chainB.GetContext(), contracts.ERC20MinterBurnerDecimalsContract.ABI, pair.GetERC20Contract(), common.BytesToAddress(suite.chainB.SenderAccount.GetAddress().Bytes()))
 
-	msg = transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coinToSendToB, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0)
+	msg = transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coinToSendToB, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, "")
 
 	res, err = suite.chainA.SendMsgs(msg)
 	suite.Require().NoError(err) // message committed
diff --git a/x/onboarding/keeper/ibc_callbacks.go b/x/onboarding/keeper/ibc_callbacks.go
index 3954aa26..d5807a5d 100644
--- a/x/onboarding/keeper/ibc_callbacks.go
+++ b/x/onboarding/keeper/ibc_callbacks.go
@@ -7,11 +7,11 @@ import (
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
 
-	transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
-	channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
-	"github.com/cosmos/ibc-go/v4/modules/core/exported"
+	transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
+	channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
+	"github.com/cosmos/ibc-go/v6/modules/core/exported"
 
-	erc20types "github.com/Canto-Network/Canto/v5/x/erc20/types"
+	erc20types "github.com/Canto-Network/Canto/v6/x/erc20/types"
 
 	"github.com/AltheaFoundation/althea-L1/ibcutils"
 	"github.com/AltheaFoundation/althea-L1/x/onboarding/types"
diff --git a/x/onboarding/keeper/ibc_callbacks_test.go b/x/onboarding/keeper/ibc_callbacks_test.go
index 07fe3e2d..cdbc168e 100644
--- a/x/onboarding/keeper/ibc_callbacks_test.go
+++ b/x/onboarding/keeper/ibc_callbacks_test.go
@@ -9,19 +9,21 @@ import (
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
 	"github.com/stretchr/testify/mock"
 
+	math "cosmossdk.io/math"
+
 	"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 	distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
 
-	transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
-	clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
-	channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
-	ibcgotesting "github.com/cosmos/ibc-go/v4/testing"
-	ibcmock "github.com/cosmos/ibc-go/v4/testing/mock"
+	transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
+	clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
+	channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
+	ibcgotesting "github.com/cosmos/ibc-go/v6/testing"
+	ibcmock "github.com/cosmos/ibc-go/v6/testing/mock"
 
-	"github.com/Canto-Network/Canto/v5/contracts"
-	erc20types "github.com/Canto-Network/Canto/v5/x/erc20/types"
+	"github.com/Canto-Network/Canto/v6/contracts"
+	erc20types "github.com/Canto-Network/Canto/v6/x/erc20/types"
 
 	"github.com/AltheaFoundation/althea-L1/x/onboarding/keeper"
 	onboardingtest "github.com/AltheaFoundation/althea-L1/x/onboarding/testutil"
@@ -98,7 +100,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
 	// Setup Cosmos <=> althea IBC relayer
 	denom := "uUSDC"
 	ibcDenom := uusdcIbcdenom
-	transferAmount := sdk.NewIntWithDecimal(25, 6)
+	transferAmount := math.NewIntWithDecimal(25, 6)
 	sourceChannel := "channel-0"
 	altheaChannel := sourceChannel
 	path := fmt.Sprintf("%s/%s", transfertypes.PortID, altheaChannel)
@@ -119,7 +121,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
 		{
 			"fail - invalid sender - missing '1' ",
 			func() {
-				transfer := transfertypes.NewFungibleTokenPacketData(denom, "100", "althea", ethsecpAddrAlthea)
+				transfer := transfertypes.NewFungibleTokenPacketData(denom, "100", "althea", ethsecpAddrAlthea, "")
 				bz := transfertypes.ModuleCdc.MustMarshalJSON(&transfer)
 				packet = channeltypes.NewPacket(bz, 100, transfertypes.PortID, sourceChannel, transfertypes.PortID, altheaChannel, timeoutHeight, 0)
 			},
@@ -130,7 +132,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
 		{
 			"fail - invalid sender - invalid bech32",
 			func() {
-				transfer := transfertypes.NewFungibleTokenPacketData(denom, "100", "badba1sv9m0g7ycejwr3s369km58h5qe7xj77hvcxrms", ethsecpAddrAlthea)
+				transfer := transfertypes.NewFungibleTokenPacketData(denom, "100", "badba1sv9m0g7ycejwr3s369km58h5qe7xj77hvcxrms", ethsecpAddrAlthea, "")
 				bz := transfertypes.ModuleCdc.MustMarshalJSON(&transfer)
 				packet = channeltypes.NewPacket(bz, 100, transfertypes.PortID, sourceChannel, transfertypes.PortID, altheaChannel, timeoutHeight, 0)
 			},
@@ -144,7 +146,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
 				distrAcc := suite.app.AccountKeeper.GetModuleAccount(suite.ctx, distrtypes.ModuleName)
 				suite.Require().NotNil(distrAcc)
 				addr := distrAcc.GetAddress().String()
-				transfer := transfertypes.NewFungibleTokenPacketData(denom, "100", addr, addr)
+				transfer := transfertypes.NewFungibleTokenPacketData(denom, "100", addr, addr, "")
 				bz := transfertypes.ModuleCdc.MustMarshalJSON(&transfer)
 				packet = channeltypes.NewPacket(bz, 100, transfertypes.PortID, sourceChannel, transfertypes.PortID, altheaChannel, timeoutHeight, 0)
 			},
@@ -170,7 +172,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
 				denom = "uUSDT"
 				ibcDenom = uusdtIbcdenom
 
-				transfer := transfertypes.NewFungibleTokenPacketData(denom, transferAmount.String(), secpAddrCosmos, ethsecpAddrAlthea)
+				transfer := transfertypes.NewFungibleTokenPacketData(denom, transferAmount.String(), secpAddrCosmos, ethsecpAddrAlthea, "")
 				bz := transfertypes.ModuleCdc.MustMarshalJSON(&transfer)
 				packet = channeltypes.NewPacket(bz, 100, transfertypes.PortID, sourceChannel, transfertypes.PortID, altheaChannel, timeoutHeight, 0)
 			},
@@ -184,8 +186,8 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
 				denom = uusdcCh100DenomTrace.BaseDenom
 				ibcDenom = uusdcCh100IbcDenom
 				altheaChannel = "channel-100"
-				transferAmount = sdk.NewIntWithDecimal(25, 6)
-				transfer := transfertypes.NewFungibleTokenPacketData(denom, transferAmount.String(), secpAddrCosmos, ethsecpAddrAlthea)
+				transferAmount = math.NewIntWithDecimal(25, 6)
+				transfer := transfertypes.NewFungibleTokenPacketData(denom, transferAmount.String(), secpAddrCosmos, ethsecpAddrAlthea, "")
 				bz := transfertypes.ModuleCdc.MustMarshalJSON(&transfer)
 				packet = channeltypes.NewPacket(bz, 100, transfertypes.PortID, sourceChannel, transfertypes.PortID, altheaChannel, timeoutHeight, 0)
 			},
@@ -200,8 +202,8 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
 				ibcDenom = uusdcIbcdenom
 
 				altheaChannel = sourceChannel
-				transferAmount = sdk.NewIntWithDecimal(25, 6)
-				transfer := transfertypes.NewFungibleTokenPacketData(denom, transferAmount.String(), secpAddrCosmos, ethsecpAddrAlthea)
+				transferAmount = math.NewIntWithDecimal(25, 6)
+				transfer := transfertypes.NewFungibleTokenPacketData(denom, transferAmount.String(), secpAddrCosmos, ethsecpAddrAlthea, "")
 				bz := transfertypes.ModuleCdc.MustMarshalJSON(&transfer)
 				packet = channeltypes.NewPacket(bz, 100, transfertypes.PortID, sourceChannel, transfertypes.PortID, altheaChannel, timeoutHeight, 0)
 
@@ -212,7 +214,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
 
 			},
 			true,
-			sdk.NewCoin(uusdcIbcdenom, sdk.NewIntWithDecimal(25, 6)),
+			sdk.NewCoin(uusdcIbcdenom, math.NewIntWithDecimal(25, 6)),
 			sdk.NewInt(0),
 		},
 	}
@@ -220,7 +222,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
 		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
 			suite.SetupTest() // reset
 
-			coins := sdk.NewCoins(sdk.NewCoin("aalthea", sdk.NewIntWithDecimal(10000, 18)), sdk.NewCoin(uusdcIbcdenom, sdk.NewIntWithDecimal(10000, 6)))
+			coins := sdk.NewCoins(sdk.NewCoin("aalthea", math.NewIntWithDecimal(10000, 18)), sdk.NewCoin(uusdcIbcdenom, math.NewIntWithDecimal(10000, 6)))
 			suite.Require().NoError(suite.app.BankKeeper.MintCoins(suite.ctx, evmtypes.ModuleName, coins))
 			err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, evmtypes.ModuleName, secpAddr, coins)
 			suite.Require().NoError(err)
diff --git a/x/onboarding/keeper/keeper.go b/x/onboarding/keeper/keeper.go
index 24d177ff..dc5a9bc2 100644
--- a/x/onboarding/keeper/keeper.go
+++ b/x/onboarding/keeper/keeper.go
@@ -9,15 +9,15 @@ import (
 	capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
 	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
 
-	transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
-	porttypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types"
-	"github.com/cosmos/ibc-go/v4/modules/core/exported"
+	clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
+	porttypes "github.com/cosmos/ibc-go/v6/modules/core/05-port/types"
+	"github.com/cosmos/ibc-go/v6/modules/core/exported"
 
 	"github.com/AltheaFoundation/althea-L1/x/onboarding/types"
 )
 
 // nolint: exhaustruct
-var _ transfertypes.ICS4Wrapper = Keeper{}
+var _ porttypes.ICS4Wrapper = Keeper{}
 
 // Keeper struct
 type Keeper struct {
@@ -74,8 +74,16 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {
 
 // SendPacket implements the ICS4Wrapper interface from the transfer module.
 // It calls the underlying SendPacket function directly to move down the middleware stack.
-func (k Keeper) SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, packet exported.PacketI) error {
-	return k.Ics4Wrapper.SendPacket(ctx, channelCap, packet)
+func (k Keeper) SendPacket(
+	ctx sdk.Context,
+	chanCap *capabilitytypes.Capability,
+	sourcePort string,
+	sourceChannel string,
+	timeoutHeight clienttypes.Height,
+	timeoutTimestamp uint64,
+	data []byte,
+) (sequence uint64, err error) {
+	return k.Ics4Wrapper.SendPacket(ctx, chanCap, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, data)
 }
 
 // WriteAcknowledgement implements the ICS4Wrapper interface from the transfer module.
diff --git a/x/onboarding/keeper/utils_test.go b/x/onboarding/keeper/utils_test.go
index 718c18f7..c572a198 100644
--- a/x/onboarding/keeper/utils_test.go
+++ b/x/onboarding/keeper/utils_test.go
@@ -8,8 +8,8 @@ import (
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
 
-	transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
-	clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
+	transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
+	clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
 
 	"github.com/AltheaFoundation/althea-L1/x/onboarding/types"
 )
diff --git a/x/onboarding/testutil/helpers.go b/x/onboarding/testutil/helpers.go
index c1362504..0cb570a6 100644
--- a/x/onboarding/testutil/helpers.go
+++ b/x/onboarding/testutil/helpers.go
@@ -7,7 +7,7 @@ import (
 	"golang.org/x/exp/slices"
 
 	sdk "github.com/cosmos/cosmos-sdk/types"
-	channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
+	channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
 
 	ibcgotesting "github.com/AltheaFoundation/althea-L1/ibcutils/testing"
 )
diff --git a/x/onboarding/types/interfaces.go b/x/onboarding/types/interfaces.go
index 30bd46a9..4fdeb3ef 100644
--- a/x/onboarding/types/interfaces.go
+++ b/x/onboarding/types/interfaces.go
@@ -12,13 +12,13 @@ import (
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
 
-	transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
-	clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
-	channeltypes "github.com/cosmos/ibc-go/v4/modules/core/04-channel/types"
+	transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
+	clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
+	channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
 
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
 
-	erc20types "github.com/Canto-Network/Canto/v5/x/erc20/types"
+	erc20types "github.com/Canto-Network/Canto/v6/x/erc20/types"
 )
 
 type Erc20Keeper interface {

From ef43ff0f285f7c333ca020c76b5124acb5e24b9f Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Mon, 7 Oct 2024 14:46:17 -0400
Subject: [PATCH 03/63] Configure block max gas in test env

---
 tests/container-scripts/setup-validators.sh | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tests/container-scripts/setup-validators.sh b/tests/container-scripts/setup-validators.sh
index d804e68e..8a8d5e62 100755
--- a/tests/container-scripts/setup-validators.sh
+++ b/tests/container-scripts/setup-validators.sh
@@ -55,6 +55,8 @@ $BIN init $STARTING_VALIDATOR_HOME --chain-id=$CHAIN_ID validator$STARTING_VALID
 ## we could keep a hardcoded genesis file around but that would prevent us from
 ## testing the generated one with the default values provided by the module.
 cp /validator$STARTING_VALIDATOR/config/genesis.json /genesis.json
+# Set the default max gas per block to a positive value (100 million)
+jq '.consensus_params.block.max_gas = "100000000"' /genesis.json > tmp_genesis.json && mv tmp_genesis.json /genesis.json
 # add in denom metadata for both native tokens
 jq '.app_state.bank.denom_metadata += [{"name": "althea", "symbol": "althea", "base": "aalthea", display: "althea", "description": "The native staking token of althea-L1 (18 decimals)", "denom_units": [{"denom": "aalthea", "exponent": 0, "aliases": ["attoalthea", "althea-wei"]}, {"denom": "nalthea", "exponent": 9, "aliases": ["nanoalthea", "althea-gwei"]}, {"denom": "althea", "exponent": 18}]}]' /genesis.json > tmp_genesis.json && mv tmp_genesis.json /genesis.json
 jq '.app_state.bank.denom_metadata += [{"name": "FOO", "symbol": "FOO", "base": "ufootoken", display: "footoken", "description": "A non-staking native test token (6 decimals)", "denom_units": [{"denom": "ufootoken", "exponent": 0}, {"denom": "footoken", "exponent": 6}]}]' /genesis.json > tmp_genesis.json && mv tmp_genesis.json /genesis.json

From f764a697d76056986955264f87ad53a4b2a160e9 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Thu, 3 Oct 2024 12:06:28 -0400
Subject: [PATCH 04/63] Configure althea root cmd

---
 cmd/althea/root.go | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/cmd/althea/root.go b/cmd/althea/root.go
index 8f4b1641..69e9d7e6 100644
--- a/cmd/althea/root.go
+++ b/cmd/althea/root.go
@@ -11,6 +11,11 @@ import (
 	"os"
 	"path/filepath"
 
+	"github.com/cosmos/go-bip39"
+	"github.com/pkg/errors"
+	"github.com/spf13/cast"
+	"github.com/spf13/cobra"
+
 	cfg "github.com/tendermint/tendermint/config"
 	tmcli "github.com/tendermint/tendermint/libs/cli"
 	"github.com/tendermint/tendermint/libs/log"
@@ -26,23 +31,20 @@ import (
 	"github.com/cosmos/cosmos-sdk/client/rpc"
 	"github.com/cosmos/cosmos-sdk/server"
 	servertypes "github.com/cosmos/cosmos-sdk/server/types"
+	simappparams "github.com/cosmos/cosmos-sdk/simapp/params"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	"github.com/cosmos/cosmos-sdk/types/module"
 	authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
 	"github.com/cosmos/cosmos-sdk/x/auth/types"
-	vestingcli "github.com/cosmos/cosmos-sdk/x/auth/vesting/client/cli"
 	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 	"github.com/cosmos/cosmos-sdk/x/crisis"
 	"github.com/cosmos/cosmos-sdk/x/genutil"
 	genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
-	"github.com/cosmos/go-bip39"
-	"github.com/pkg/errors"
-	"github.com/spf13/cast"
-	"github.com/spf13/cobra"
 
 	// EVM
 
 	ethermintclient "github.com/evmos/ethermint/client"
+	"github.com/evmos/ethermint/ethereum/eip712"
 	ethermintserver "github.com/evmos/ethermint/server"
 	ethermintserverconfig "github.com/evmos/ethermint/server/config"
 	ethermintserverflags "github.com/evmos/ethermint/server/flags"
@@ -108,6 +110,8 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) {
 		WithKeyringOptions(keyring.Option()).
 		WithViper(EnvPrefix)
 
+	eip712.SetEncodingConfig(simappparams.EncodingConfig(encodingConfig))
+
 	rootCmd := &cobra.Command{
 		Use:   althea.Name,
 		Short: "Althea L1: Submit transactions or run a validator",
@@ -372,8 +376,6 @@ func txCommand() *cobra.Command {
 		authcmd.GetEncodeCommand(),
 		authcmd.GetDecodeCommand(),
 		authcmd.GetAuxToFeeCommand(),
-		flags.LineBreak,
-		vestingcli.GetTxCmd(),
 	)
 
 	althea.ModuleBasics.AddTxCommands(cmd)

From 1b91186b30f6b0a43afa4ae3670e8e35afba4e5a Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Mon, 7 Oct 2024 14:16:04 -0400
Subject: [PATCH 05/63] Copy ERC20 module implementation from Canto repo

---
 app/ante/charge_gasfree_fees_test.go      |    2 +-
 app/ante/cosmos_handler_test.go           |    4 +-
 app/ante/eth_set_pubkey_test.go           |   13 -
 app/ante/utils_test.go                    |   20 +-
 app/app.go                                |   31 +-
 app/genesis.go                            |   14 +-
 app/sigverify.go                          |   80 +-
 app/testutil.go                           |  175 ++-
 go.mod                                    |   15 +-
 go.sum                                    |   10 +-
 testutil/fund.go                          |   29 +
 testutil/network/doc.go                   |   65 +
 testutil/network/network.go               |  694 ++++++++++
 testutil/network/network_test.go          |   64 +
 testutil/network/util.go                  |  256 ++++
 x/erc20/client/cli/query.go               |  128 ++
 x/erc20/client/cli/tx.go                  |  363 ++++++
 x/erc20/client/cli/utils.go               |   25 +
 x/erc20/client/proposal_handler.go        |   13 +
 x/erc20/genesis.go                        |   40 +
 x/erc20/genesis_test.go                   |  160 +++
 x/erc20/handler.go                        |   27 +
 x/erc20/keeper/evm.go                     |  241 ++++
 x/erc20/keeper/evm_hooks.go               |  157 +++
 x/erc20/keeper/evm_hooks_test.go          |  431 +++++++
 x/erc20/keeper/evm_test.go                |  386 ++++++
 x/erc20/keeper/grpc_query.go              |   85 ++
 x/erc20/keeper/grpc_query_test.go         |  170 +++
 x/erc20/keeper/integration_test.go        |  302 +++++
 x/erc20/keeper/keeper.go                  |   53 +
 x/erc20/keeper/keeper_test.go             |  518 ++++++++
 x/erc20/keeper/migrations.go              |   27 +
 x/erc20/keeper/mint.go                    |   66 +
 x/erc20/keeper/mint_test.go               |  112 ++
 x/erc20/keeper/msg_server.go              |  526 ++++++++
 x/erc20/keeper/msg_server_test.go         | 1344 ++++++++++++++++++++
 x/erc20/keeper/params.go                  |   18 +
 x/erc20/keeper/params_test.go             |   12 +
 x/erc20/keeper/proposals.go               |  216 ++++
 x/erc20/keeper/proposals_test.go          |  473 +++++++
 x/erc20/keeper/token_pairs.go             |  129 ++
 x/erc20/keeper/token_pairs_test.go        |  234 ++++
 x/erc20/migrations/v2/migration.go        |   21 +
 x/erc20/migrations/v2/migration_test.go   |   54 +
 x/erc20/module.go                         |  181 +++
 x/erc20/proposal_handler.go               |   77 ++
 x/erc20/types/codec.go                    |   60 +
 x/erc20/types/erc20.pb.go                 | 1401 +++++++++++++++++++++
 x/erc20/types/errors.go                   |   21 +
 x/erc20/types/events.go                   |   33 +
 x/erc20/types/evm.go                      |   33 +
 x/erc20/types/evm_test.go                 |   13 +
 x/erc20/types/genesis.go                  |   44 +
 x/erc20/types/genesis.pb.go               |  598 +++++++++
 x/erc20/types/genesis_test.go             |  148 +++
 x/erc20/types/interfaces.go               |   43 +
 x/erc20/types/keys.go                     |   39 +
 x/erc20/types/msg.go                      |  111 ++
 x/erc20/types/msg_test.go                 |  247 ++++
 x/erc20/types/params.go                   |   57 +
 x/erc20/types/params_test.go              |   55 +
 x/erc20/types/proposal.go                 |  163 +++
 x/erc20/types/proposal_test.go            |  279 ++++
 x/erc20/types/query.pb.go                 | 1399 ++++++++++++++++++++
 x/erc20/types/query.pb.gw.go              |  337 +++++
 x/erc20/types/token_pair.go               |   54 +
 x/erc20/types/token_pair_test.go          |  159 +++
 x/erc20/types/tx.pb.go                    | 1140 +++++++++++++++++
 x/erc20/types/tx.pb.gw.go                 |  254 ++++
 x/erc20/types/utils.go                    |   83 ++
 x/erc20/types/utils_test.go               |  249 ++++
 x/gasfree/module_test.go                  |  121 +-
 x/lockup/keeper/test_common.go            |    9 +-
 x/microtx/keeper/keeper.go                |    2 +-
 x/microtx/keeper/liquid_account.go        |    2 +-
 x/onboarding/genesis_test.go              |    8 +-
 x/onboarding/ibc_module_test.go           |    2 +-
 x/onboarding/keeper/ibc_callbacks.go      |    3 +-
 x/onboarding/keeper/ibc_callbacks_test.go |    4 +-
 x/onboarding/keeper/keeper_test.go        |   42 +-
 x/onboarding/types/interfaces.go          |    2 +-
 81 files changed, 15077 insertions(+), 199 deletions(-)
 create mode 100644 testutil/fund.go
 create mode 100644 testutil/network/doc.go
 create mode 100644 testutil/network/network.go
 create mode 100644 testutil/network/network_test.go
 create mode 100644 testutil/network/util.go
 create mode 100644 x/erc20/client/cli/query.go
 create mode 100644 x/erc20/client/cli/tx.go
 create mode 100644 x/erc20/client/cli/utils.go
 create mode 100644 x/erc20/client/proposal_handler.go
 create mode 100644 x/erc20/genesis.go
 create mode 100644 x/erc20/genesis_test.go
 create mode 100644 x/erc20/handler.go
 create mode 100644 x/erc20/keeper/evm.go
 create mode 100644 x/erc20/keeper/evm_hooks.go
 create mode 100644 x/erc20/keeper/evm_hooks_test.go
 create mode 100644 x/erc20/keeper/evm_test.go
 create mode 100644 x/erc20/keeper/grpc_query.go
 create mode 100644 x/erc20/keeper/grpc_query_test.go
 create mode 100644 x/erc20/keeper/integration_test.go
 create mode 100644 x/erc20/keeper/keeper.go
 create mode 100644 x/erc20/keeper/keeper_test.go
 create mode 100644 x/erc20/keeper/migrations.go
 create mode 100644 x/erc20/keeper/mint.go
 create mode 100644 x/erc20/keeper/mint_test.go
 create mode 100644 x/erc20/keeper/msg_server.go
 create mode 100644 x/erc20/keeper/msg_server_test.go
 create mode 100644 x/erc20/keeper/params.go
 create mode 100644 x/erc20/keeper/params_test.go
 create mode 100644 x/erc20/keeper/proposals.go
 create mode 100644 x/erc20/keeper/proposals_test.go
 create mode 100644 x/erc20/keeper/token_pairs.go
 create mode 100644 x/erc20/keeper/token_pairs_test.go
 create mode 100644 x/erc20/migrations/v2/migration.go
 create mode 100644 x/erc20/migrations/v2/migration_test.go
 create mode 100644 x/erc20/module.go
 create mode 100644 x/erc20/proposal_handler.go
 create mode 100644 x/erc20/types/codec.go
 create mode 100644 x/erc20/types/erc20.pb.go
 create mode 100644 x/erc20/types/errors.go
 create mode 100644 x/erc20/types/events.go
 create mode 100644 x/erc20/types/evm.go
 create mode 100644 x/erc20/types/evm_test.go
 create mode 100644 x/erc20/types/genesis.go
 create mode 100644 x/erc20/types/genesis.pb.go
 create mode 100644 x/erc20/types/genesis_test.go
 create mode 100644 x/erc20/types/interfaces.go
 create mode 100644 x/erc20/types/keys.go
 create mode 100644 x/erc20/types/msg.go
 create mode 100644 x/erc20/types/msg_test.go
 create mode 100644 x/erc20/types/params.go
 create mode 100644 x/erc20/types/params_test.go
 create mode 100644 x/erc20/types/proposal.go
 create mode 100644 x/erc20/types/proposal_test.go
 create mode 100644 x/erc20/types/query.pb.go
 create mode 100644 x/erc20/types/query.pb.gw.go
 create mode 100644 x/erc20/types/token_pair.go
 create mode 100644 x/erc20/types/token_pair_test.go
 create mode 100644 x/erc20/types/tx.pb.go
 create mode 100644 x/erc20/types/tx.pb.gw.go
 create mode 100644 x/erc20/types/utils.go
 create mode 100644 x/erc20/types/utils_test.go

diff --git a/app/ante/charge_gasfree_fees_test.go b/app/ante/charge_gasfree_fees_test.go
index f27965e3..6f502ab4 100644
--- a/app/ante/charge_gasfree_fees_test.go
+++ b/app/ante/charge_gasfree_fees_test.go
@@ -192,6 +192,6 @@ func (suite *AnteTestSuite) TestChargeGasfreeFeesDecorator() {
 	// nolint: exhaustruct
 	suite.app.GasfreeKeeper.SetGasFreeMessageTypes(bothGasfreeCtx, []string{sdk.MsgTypeURL(&microtxtypes.MsgMicrotx{}), sdk.MsgTypeURL(&banktypes.MsgSend{})})
 
-	// Expect the error from the mempool fee decorator to contain something like "insufficient fees; got: x required: provided fee < minimum global feey"
+	// Expect the error from the mempool fee decorator to contain something like "insufficient fees; got: x required: provided fee < minimum global fee y"
 	suite.Require().NoError(runGasfreeTests(suite, gasfreeMicrotxCtx, gasfreeSendCtx, noGasfreeCtx, bothGasfreeCtx, msgMicrotxTx, msgSendTx, bothTx, addr, testDenom))
 }
diff --git a/app/ante/cosmos_handler_test.go b/app/ante/cosmos_handler_test.go
index abf8e190..5edaab43 100644
--- a/app/ante/cosmos_handler_test.go
+++ b/app/ante/cosmos_handler_test.go
@@ -115,8 +115,8 @@ func (suite *AnteTestSuite) TestCosmosAnteHandlerMempoolFeeBypass() {
 	// nolint: exhaustruct
 	suite.app.GasfreeKeeper.SetGasFreeMessageTypes(bothGasfreeCtx, []string{sdk.MsgTypeURL(&microtxtypes.MsgMicrotx{}), sdk.MsgTypeURL(&banktypes.MsgSend{})})
 
-	// Expect the error from the mempool fee decorator to contain something like "insufficient fees; got: x required: provided fee < minimum global feey"
-	suite.Require().NoError(runBypassTest(suite, "insufficient fees; got:", gasfreeMicrotxCtx, gasfreeSendCtx, noGasfreeCtx, bothGasfreeCtx, msgMicrotxTx, msgSendTx, bothTx))
+	// Expect the error from the mempool fee decorator to contain something like "insufficient fees; got: x required: provided fee < minimum global fee"
+	suite.Require().NoError(runBypassTest(suite, "provided fee < minimum global fee", gasfreeMicrotxCtx, gasfreeSendCtx, noGasfreeCtx, bothGasfreeCtx, msgMicrotxTx, msgSendTx, bothTx))
 }
 
 // Checks that the MinGasPrices antedecorator is bypassed for applicable txs
diff --git a/app/ante/eth_set_pubkey_test.go b/app/ante/eth_set_pubkey_test.go
index 412b4b2b..189b13c3 100644
--- a/app/ante/eth_set_pubkey_test.go
+++ b/app/ante/eth_set_pubkey_test.go
@@ -153,19 +153,6 @@ func (suite *AnteTestSuite) TestEthSetPubkeyHandler() {
 				suite.Require().Error(err)
 			}
 			suite.Require().True(tc.correctPubKey(pubKey), "PubKey type incorrect after AnteHandler")
-
-			// Now check the old antehandler against the same Tx, see if it would create a pubkey
-			ctx, _ = suite.ctx.CacheContext()
-			suite.testCaseSetup(ctx, addr, tc.startBaseAcc)
-			// nolint: errcheck
-			_, _ = suite.oldAnteHandler(ctx, tx, false)
-			acc = suite.app.AccountKeeper.GetAccount(ctx, addr)
-			pubKey = acc.GetPubKey()
-			if tc.expOldPubKey {
-				suite.Require().True(pubKey != nil && tc.correctPubKey(pubKey), "Old ante handler should have set a pubkey")
-			} else {
-				suite.Require().True(pubKey == nil, "Old ante handler should have set a pubkey")
-			}
 		})
 	}
 }
diff --git a/app/ante/utils_test.go b/app/ante/utils_test.go
index 34ab2289..be0eab38 100644
--- a/app/ante/utils_test.go
+++ b/app/ante/utils_test.go
@@ -40,8 +40,6 @@ import (
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
 	feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"
 
-	cantoante "github.com/Canto-Network/Canto/v6/app/ante"
-
 	althea "github.com/AltheaFoundation/althea-L1/app"
 	ante "github.com/AltheaFoundation/althea-L1/app/ante"
 	altheaconfig "github.com/AltheaFoundation/althea-L1/config"
@@ -55,7 +53,6 @@ type AnteTestSuite struct {
 	app             *althea.AltheaApp
 	clientCtx       client.Context
 	anteHandler     sdk.AnteHandler
-	oldAnteHandler  sdk.AnteHandler
 	ethSigner       ethtypes.Signer
 	enableFeemarket bool
 	evmParamsOption func(*evmtypes.Params)
@@ -73,12 +70,13 @@ func (suite *AnteTestSuite) SetupTest() {
 	cfg := sdk.GetConfig()
 	cfg.SetBech32PrefixForAccount("althea", "altheapub")
 
-	suite.app = althea.Setup(checkTx, func(app *althea.AltheaApp, genesis althea.GenesisState) althea.GenesisState {
+	suite.app = althea.NewSetup(checkTx, func(app *althea.AltheaApp, genesis simapp.GenesisState) simapp.GenesisState {
 		if suite.enableFeemarket {
 			// setup feemarketGenesis params
 			feemarketGenesis := feemarkettypes.DefaultGenesisState()
 			feemarketGenesis.Params.EnableHeight = 1
 			feemarketGenesis.Params.NoBaseFee = false
+			feemarketGenesis.BlockGas = 30000000
 			// Verify feeMarket genesis
 			err := feemarketGenesis.Validate()
 			suite.Require().NoError(err)
@@ -117,20 +115,6 @@ func (suite *AnteTestSuite) SetupTest() {
 
 	suite.anteHandler = anteHandler
 
-	// Also make a copy of the old Canto antehandler we were using to ensure that our changes fix the problem
-	// nolint: exhaustruct
-	oldAnteHandler := cantoante.NewAnteHandler(cantoante.HandlerOptions{
-		AccountKeeper:   *suite.app.AccountKeeper,
-		BankKeeper:      suite.app.BankKeeper,
-		EvmKeeper:       suite.app.EvmKeeper,
-		FeegrantKeeper:  nil,
-		IBCKeeper:       suite.app.IbcKeeper,
-		FeeMarketKeeper: *suite.app.FeemarketKeeper,
-		SignModeHandler: encodingConfig.TxConfig.SignModeHandler(),
-		SigGasConsumer:  althea.SigVerificationGasConsumer,
-	})
-	suite.oldAnteHandler = oldAnteHandler
-
 	// Defines the siging method (e.g. homestead, london, etc)
 	suite.ethSigner = ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
 }
diff --git a/app/app.go b/app/app.go
index 212951e9..ade22785 100644
--- a/app/app.go
+++ b/app/app.go
@@ -112,11 +112,6 @@ import (
 
 	// EVM + ERC20
 
-	"github.com/Canto-Network/Canto/v6/x/erc20"
-	erc20client "github.com/Canto-Network/Canto/v6/x/erc20/client"
-	erc20keeper "github.com/Canto-Network/Canto/v6/x/erc20/keeper"
-	erc20types "github.com/Canto-Network/Canto/v6/x/erc20/types"
-
 	ethante "github.com/evmos/ethermint/app/ante"
 	"github.com/evmos/ethermint/ethereum/eip712"
 	ethermintsrvflags "github.com/evmos/ethermint/server/flags"
@@ -137,6 +132,10 @@ import (
 	"github.com/AltheaFoundation/althea-L1/app/upgrades"
 	"github.com/AltheaFoundation/althea-L1/app/upgrades/neutrino"
 	altheacfg "github.com/AltheaFoundation/althea-L1/config"
+	"github.com/AltheaFoundation/althea-L1/x/erc20"
+	erc20client "github.com/AltheaFoundation/althea-L1/x/erc20/client"
+	erc20keeper "github.com/AltheaFoundation/althea-L1/x/erc20/keeper"
+	erc20types "github.com/AltheaFoundation/althea-L1/x/erc20/types"
 	"github.com/AltheaFoundation/althea-L1/x/gasfree"
 	gasfreekeeper "github.com/AltheaFoundation/althea-L1/x/gasfree/keeper"
 	gasfreetypes "github.com/AltheaFoundation/althea-L1/x/gasfree/types"
@@ -578,6 +577,16 @@ func NewAltheaApp(
 	)
 	app.SlashingKeeper = &slashingKeeper
 
+	// Connect the inter-module staking hooks together, these are the only modules allowed to interact with how staking
+	// works, including inflationary staking rewards and punishing bad actors (excluding genutil which works at genesis to
+	// seed the set of validators from the genesis txs set)
+	stakingKeeper.SetHooks(
+		stakingtypes.NewMultiStakingHooks(
+			distrKeeper.Hooks(),
+			slashingKeeper.Hooks(),
+		),
+	)
+
 	upgradeKeeper := upgradekeeper.NewKeeper(
 		skipUpgradeHeights,
 		keys[upgradetypes.StoreKey],
@@ -682,16 +691,6 @@ func NewAltheaApp(
 	ibcRouter.AddRoute(icahosttypes.SubModuleName, icaHostStack)
 	ibcKeeper.SetRouter(ibcRouter)
 
-	// Connect the inter-module staking hooks together, these are the only modules allowed to interact with how staking
-	// works, including inflationary staking rewards and punishing bad actors (excluding genutil which works at genesis to
-	// seed the set of validators from the genesis txs set)
-	stakingKeeper.SetHooks(
-		stakingtypes.NewMultiStakingHooks(
-			distrKeeper.Hooks(),
-			slashingKeeper.Hooks(),
-		),
-	)
-
 	mintKeeper := mintkeeper.NewKeeper(
 		appCodec,
 		keys[minttypes.StoreKey],
@@ -1065,7 +1064,7 @@ func (app *AltheaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci
 // InitChainer deserializes the given chain genesis state, registers in-place upgrade migrations, and delegates
 // the ABCI InitGenesis execution to the ModuleManager
 func (app *AltheaApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
-	var genesisState GenesisState
+	var genesisState simapp.GenesisState
 	if err := tmjson.Unmarshal(req.AppStateBytes, &genesisState); err != nil {
 		panic(err)
 	}
diff --git a/app/genesis.go b/app/genesis.go
index 284efda6..d1d293c0 100644
--- a/app/genesis.go
+++ b/app/genesis.go
@@ -1,20 +1,10 @@
 package althea
 
 import (
-	"encoding/json"
+	"github.com/cosmos/cosmos-sdk/simapp"
 )
 
-// The genesis state of the blockchain is represented here as a map of raw json
-// messages key'd by a 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
-
-// NewDefaultGenesisState generates the default state for the application.
-func NewDefaultGenesisState() GenesisState {
+func NewDefaultGenesisState() simapp.GenesisState {
 	encCfg := MakeEncodingConfig()
 	return ModuleBasics.DefaultGenesis(encCfg.Codec)
 }
diff --git a/app/sigverify.go b/app/sigverify.go
index 130703dd..cdf75cce 100644
--- a/app/sigverify.go
+++ b/app/sigverify.go
@@ -1,12 +1,19 @@
 package althea
 
 import (
+	"fmt"
+
+	errorsmod "cosmossdk.io/errors"
+
+	"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
 	"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
+	"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
 	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	"github.com/cosmos/cosmos-sdk/types/tx/signing"
+	authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
 	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
-
-	canto "github.com/Canto-Network/Canto/v6/app"
+	"github.com/evmos/ethermint/crypto/ethsecp256k1"
 )
 
 const (
@@ -38,6 +45,73 @@ func SigVerificationGasConsumer(
 		return nil
 
 	default:
-		return canto.SigVerificationGasConsumer(meter, sig, params)
+		return CantoSigVerificationGasConsumer(meter, sig, params)
+	}
+}
+
+var _ authante.SignatureVerificationGasConsumer = CantoSigVerificationGasConsumer
+
+// SigVerificationGasConsumer is the canto implementation of SignatureVerificationGasConsumer. It consumes gas
+// for signature verification based upon the public key type. The cost is fetched from the given params and is matched
+// by the concrete type.
+// The types of keys supported are:
+//
+// - ethsecp256k1 (Ethereum keys)
+//
+// - ed25519 (Validators)
+//
+// - multisig (Cosmos SDK multisigs)
+func CantoSigVerificationGasConsumer(
+	meter sdk.GasMeter, sig signing.SignatureV2, params authtypes.Params,
+) error {
+	pubkey := sig.PubKey
+	switch pubkey := pubkey.(type) {
+
+	case *ethsecp256k1.PubKey:
+		// Ethereum keys
+		meter.ConsumeGas(secp256k1VerifyCost, "ante verify: eth_secp256k1")
+		return nil
+	case *ed25519.PubKey:
+		// Validator keys
+		meter.ConsumeGas(params.SigVerifyCostED25519, "ante verify: ed25519")
+		return errorsmod.Wrap(sdkerrors.ErrInvalidPubKey, "ED25519 public keys are unsupported")
+
+	case multisig.PubKey:
+		// Multisig keys
+		multisignature, ok := sig.Data.(*signing.MultiSignatureData)
+		if !ok {
+			return fmt.Errorf("expected %T, got, %T", &signing.MultiSignatureData{}, sig.Data)
+		}
+		return ConsumeMultisignatureVerificationGas(meter, multisignature, pubkey, params, sig.Sequence)
+
+	default:
+		return errorsmod.Wrapf(sdkerrors.ErrInvalidPubKey, "unrecognized/unsupported public key type: %T", pubkey)
 	}
 }
+
+// ConsumeMultisignatureVerificationGas consumes gas from a GasMeter for verifying a multisig pubkey signature
+func ConsumeMultisignatureVerificationGas(
+	meter sdk.GasMeter, sig *signing.MultiSignatureData, pubkey multisig.PubKey,
+	params authtypes.Params, accSeq uint64,
+) error {
+	size := sig.BitArray.Count()
+	sigIndex := 0
+
+	for i := 0; i < size; i++ {
+		if !sig.BitArray.GetIndex(i) {
+			continue
+		}
+		sigV2 := signing.SignatureV2{
+			PubKey:   pubkey.GetPubKeys()[i],
+			Data:     sig.Signatures[sigIndex],
+			Sequence: accSeq,
+		}
+		err := SigVerificationGasConsumer(meter, sigV2, params)
+		if err != nil {
+			return err
+		}
+		sigIndex++
+	}
+
+	return nil
+}
diff --git a/app/testutil.go b/app/testutil.go
index b387c6e9..a3a7f875 100644
--- a/app/testutil.go
+++ b/app/testutil.go
@@ -10,11 +10,26 @@ import (
 	tmtypes "github.com/tendermint/tendermint/types"
 	dbm "github.com/tendermint/tm-db"
 
+	"github.com/cosmos/cosmos-sdk/codec"
+	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
+	cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
+	"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
+	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
 	"github.com/cosmos/cosmos-sdk/simapp"
+	"github.com/cosmos/cosmos-sdk/testutil/mock"
+	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"
+	minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
+	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
+
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+
+	altheacfg "github.com/AltheaFoundation/althea-L1/config"
 )
 
 // Setup initializes a new Althea app. A Nop logger is set in AltheaApp.
-func Setup(isCheckTx bool, patchGenesis func(*AltheaApp, GenesisState) GenesisState) *AltheaApp {
+func Setup(isCheckTx bool, patchGenesis func(*AltheaApp, simapp.GenesisState) simapp.GenesisState) *AltheaApp {
 	db := dbm.NewMemDB()
 	app := NewAltheaApp(tmlog.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, 5, MakeEncodingConfig(), simapp.EmptyAppOptions{})
 	if !isCheckTx {
@@ -45,7 +60,7 @@ func Setup(isCheckTx bool, patchGenesis func(*AltheaApp, GenesisState) GenesisSt
 }
 
 // DefaultConsensusParams defines the default Tendermint consensus params used in
-// EthermintApp testing.
+// AltheaApp testing.
 // nolint: exhaustruct
 var DefaultConsensusParams = &abci.ConsensusParams{
 	Block: &abci.BlockParams{
@@ -63,3 +78,159 @@ var DefaultConsensusParams = &abci.ConsensusParams{
 		},
 	},
 }
+
+var ValidatorPrivKey cryptotypes.PrivKey
+var ValidatorPubKey cryptotypes.PubKey
+
+// Setup initializes a new AltheaApp. A Nop logger is set in AltheaApp.
+func NewSetup(isCheckTx bool, patchGenesis func(*AltheaApp, simapp.GenesisState) simapp.GenesisState) *AltheaApp {
+	return SetupWithDB(isCheckTx, patchGenesis, dbm.NewMemDB())
+}
+
+// SetupWithDB initializes a new AltheaApp. A Nop logger is set in AltheaApp.
+func SetupWithDB(isCheckTx bool, patchGenesis func(*AltheaApp, simapp.GenesisState) simapp.GenesisState, db dbm.DB) *AltheaApp {
+	app := NewAltheaApp(
+		tmlog.NewNopLogger(),
+		db,
+		nil,
+		true,
+		map[int64]bool{},
+		DefaultNodeHome,
+		5,
+		MakeEncodingConfig(),
+		simapp.EmptyAppOptions{},
+	)
+	if !isCheckTx {
+		// init chain must be called to stop deliverState from being nil
+		genesisState := NewTestGenesisState(app.AppCodec())
+		if patchGenesis != nil {
+			genesisState = patchGenesis(app, genesisState)
+		}
+
+		stateBytes, err := json.MarshalIndent(genesisState, "", " ")
+		if err != nil {
+			panic(err)
+		}
+
+		// Initialize the chain
+		app.InitChain(
+			abci.RequestInitChain{
+				ChainId:         "althea_7357-1",
+				Validators:      []abci.ValidatorUpdate{},
+				ConsensusParams: DefaultConsensusParams,
+				AppStateBytes:   stateBytes,
+			},
+		)
+	}
+
+	return app
+}
+
+// NewTestGenesisState generate genesis state with single validator
+func NewTestGenesisState(codec codec.Codec) simapp.GenesisState {
+	privVal := mock.NewPV()
+	ValidatorPrivKey = privVal.PrivKey
+	pubKey, err := privVal.GetPubKey()
+	ValidatorPubKey = ValidatorPrivKey.PubKey()
+	if err != nil {
+		panic(err)
+	}
+	// create validator set with single validator
+	validator := tmtypes.NewValidator(pubKey, 1)
+	valSet := tmtypes.NewValidatorSet([]*tmtypes.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(altheacfg.BaseDenom, sdk.NewInt(100000000000000))),
+	}
+
+	genesisState := NewDefaultGenesisState()
+	return genesisStateWithValSet(codec, genesisState, valSet, []authtypes.GenesisAccount{acc}, balance)
+}
+
+func genesisStateWithValSet(codec codec.Codec, genesisState simapp.GenesisState,
+	valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount,
+	balances ...banktypes.Balance,
+) simapp.GenesisState {
+	mintGenesis := minttypes.DefaultGenesisState()
+	mintGenesis.Params.MintDenom = altheacfg.BaseDenom
+	genesisState[minttypes.ModuleName] = codec.MustMarshalJSON(mintGenesis)
+
+	evmGenesis := evmtypes.DefaultGenesisState()
+	evmGenesis.Params.EvmDenom = altheacfg.BaseDenom
+	genesisState[evmtypes.ModuleName] = codec.MustMarshalJSON(evmGenesis)
+
+	// set genesis accounts
+	authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs)
+	genAccs, err := authtypes.UnpackAccounts(authGenesis.Accounts)
+	if err != nil {
+		panic(err)
+	}
+	genAccs = append(genAccs, authtypes.NewEmptyModuleAccount(authtypes.FeeCollectorName))
+	authGenesis.Accounts, err = authtypes.PackAccounts(genAccs)
+	if err != nil {
+		panic(err)
+	}
+	genesisState[authtypes.ModuleName] = codec.MustMarshalJSON(authGenesis)
+
+	validators := make([]stakingtypes.Validator, 0, len(valSet.Validators))
+	delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators))
+
+	bondAmt := sdk.DefaultPowerReduction
+
+	for _, val := range valSet.Validators {
+		pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey)
+		if err != nil {
+			panic(err)
+		}
+		pkAny, err := codectypes.NewAnyWithValue(pk)
+		if err != nil {
+			panic(err)
+		}
+		validator := stakingtypes.Validator{
+			OperatorAddress:   sdk.ValAddress(val.Address).String(),
+			ConsensusPubkey:   pkAny,
+			Jailed:            false,
+			Status:            stakingtypes.Bonded,
+			Tokens:            bondAmt,
+			DelegatorShares:   sdk.OneDec(),
+			Description:       stakingtypes.Description{},
+			UnbondingHeight:   int64(0),
+			UnbondingTime:     time.Unix(0, 0).UTC(),
+			Commission:        stakingtypes.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
+			MinSelfDelegation: sdk.ZeroInt(),
+		}
+		validators = append(validators, validator)
+		delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec()))
+	}
+	// set validators and delegations
+	stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations)
+	stakingGenesis.Params.BondDenom = altheacfg.BaseDenom
+	genesisState[stakingtypes.ModuleName] = codec.MustMarshalJSON(stakingGenesis)
+
+	totalSupply := sdk.NewCoins()
+	for _, b := range balances {
+		// add genesis acc tokens to total supply
+		totalSupply = totalSupply.Add(b.Coins...)
+	}
+
+	for range delegations {
+		// add delegated tokens to total supply
+		totalSupply = totalSupply.Add(sdk.NewCoin(altheacfg.BaseDenom, bondAmt))
+	}
+
+	// add bonded amount to bonded pool module account
+	balances = append(balances, banktypes.Balance{
+		Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(),
+		Coins:   sdk.Coins{sdk.NewCoin(altheacfg.BaseDenom, bondAmt)},
+	})
+
+	// update total supply
+	bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{})
+	genesisState[banktypes.ModuleName] = codec.MustMarshalJSON(bankGenesis)
+
+	return genesisState
+}
diff --git a/go.mod b/go.mod
index 658cbde3..99a006bb 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,6 @@ go 1.22.0
 toolchain go1.22.7
 
 require (
-	github.com/Canto-Network/Canto/v6 v6.0.1
 	github.com/cosmos/cosmos-sdk v0.46.17
 	github.com/cosmos/ibc-go/v6 v6.3.1
 	github.com/evmos/ethermint v0.22.3
@@ -45,6 +44,8 @@ require (
 	github.com/golang/protobuf v1.5.4
 	github.com/gorilla/mux v1.8.0
 	github.com/grpc-ecosystem/grpc-gateway v1.16.0
+	github.com/onsi/ginkgo/v2 v2.9.2
+	github.com/onsi/gomega v1.27.6
 	github.com/pkg/errors v0.9.1
 	github.com/rakyll/statik v0.1.7
 	github.com/spf13/cast v1.7.0
@@ -60,17 +61,22 @@ require (
 	cloud.google.com/go/compute/metadata v0.3.0 // indirect
 	cloud.google.com/go/iam v1.1.6 // indirect
 	cloud.google.com/go/storage v1.38.0 // indirect
+	github.com/allegro/bigcache v1.2.1 // indirect
 	github.com/aws/aws-sdk-go v1.44.122 // indirect
 	github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
 	github.com/bufbuild/protocompile v0.14.1 // indirect
 	github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
 	github.com/cockroachdb/apd/v2 v2.0.2 // indirect
 	github.com/cosmos/gogoproto v1.4.7 // indirect
+	github.com/gin-gonic/gin v1.8.1 // indirect
 	github.com/go-logr/logr v1.4.1 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
-	github.com/go-playground/universal-translator v0.18.0 // indirect
+	github.com/go-playground/validator/v10 v10.11.1 // indirect
+	github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
+	github.com/goccy/go-json v0.9.11 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/google/go-cmp v0.6.0 // indirect
+	github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
 	github.com/google/s2a-go v0.1.7 // indirect
 	github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
 	github.com/googleapis/gax-go/v2 v2.12.3 // indirect
@@ -79,7 +85,6 @@ require (
 	github.com/hashicorp/go-safetemp v1.0.0 // indirect
 	github.com/hashicorp/go-version v1.6.0 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
-	github.com/leodido/go-urn v1.2.1 // indirect
 	github.com/manifoldco/promptui v0.9.0 // indirect
 	github.com/mitchellh/go-homedir v1.1.0 // indirect
 	github.com/mitchellh/go-testing-interface v1.14.1 // indirect
@@ -92,7 +97,6 @@ require (
 	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/ugorji/go/codec v1.2.7 // indirect
 	github.com/ulikunitz/xz v0.5.10 // 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
@@ -101,6 +105,7 @@ require (
 	go.opentelemetry.io/otel/trace v1.24.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/oauth2 v0.22.0 // indirect
+	golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
 	google.golang.org/api v0.171.0 // indirect
 	google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
@@ -117,7 +122,7 @@ require (
 	github.com/StackExchange/wmi v1.2.1 // indirect
 	github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
 	github.com/Workiva/go-datastructures v1.1.5 // indirect
-	github.com/armon/go-metrics v0.4.1 // indirect
+	github.com/armon/go-metrics v0.4.1
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect
 	github.com/btcsuite/btcd v0.24.2 // indirect
diff --git a/go.sum b/go.sum
index df18a798..bcdbd677 100644
--- a/go.sum
+++ b/go.sum
@@ -199,8 +199,6 @@ github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMb
 github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
 github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o=
 github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA=
-github.com/AltheaFoundation/canto/v6 v6.0.1 h1:3NaThdYw6AZD2bf12bz8Qa3beXDbe0a5pNDB0lAitv4=
-github.com/AltheaFoundation/canto/v6 v6.0.1/go.mod h1:CaaiFJ+WEL09j7JTI58JNBrd9p0d7a54eTt61a+nEDk=
 github.com/AltheaFoundation/ethermint v0.22.3 h1:NlGZmFveXsFtiOFiTXiEwyA5qTbatJtleWL2yx5h/Ow=
 github.com/AltheaFoundation/ethermint v0.22.3/go.mod h1:Pu0VyapSeMO2o7LlvHVn2ir+P9RuUxhIiIv6VYaFivw=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM=
@@ -857,6 +855,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -1025,6 +1024,7 @@ github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG
 github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
 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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -1088,6 +1088,8 @@ github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
 github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
 github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
 github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
@@ -1212,8 +1214,8 @@ github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:s
 github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
 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 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
 github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
-github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
 github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
@@ -1307,6 +1309,7 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP
 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
 golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
 golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
@@ -1387,6 +1390,7 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
diff --git a/testutil/fund.go b/testutil/fund.go
new file mode 100644
index 00000000..018f1cfd
--- /dev/null
+++ b/testutil/fund.go
@@ -0,0 +1,29 @@
+package testutil
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
+	minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
+)
+
+// FundAccount is a utility function that funds an account by minting and
+// sending the coins to the address. This should be used for testing purposes
+// only!
+func FundAccount(bankKeeper bankkeeper.Keeper, ctx sdk.Context, addr sdk.AccAddress, amounts sdk.Coins) error {
+	if err := bankKeeper.MintCoins(ctx, minttypes.ModuleName, amounts); err != nil {
+		return err
+	}
+
+	return bankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, amounts)
+}
+
+// FundModuleAccount is a utility function that funds a module account by
+// minting and sending the coins to the address. This should be used for testing
+// purposes only!
+func FundModuleAccount(bankKeeper bankkeeper.Keeper, ctx sdk.Context, recipientMod string, amounts sdk.Coins) error {
+	if err := bankKeeper.MintCoins(ctx, minttypes.ModuleName, amounts); err != nil {
+		return err
+	}
+
+	return bankKeeper.SendCoinsFromModuleToModule(ctx, minttypes.ModuleName, recipientMod, amounts)
+}
diff --git a/testutil/network/doc.go b/testutil/network/doc.go
new file mode 100644
index 00000000..9e44f371
--- /dev/null
+++ b/testutil/network/doc.go
@@ -0,0 +1,65 @@
+/*
+Package network implements and exposes a fully operational in-process Tendermint
+test network that consists of at least one or potentially many validators. This
+test network can be used primarily for integration tests or unit test suites.
+
+The test network utilizes SimApp as the ABCI application and uses all the modules
+defined in the Cosmos SDK. An in-process test network can be configured with any
+number of validators as well as account funds and even custom genesis state.
+
+When creating a test network, a series of Validator objects are returned. Each
+Validator object has useful information such as their address and public key. A
+Validator will also provide its RPC, P2P, and API addresses that can be useful
+for integration testing. In addition, a Tendermint local RPC client is also provided
+which can be handy for making direct RPC calls to Tendermint.
+
+Note, due to limitations in concurrency and the design of the RPC layer in
+Tendermint, only the first Validator object will have an RPC and API client
+exposed. Due to this exact same limitation, only a single test network can exist
+at a time. A caller must be certain it calls Cleanup after it no longer needs
+the network.
+
+A typical testing flow might look like the following:
+
+	type IntegrationTestSuite struct {
+		suite.Suite
+
+		cfg     testutil.Config
+		network *testutil.Network
+	}
+
+	func (s *IntegrationTestSuite) SetupSuite() {
+		s.T().Log("setting up integration test suite")
+
+		cfg := testutil.DefaultConfig()
+		cfg.NumValidators = 1
+
+		s.cfg = cfg
+		s.network = testutil.New(s.T(), cfg)
+
+		_, err := s.network.WaitForHeight(1)
+		s.Require().NoError(err)
+	}
+
+	func (s *IntegrationTestSuite) TearDownSuite() {
+		s.T().Log("tearing down integration test suite")
+
+		// This is important and must be called to ensure other tests can create
+		// a network!
+		s.network.Cleanup()
+	}
+
+	func (s *IntegrationTestSuite) TestQueryBalancesRequestHandlerFn() {
+		val := s.network.Validators[0]
+		baseURL := val.APIAddress
+
+		// Use baseURL to make API HTTP requests or use val.RPCClient to make direct
+		// Tendermint RPC calls.
+		// ...
+	}
+
+	func TestIntegrationTestSuite(t *testing.T) {
+		suite.Run(t, new(IntegrationTestSuite))
+	}
+*/
+package network
diff --git a/testutil/network/network.go b/testutil/network/network.go
new file mode 100644
index 00000000..269d161e
--- /dev/null
+++ b/testutil/network/network.go
@@ -0,0 +1,694 @@
+package network
+
+import (
+	"bufio"
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+	"net/url"
+	"os"
+	"path/filepath"
+	"strings"
+	"sync"
+	"testing"
+	"time"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/ethclient"
+	"github.com/spf13/cobra"
+	tmcfg "github.com/tendermint/tendermint/config"
+	tmflags "github.com/tendermint/tendermint/libs/cli/flags"
+	"github.com/tendermint/tendermint/libs/log"
+	tmrand "github.com/tendermint/tendermint/libs/rand"
+	"github.com/tendermint/tendermint/node"
+	tmclient "github.com/tendermint/tendermint/rpc/client"
+	dbm "github.com/tendermint/tm-db"
+	"google.golang.org/grpc"
+
+	"github.com/cosmos/cosmos-sdk/baseapp"
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/client/tx"
+	"github.com/cosmos/cosmos-sdk/codec"
+	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
+	"github.com/cosmos/cosmos-sdk/crypto/keyring"
+	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
+	pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types"
+	"github.com/cosmos/cosmos-sdk/server"
+	"github.com/cosmos/cosmos-sdk/server/api"
+	srvconfig "github.com/cosmos/cosmos-sdk/server/config"
+	servertypes "github.com/cosmos/cosmos-sdk/server/types"
+	"github.com/cosmos/cosmos-sdk/simapp"
+	"github.com/cosmos/cosmos-sdk/testutil"
+	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"
+	"github.com/cosmos/cosmos-sdk/x/genutil"
+	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
+	"github.com/evmos/ethermint/crypto/hd"
+
+	"github.com/evmos/ethermint/server/config"
+	ethermint "github.com/evmos/ethermint/types"
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+
+	althea "github.com/AltheaFoundation/althea-L1/app"
+	"github.com/AltheaFoundation/althea-L1/app/params"
+)
+
+// package-wide network lock to only allow one test network at a time
+var lock = new(sync.Mutex)
+
+// AppConstructor defines a function which accepts a network configuration and
+// creates an ABCI Application to provide to Tendermint.
+type AppConstructor = func(val Validator) servertypes.Application
+
+// Config defines the necessary configuration used to bootstrap and start an
+// in-process local testing network.
+type Config struct {
+	KeyringOptions    []keyring.Option // keyring configuration options
+	Codec             codec.Codec
+	LegacyAmino       *codec.LegacyAmino // TODO: Remove!
+	InterfaceRegistry codectypes.InterfaceRegistry
+	TxConfig          client.TxConfig
+	AccountRetriever  client.AccountRetriever
+	AppConstructor    AppConstructor      // the ABCI application constructor
+	GenesisState      simapp.GenesisState // custom gensis state to provide
+	TimeoutCommit     time.Duration       // the consensus commitment timeout
+	AccountTokens     sdk.Int             // the amount of unique validator tokens (e.g. 1000node0)
+	StakingTokens     sdk.Int             // the amount of tokens each validator has available to stake
+	BondedTokens      sdk.Int             // the amount of tokens each validator stakes
+	NumValidators     int                 // the total number of validators to create and bond
+	ChainID           string              // the network chain-id
+	BondDenom         string              // the staking bond denomination
+	MinGasPrices      string              // the minimum gas prices each validator will accept
+	PruningStrategy   string              // the pruning strategy each validator will have
+	SigningAlgo       string              // signing algorithm for keys
+	RPCAddress        string              // RPC listen address (including port)
+	JSONRPCAddress    string              // JSON-RPC listen address (including port)
+	APIAddress        string              // REST API listen address (including port)
+	GRPCAddress       string              // GRPC server listen address (including port)
+	EnableTMLogging   bool                // enable Tendermint logging to STDOUT
+	CleanupDir        bool                // remove base temporary directory during cleanup
+	PrintMnemonic     bool                // print the mnemonic of first validator as log output for testing
+}
+
+// DefaultConfig returns a sane default configuration suitable for nearly all
+// testing requirements.
+func DefaultConfig() Config {
+	encCfg := althea.MakeEncodingConfig()
+
+	return Config{
+		Codec:             encCfg.Codec,
+		TxConfig:          encCfg.TxConfig,
+		LegacyAmino:       encCfg.Amino,
+		InterfaceRegistry: encCfg.InterfaceRegistry,
+		AccountRetriever:  authtypes.AccountRetriever{},
+		AppConstructor:    NewAppConstructor(encCfg),
+		GenesisState:      althea.ModuleBasics.DefaultGenesis(encCfg.Codec),
+		TimeoutCommit:     2 * time.Second,
+		ChainID:           fmt.Sprintf("canto_%d-1", tmrand.Int63n(9999999999999)+1),
+		NumValidators:     4,
+		BondDenom:         ethermint.AttoPhoton,
+		MinGasPrices:      fmt.Sprintf("0.000006%s", ethermint.AttoPhoton),
+		AccountTokens:     sdk.TokensFromConsensusPower(1000, ethermint.PowerReduction),
+		StakingTokens:     sdk.TokensFromConsensusPower(500, ethermint.PowerReduction),
+		BondedTokens:      sdk.TokensFromConsensusPower(100, ethermint.PowerReduction),
+		PruningStrategy:   pruningtypes.PruningOptionNothing,
+		CleanupDir:        true,
+		SigningAlgo:       string(hd.EthSecp256k1Type),
+		KeyringOptions:    []keyring.Option{hd.EthSecp256k1Option()},
+		PrintMnemonic:     false,
+	}
+}
+
+// NewAppConstructor returns a new canto AppConstructor
+func NewAppConstructor(encodingCfg params.EncodingConfig) AppConstructor {
+	return func(val Validator) servertypes.Application {
+		return althea.NewAltheaApp(
+			val.Ctx.Logger, dbm.NewMemDB(), nil, true, make(map[int64]bool), val.Ctx.Config.RootDir, 0,
+			encodingCfg,
+			simapp.EmptyAppOptions{},
+			baseapp.SetPruning(pruningtypes.NewPruningOptionsFromString(val.AppConfig.Pruning)),
+			baseapp.SetMinGasPrices(val.AppConfig.MinGasPrices),
+		)
+	}
+}
+
+type (
+	// Network defines a local in-process testing network using SimApp. It can be
+	// configured to start any number of validators, each with its own RPC and API
+	// clients. Typically, this test network would be used in client and integration
+	// testing where user input is expected.
+	//
+	// Note, due to Tendermint constraints in regards to RPC functionality, there
+	// may only be one test network running at a time. Thus, any caller must be
+	// sure to Cleanup after testing is finished in order to allow other tests
+	// to create networks. In addition, only the first validator will have a valid
+	// RPC and API server/client.
+	Network struct {
+		Logger     Logger
+		BaseDir    string
+		Validators []*Validator
+
+		Config Config
+	}
+
+	// Validator defines an in-process Tendermint validator node. Through this object,
+	// a client can make RPC and API calls and interact with any client command
+	// or handler.
+	Validator struct {
+		AppConfig     *config.Config
+		ClientCtx     client.Context
+		Ctx           *server.Context
+		Dir           string
+		NodeID        string
+		PubKey        cryptotypes.PubKey
+		Moniker       string
+		APIAddress    string
+		RPCAddress    string
+		P2PAddress    string
+		Address       sdk.AccAddress
+		ValAddress    sdk.ValAddress
+		RPCClient     tmclient.Client
+		JSONRPCClient *ethclient.Client
+
+		tmNode      *node.Node
+		api         *api.Server
+		grpc        *grpc.Server
+		grpcWeb     *http.Server
+		jsonrpc     *http.Server
+		jsonrpcDone chan struct{}
+	}
+)
+
+// Logger is a network logger interface that exposes testnet-level Log() methods for an in-process testing network
+// This is not to be confused with logging that may happen at an individual node or validator level
+type Logger interface {
+	Log(args ...interface{})
+	Logf(format string, args ...interface{})
+}
+
+var (
+	_ Logger = (*testing.T)(nil)
+	_ Logger = (*CLILogger)(nil)
+)
+
+type CLILogger struct {
+	cmd *cobra.Command
+}
+
+func (s CLILogger) Log(args ...interface{}) {
+	s.cmd.Println(args...)
+}
+
+func (s CLILogger) Logf(format string, args ...interface{}) {
+	s.cmd.Printf(format, args...)
+}
+
+func NewCLILogger(cmd *cobra.Command) CLILogger {
+	return CLILogger{cmd}
+}
+
+// New creates a new Network for integration tests or in-process testnets run via the CLI
+func New(l Logger, baseDir string, cfg Config) (*Network, error) {
+	// only one caller/test can create and use a network at a time
+	l.Log("acquiring test network lock")
+	lock.Lock()
+
+	if !ethermint.IsValidChainID(cfg.ChainID) {
+		return nil, fmt.Errorf("invalid chain-id: %s", cfg.ChainID)
+	}
+
+	network := &Network{
+		Logger:     l,
+		BaseDir:    baseDir,
+		Validators: make([]*Validator, cfg.NumValidators),
+		Config:     cfg,
+	}
+
+	l.Logf("preparing test network with chain-id \"%s\"\n", cfg.ChainID)
+
+	monikers := make([]string, cfg.NumValidators)
+	nodeIDs := make([]string, cfg.NumValidators)
+	valPubKeys := make([]cryptotypes.PubKey, cfg.NumValidators)
+
+	var (
+		genAccounts []authtypes.GenesisAccount
+		genBalances []banktypes.Balance
+		genFiles    []string
+	)
+
+	buf := bufio.NewReader(os.Stdin)
+
+	// generate private keys, node IDs, and initial transactions
+	for i := 0; i < cfg.NumValidators; i++ {
+		appCfg := config.DefaultConfig()
+		appCfg.Pruning = cfg.PruningStrategy
+		appCfg.MinGasPrices = cfg.MinGasPrices
+		appCfg.API.Enable = true
+		appCfg.API.Swagger = false
+		appCfg.Telemetry.Enabled = false
+		appCfg.Telemetry.GlobalLabels = [][]string{{"chain_id", cfg.ChainID}}
+
+		ctx := server.NewDefaultContext()
+		tmCfg := ctx.Config
+		tmCfg.Consensus.TimeoutCommit = cfg.TimeoutCommit
+
+		// Only allow the first validator to expose an RPC, API and gRPC
+		// server/client due to Tendermint in-process constraints.
+		apiAddr := ""
+		tmCfg.RPC.ListenAddress = ""
+		appCfg.GRPC.Enable = false
+		appCfg.GRPCWeb.Enable = false
+		apiListenAddr := ""
+		if i == 0 {
+			if cfg.APIAddress != "" {
+				apiListenAddr = cfg.APIAddress
+			} else {
+				var err error
+				apiListenAddr, _, err = server.FreeTCPAddr()
+				if err != nil {
+					return nil, err
+				}
+			}
+
+			appCfg.API.Address = apiListenAddr
+			apiURL, err := url.Parse(apiListenAddr)
+			if err != nil {
+				return nil, err
+			}
+			apiAddr = fmt.Sprintf("http://%s:%s", apiURL.Hostname(), apiURL.Port())
+
+			if cfg.RPCAddress != "" {
+				tmCfg.RPC.ListenAddress = cfg.RPCAddress
+			} else {
+				rpcAddr, _, err := server.FreeTCPAddr()
+				if err != nil {
+					return nil, err
+				}
+				tmCfg.RPC.ListenAddress = rpcAddr
+			}
+
+			if cfg.GRPCAddress != "" {
+				appCfg.GRPC.Address = cfg.GRPCAddress
+			} else {
+				_, grpcPort, err := server.FreeTCPAddr()
+				if err != nil {
+					return nil, err
+				}
+				appCfg.GRPC.Address = fmt.Sprintf("0.0.0.0:%s", grpcPort)
+			}
+			appCfg.GRPC.Enable = true
+
+			_, grpcWebPort, err := server.FreeTCPAddr()
+			if err != nil {
+				return nil, err
+			}
+			appCfg.GRPCWeb.Address = fmt.Sprintf("0.0.0.0:%s", grpcWebPort)
+			appCfg.GRPCWeb.Enable = true
+
+			if cfg.JSONRPCAddress != "" {
+				appCfg.JSONRPC.Address = cfg.JSONRPCAddress
+			} else {
+				_, jsonRPCPort, err := server.FreeTCPAddr()
+				if err != nil {
+					return nil, err
+				}
+				appCfg.JSONRPC.Address = fmt.Sprintf("0.0.0.0:%s", jsonRPCPort)
+			}
+			appCfg.JSONRPC.Enable = true
+			appCfg.JSONRPC.API = config.GetAPINamespaces()
+		}
+
+		logger := log.NewNopLogger()
+		if cfg.EnableTMLogging {
+			logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout))
+			logger, _ = tmflags.ParseLogLevel("info", logger, tmcfg.DefaultLogLevel)
+		}
+
+		ctx.Logger = logger
+
+		nodeDirName := fmt.Sprintf("node%d", i)
+		nodeDir := filepath.Join(network.BaseDir, nodeDirName, "cantod")
+		clientDir := filepath.Join(network.BaseDir, nodeDirName, "cantocli")
+		gentxsDir := filepath.Join(network.BaseDir, "gentxs")
+
+		err := os.MkdirAll(filepath.Join(nodeDir, "config"), 0o750)
+		if err != nil {
+			return nil, err
+		}
+
+		err = os.MkdirAll(clientDir, 0o750)
+		if err != nil {
+			return nil, err
+		}
+
+		tmCfg.SetRoot(nodeDir)
+		tmCfg.Moniker = nodeDirName
+		monikers[i] = nodeDirName
+
+		proxyAddr, _, err := server.FreeTCPAddr()
+		if err != nil {
+			return nil, err
+		}
+		tmCfg.ProxyApp = proxyAddr
+
+		p2pAddr, _, err := server.FreeTCPAddr()
+		if err != nil {
+			return nil, err
+		}
+		tmCfg.P2P.ListenAddress = p2pAddr
+		tmCfg.P2P.AddrBookStrict = false
+		tmCfg.P2P.AllowDuplicateIP = true
+
+		nodeID, pubKey, err := genutil.InitializeNodeValidatorFiles(tmCfg)
+		if err != nil {
+			return nil, err
+		}
+		nodeIDs[i] = nodeID
+		valPubKeys[i] = pubKey
+
+		kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, clientDir, buf, cfg.Codec, cfg.KeyringOptions...)
+		if err != nil {
+			return nil, err
+		}
+
+		keyringAlgos, _ := kb.SupportedAlgorithms()
+		algo, err := keyring.NewSigningAlgoFromString(cfg.SigningAlgo, keyringAlgos)
+		if err != nil {
+			return nil, err
+		}
+
+		addr, secret, err := testutil.GenerateSaveCoinKey(kb, nodeDirName, "", true, algo)
+		if err != nil {
+			return nil, err
+		}
+
+		// if PrintMnemonic is set to true, we print the first validator node's secret to the network's logger
+		// for debugging and manual testing
+		if cfg.PrintMnemonic && i == 0 {
+			printMnemonic(l, secret)
+		}
+
+		info := map[string]string{"secret": secret}
+		infoBz, err := json.Marshal(info)
+		if err != nil {
+			return nil, err
+		}
+
+		// save private key seed words
+		err = WriteFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, infoBz)
+		if err != nil {
+			return nil, err
+		}
+
+		balances := sdk.NewCoins(
+			sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), cfg.AccountTokens),
+			sdk.NewCoin(cfg.BondDenom, cfg.StakingTokens),
+		)
+
+		genFiles = append(genFiles, tmCfg.GenesisFile())
+		genBalances = append(genBalances, banktypes.Balance{Address: addr.String(), Coins: balances.Sort()})
+		genAccounts = append(genAccounts, &ethermint.EthAccount{
+			BaseAccount: authtypes.NewBaseAccount(addr, nil, 0, 0),
+			CodeHash:    common.BytesToHash(evmtypes.EmptyCodeHash).Hex(),
+		})
+
+		commission, err := sdk.NewDecFromStr("0.5")
+		if err != nil {
+			return nil, err
+		}
+
+		createValMsg, err := stakingtypes.NewMsgCreateValidator(
+			sdk.ValAddress(addr),
+			valPubKeys[i],
+			sdk.NewCoin(cfg.BondDenom, cfg.BondedTokens),
+			stakingtypes.NewDescription(nodeDirName, "", "", "", ""),
+			stakingtypes.NewCommissionRates(commission, sdk.OneDec(), sdk.OneDec()),
+			sdk.OneInt(),
+		)
+		if err != nil {
+			return nil, err
+		}
+
+		p2pURL, err := url.Parse(p2pAddr)
+		if err != nil {
+			return nil, err
+		}
+
+		memo := fmt.Sprintf("%s@%s:%s", nodeIDs[i], p2pURL.Hostname(), p2pURL.Port())
+		fee := sdk.NewCoins(sdk.NewCoin(cfg.BondDenom, sdk.NewInt(0)))
+		txBuilder := cfg.TxConfig.NewTxBuilder()
+		err = txBuilder.SetMsgs(createValMsg)
+		if err != nil {
+			return nil, err
+		}
+		txBuilder.SetFeeAmount(fee)    // Arbitrary fee
+		txBuilder.SetGasLimit(1000000) // Need at least 100386
+		txBuilder.SetMemo(memo)
+
+		txFactory := tx.Factory{}
+		txFactory = txFactory.
+			WithChainID(cfg.ChainID).
+			WithMemo(memo).
+			WithKeybase(kb).
+			WithTxConfig(cfg.TxConfig)
+
+		if err := tx.Sign(txFactory, nodeDirName, txBuilder, true); err != nil {
+			return nil, err
+		}
+
+		txBz, err := cfg.TxConfig.TxJSONEncoder()(txBuilder.GetTx())
+		if err != nil {
+			return nil, err
+		}
+
+		if err := WriteFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBz); err != nil {
+			return nil, err
+		}
+
+		customAppTemplate, _ := config.AppConfig(ethermint.AttoPhoton)
+		srvconfig.SetConfigTemplate(customAppTemplate)
+		srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config/app.toml"), appCfg)
+
+		ctx.Viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
+		ctx.Viper.SetConfigFile(filepath.Join(nodeDir, "config/app.toml"))
+		err = ctx.Viper.ReadInConfig()
+		if err != nil {
+			return nil, err
+		}
+
+		clientCtx := client.Context{}.
+			WithKeyringDir(clientDir).
+			WithKeyring(kb).
+			WithHomeDir(tmCfg.RootDir).
+			WithChainID(cfg.ChainID).
+			WithInterfaceRegistry(cfg.InterfaceRegistry).
+			WithCodec(cfg.Codec).
+			WithLegacyAmino(cfg.LegacyAmino).
+			WithTxConfig(cfg.TxConfig).
+			WithAccountRetriever(cfg.AccountRetriever)
+
+		network.Validators[i] = &Validator{
+			AppConfig:  appCfg,
+			ClientCtx:  clientCtx,
+			Ctx:        ctx,
+			Dir:        filepath.Join(network.BaseDir, nodeDirName),
+			NodeID:     nodeID,
+			PubKey:     pubKey,
+			Moniker:    nodeDirName,
+			RPCAddress: tmCfg.RPC.ListenAddress,
+			P2PAddress: tmCfg.P2P.ListenAddress,
+			APIAddress: apiAddr,
+			Address:    addr,
+			ValAddress: sdk.ValAddress(addr),
+		}
+	}
+
+	err := initGenFiles(cfg, genAccounts, genBalances, genFiles)
+	if err != nil {
+		return nil, err
+	}
+	err = collectGenFiles(cfg, network.Validators, network.BaseDir)
+	if err != nil {
+		return nil, err
+	}
+
+	l.Log("starting test network...")
+	for _, v := range network.Validators {
+		err := startInProcess(cfg, v)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	l.Log("started test network")
+
+	// Ensure we cleanup incase any test was abruptly halted (e.g. SIGINT) as any
+	// defer in a test would not be called.
+	server.TrapSignal(network.Cleanup)
+
+	return network, nil
+}
+
+// LatestHeight returns the latest height of the network or an error if the
+// query fails or no validators exist.
+func (n *Network) LatestHeight() (int64, error) {
+	if len(n.Validators) == 0 {
+		return 0, errors.New("no validators available")
+	}
+
+	status, err := n.Validators[0].RPCClient.Status(context.Background())
+	if err != nil {
+		return 0, err
+	}
+
+	return status.SyncInfo.LatestBlockHeight, nil
+}
+
+// WaitForHeight performs a blocking check where it waits for a block to be
+// committed after a given block. If that height is not reached within a timeout,
+// an error is returned. Regardless, the latest height queried is returned.
+func (n *Network) WaitForHeight(h int64) (int64, error) {
+	return n.WaitForHeightWithTimeout(h, 10*time.Second)
+}
+
+// WaitForHeightWithTimeout is the same as WaitForHeight except the caller can
+// provide a custom timeout.
+func (n *Network) WaitForHeightWithTimeout(h int64, t time.Duration) (int64, error) {
+	ticker := time.NewTicker(time.Second)
+	timeout := time.After(t)
+
+	if len(n.Validators) == 0 {
+		return 0, errors.New("no validators available")
+	}
+
+	var latestHeight int64
+	val := n.Validators[0]
+
+	for {
+		select {
+		case <-timeout:
+			ticker.Stop()
+			return latestHeight, errors.New("timeout exceeded waiting for block")
+		case <-ticker.C:
+			status, err := val.RPCClient.Status(context.Background())
+			if err == nil && status != nil {
+				latestHeight = status.SyncInfo.LatestBlockHeight
+				if latestHeight >= h {
+					return latestHeight, nil
+				}
+			}
+		}
+	}
+}
+
+// WaitForNextBlock waits for the next block to be committed, returning an error
+// upon failure.
+func (n *Network) WaitForNextBlock() error {
+	lastBlock, err := n.LatestHeight()
+	if err != nil {
+		return err
+	}
+
+	_, err = n.WaitForHeight(lastBlock + 1)
+	if err != nil {
+		return err
+	}
+
+	return err
+}
+
+// Cleanup removes the root testing (temporary) directory and stops both the
+// Tendermint and API services. It allows other callers to create and start
+// test networks. This method must be called when a test is finished, typically
+// in a defer.
+func (n *Network) Cleanup() {
+	defer func() {
+		lock.Unlock()
+		n.Logger.Log("released test network lock")
+	}()
+
+	n.Logger.Log("cleaning up test network...")
+
+	for _, v := range n.Validators {
+		if v.tmNode != nil && v.tmNode.IsRunning() {
+			_ = v.tmNode.Stop()
+		}
+
+		if v.api != nil {
+			_ = v.api.Close()
+		}
+
+		if v.grpc != nil {
+			v.grpc.Stop()
+			if v.grpcWeb != nil {
+				_ = v.grpcWeb.Close()
+			}
+		}
+
+		if v.jsonrpc != nil {
+			shutdownCtx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second)
+			defer cancelFn()
+
+			if err := v.jsonrpc.Shutdown(shutdownCtx); err != nil {
+				v.tmNode.Logger.Error("HTTP server shutdown produced a warning", "error", err.Error())
+			} else {
+				v.tmNode.Logger.Info("HTTP server shut down, waiting 5 sec")
+				select {
+				case <-time.Tick(5 * time.Second):
+				case <-v.jsonrpcDone:
+				}
+			}
+		}
+	}
+
+	if n.Config.CleanupDir {
+		_ = os.RemoveAll(n.BaseDir)
+	}
+
+	n.Logger.Log("finished cleaning up test network")
+}
+
+// printMnemonic prints a provided mnemonic seed phrase on a network logger
+// for debugging and manual testing
+func printMnemonic(l Logger, secret string) {
+	lines := []string{
+		"THIS MNEMONIC IS FOR TESTING PURPOSES ONLY",
+		"DO NOT USE IN PRODUCTION",
+		"",
+		strings.Join(strings.Fields(secret)[0:8], " "),
+		strings.Join(strings.Fields(secret)[8:16], " "),
+		strings.Join(strings.Fields(secret)[16:24], " "),
+	}
+
+	lineLengths := make([]int, len(lines))
+	for i, line := range lines {
+		lineLengths[i] = len(line)
+	}
+
+	maxLineLength := 0
+	for _, lineLen := range lineLengths {
+		if lineLen > maxLineLength {
+			maxLineLength = lineLen
+		}
+	}
+
+	l.Log("\n")
+	l.Log(strings.Repeat("+", maxLineLength+8))
+	for _, line := range lines {
+		l.Logf("++  %s  ++\n", centerText(line, maxLineLength))
+	}
+	l.Log(strings.Repeat("+", maxLineLength+8))
+	l.Log("\n")
+}
+
+// centerText centers text across a fixed width, filling either side with whitespace buffers
+func centerText(text string, width int) string {
+	textLen := len(text)
+	leftBuffer := strings.Repeat(" ", (width-textLen)/2)
+	rightBuffer := strings.Repeat(" ", (width-textLen)/2+(width-textLen)%2)
+
+	return fmt.Sprintf("%s%s%s", leftBuffer, text, rightBuffer)
+}
diff --git a/testutil/network/network_test.go b/testutil/network/network_test.go
new file mode 100644
index 00000000..030e158f
--- /dev/null
+++ b/testutil/network/network_test.go
@@ -0,0 +1,64 @@
+//go:build norace
+// +build norace
+
+package network_test
+
+import (
+	"fmt"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/suite"
+
+	"github.com/ethereum/go-ethereum/ethclient"
+	"github.com/evmos/ethermint/server/config"
+	"github.com/evmos/ethermint/testutil/network"
+
+	cantonetwork "github.com/AltheaFoundation/althea-L1/testutil/network"
+)
+
+type IntegrationTestSuite struct {
+	suite.Suite
+
+	network *network.Network
+}
+
+func (s *IntegrationTestSuite) SetupSuite() {
+	s.T().Log("setting up integration test suite")
+
+	var err error
+	cfg := cantonetwork.DefaultConfig()
+	cfg.JSONRPCAddress = config.DefaultJSONRPCAddress
+	cfg.NumValidators = 1
+
+	s.network, err = network.New(s.T(), s.T().TempDir(), cfg)
+	s.Require().NoError(err)
+	s.Require().NotNil(s.network)
+
+	_, err = s.network.WaitForHeight(2)
+	s.Require().NoError(err)
+
+	if s.network.Validators[0].JSONRPCClient == nil {
+		address := fmt.Sprintf("http://%s", s.network.Validators[0].AppConfig.JSONRPC.Address)
+		s.network.Validators[0].JSONRPCClient, err = ethclient.Dial(address)
+		s.Require().NoError(err)
+	}
+}
+
+func (s *IntegrationTestSuite) TearDownSuite() {
+	s.T().Log("tearing down integration test suite")
+	s.network.Cleanup()
+}
+
+func (s *IntegrationTestSuite) TestNetwork_Liveness() {
+	h, err := s.network.WaitForHeightWithTimeout(10, time.Minute)
+	s.Require().NoError(err, "expected to reach 10 blocks; got %d", h)
+
+	latestHeight, err := s.network.LatestHeight()
+	s.Require().NoError(err, "latest height failed")
+	s.Require().GreaterOrEqual(latestHeight, h)
+}
+
+func TestIntegrationTestSuite(t *testing.T) {
+	suite.Run(t, new(IntegrationTestSuite))
+}
diff --git a/testutil/network/util.go b/testutil/network/util.go
new file mode 100644
index 00000000..928ab49c
--- /dev/null
+++ b/testutil/network/util.go
@@ -0,0 +1,256 @@
+package network
+
+import (
+	"encoding/json"
+	"fmt"
+	"path/filepath"
+	"time"
+
+	"github.com/ethereum/go-ethereum/ethclient"
+	tmos "github.com/tendermint/tendermint/libs/os"
+	"github.com/tendermint/tendermint/node"
+	"github.com/tendermint/tendermint/p2p"
+	pvm "github.com/tendermint/tendermint/privval"
+	"github.com/tendermint/tendermint/proxy"
+	"github.com/tendermint/tendermint/rpc/client/local"
+	"github.com/tendermint/tendermint/types"
+	tmtime "github.com/tendermint/tendermint/types/time"
+
+	"github.com/cosmos/cosmos-sdk/server/api"
+	servergrpc "github.com/cosmos/cosmos-sdk/server/grpc"
+	srvtypes "github.com/cosmos/cosmos-sdk/server/types"
+	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+	crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types"
+	"github.com/cosmos/cosmos-sdk/x/genutil"
+	genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
+	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
+	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
+	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
+
+	"github.com/evmos/ethermint/server"
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+)
+
+func startInProcess(cfg Config, val *Validator) error {
+	logger := val.Ctx.Logger
+	tmCfg := val.Ctx.Config
+	tmCfg.Instrumentation.Prometheus = false
+
+	if err := val.AppConfig.ValidateBasic(); err != nil {
+		return err
+	}
+
+	nodeKey, err := p2p.LoadOrGenNodeKey(tmCfg.NodeKeyFile())
+	if err != nil {
+		return err
+	}
+
+	app := cfg.AppConstructor(*val)
+
+	genDocProvider := node.DefaultGenesisDocProviderFunc(tmCfg)
+	tmNode, err := node.NewNode(
+		tmCfg,
+		pvm.LoadOrGenFilePV(tmCfg.PrivValidatorKeyFile(), tmCfg.PrivValidatorStateFile()),
+		nodeKey,
+		proxy.NewLocalClientCreator(app),
+		genDocProvider,
+		node.DefaultDBProvider,
+		node.DefaultMetricsProvider(tmCfg.Instrumentation),
+		logger.With("module", val.Moniker),
+	)
+	if err != nil {
+		return err
+	}
+
+	if err := tmNode.Start(); err != nil {
+		return err
+	}
+
+	val.tmNode = tmNode
+
+	if val.RPCAddress != "" {
+		val.RPCClient = local.New(tmNode)
+	}
+
+	// We'll need a RPC client if the validator exposes a gRPC or REST endpoint.
+	if val.APIAddress != "" || val.AppConfig.GRPC.Enable {
+		val.ClientCtx = val.ClientCtx.
+			WithClient(val.RPCClient)
+
+		// Add the tx service in the gRPC router.
+		app.RegisterTxService(val.ClientCtx)
+
+		// Add the tendermint queries service in the gRPC router.
+		app.RegisterTendermintService(val.ClientCtx)
+	}
+
+	if val.AppConfig.API.Enable && val.APIAddress != "" {
+		apiSrv := api.New(val.ClientCtx, logger.With("module", "api-server"))
+		app.RegisterAPIRoutes(apiSrv, val.AppConfig.API)
+
+		errCh := make(chan error)
+
+		go func() {
+			if err := apiSrv.Start(val.AppConfig.Config); err != nil {
+				errCh <- err
+			}
+		}()
+
+		select {
+		case err := <-errCh:
+			return err
+		case <-time.After(srvtypes.ServerStartTime): // assume server started successfully
+		}
+
+		val.api = apiSrv
+	}
+
+	if val.AppConfig.GRPC.Enable {
+		grpcSrv, err := servergrpc.StartGRPCServer(val.ClientCtx, app, val.AppConfig.GRPC)
+		if err != nil {
+			return err
+		}
+
+		val.grpc = grpcSrv
+
+		if val.AppConfig.GRPCWeb.Enable {
+			val.grpcWeb, err = servergrpc.StartGRPCWeb(grpcSrv, val.AppConfig.Config)
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	if val.AppConfig.JSONRPC.Enable && val.AppConfig.JSONRPC.Address != "" {
+		if val.Ctx == nil || val.Ctx.Viper == nil {
+			return fmt.Errorf("validator %s context is nil", val.Moniker)
+		}
+
+		tmEndpoint := "/websocket"
+		tmRPCAddr := fmt.Sprintf("tcp://%s", val.AppConfig.GRPC.Address)
+
+		val.jsonrpc, val.jsonrpcDone, err = server.StartJSONRPC(val.Ctx, val.ClientCtx, tmRPCAddr, tmEndpoint, val.AppConfig, nil)
+		if err != nil {
+			return err
+		}
+
+		address := fmt.Sprintf("http://%s", val.AppConfig.JSONRPC.Address)
+
+		val.JSONRPCClient, err = ethclient.Dial(address)
+		if err != nil {
+			return fmt.Errorf("failed to dial JSON-RPC at %s: %w", val.AppConfig.JSONRPC.Address, err)
+		}
+	}
+
+	return nil
+}
+
+func collectGenFiles(cfg Config, vals []*Validator, outputDir string) error {
+	genTime := tmtime.Now()
+
+	for i := 0; i < cfg.NumValidators; i++ {
+		tmCfg := vals[i].Ctx.Config
+
+		nodeDir := filepath.Join(outputDir, vals[i].Moniker, "cantod")
+		gentxsDir := filepath.Join(outputDir, "gentxs")
+
+		tmCfg.Moniker = vals[i].Moniker
+		tmCfg.SetRoot(nodeDir)
+
+		initCfg := genutiltypes.NewInitConfig(cfg.ChainID, gentxsDir, vals[i].NodeID, vals[i].PubKey)
+
+		genFile := tmCfg.GenesisFile()
+		genDoc, err := types.GenesisDocFromFile(genFile)
+		if err != nil {
+			return err
+		}
+
+		appState, err := genutil.GenAppStateFromConfig(cfg.Codec, cfg.TxConfig,
+			tmCfg, initCfg, *genDoc, banktypes.GenesisBalancesIterator{})
+		if err != nil {
+			return err
+		}
+
+		// overwrite each validator's genesis file to have a canonical genesis time
+		if err := genutil.ExportGenesisFileWithTime(genFile, cfg.ChainID, nil, appState, genTime); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func initGenFiles(cfg Config, genAccounts []authtypes.GenesisAccount, genBalances []banktypes.Balance, genFiles []string) error {
+	// set the accounts in the genesis state
+	var authGenState authtypes.GenesisState
+	cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[authtypes.ModuleName], &authGenState)
+
+	accounts, err := authtypes.PackAccounts(genAccounts)
+	if err != nil {
+		return err
+	}
+
+	authGenState.Accounts = append(authGenState.Accounts, accounts...)
+	cfg.GenesisState[authtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&authGenState)
+
+	// set the balances in the genesis state
+	var bankGenState banktypes.GenesisState
+	bankGenState.Balances = genBalances
+	cfg.GenesisState[banktypes.ModuleName] = cfg.Codec.MustMarshalJSON(&bankGenState)
+
+	var stakingGenState stakingtypes.GenesisState
+	cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[stakingtypes.ModuleName], &stakingGenState)
+
+	stakingGenState.Params.BondDenom = cfg.BondDenom
+	cfg.GenesisState[stakingtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&stakingGenState)
+
+	var govGenState govv1beta1.GenesisState
+	cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[govtypes.ModuleName], &govGenState)
+
+	govGenState.DepositParams.MinDeposit[0].Denom = cfg.BondDenom
+	cfg.GenesisState[govtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&govGenState)
+
+	var crisisGenState crisistypes.GenesisState
+	cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[crisistypes.ModuleName], &crisisGenState)
+
+	crisisGenState.ConstantFee.Denom = cfg.BondDenom
+	cfg.GenesisState[crisistypes.ModuleName] = cfg.Codec.MustMarshalJSON(&crisisGenState)
+
+	var evmGenState evmtypes.GenesisState
+	cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[evmtypes.ModuleName], &evmGenState)
+
+	evmGenState.Params.EvmDenom = cfg.BondDenom
+	cfg.GenesisState[evmtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&evmGenState)
+
+	appGenStateJSON, err := json.MarshalIndent(cfg.GenesisState, "", "  ")
+	if err != nil {
+		return err
+	}
+
+	genDoc := types.GenesisDoc{
+		ChainID:    cfg.ChainID,
+		AppState:   appGenStateJSON,
+		Validators: nil,
+	}
+
+	// generate empty genesis files for each validator and save
+	for i := 0; i < cfg.NumValidators; i++ {
+		if err := genDoc.SaveAs(genFiles[i]); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func WriteFile(name string, dir string, contents []byte) error {
+	file := filepath.Join(dir, name)
+
+	err := tmos.EnsureDir(dir, 0o755)
+	if err != nil {
+		return err
+	}
+
+	return tmos.WriteFile(file, contents, 0o644)
+}
diff --git a/x/erc20/client/cli/query.go b/x/erc20/client/cli/query.go
new file mode 100644
index 00000000..a99ebbb1
--- /dev/null
+++ b/x/erc20/client/cli/query.go
@@ -0,0 +1,128 @@
+package cli
+
+import (
+	"context"
+
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/client/flags"
+	"github.com/spf13/cobra"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+// GetQueryCmd returns the parent command for all erc20 CLI query commands
+func GetQueryCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:                        types.ModuleName,
+		Short:                      "Querying commands for the erc20 module",
+		DisableFlagParsing:         true,
+		SuggestionsMinimumDistance: 2,
+		RunE:                       client.ValidateCmd,
+	}
+
+	cmd.AddCommand(
+		GetTokenPairsCmd(),
+		GetTokenPairCmd(),
+		GetParamsCmd(),
+	)
+	return cmd
+}
+
+// GetTokenPairsCmd queries all registered token pairs
+func GetTokenPairsCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "token-pairs",
+		Short: "Gets registered token pairs",
+		Long:  "Gets registered token pairs",
+		Args:  cobra.NoArgs,
+		RunE: func(cmd *cobra.Command, _ []string) error {
+			clientCtx, err := client.GetClientQueryContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			queryClient := types.NewQueryClient(clientCtx)
+
+			pageReq, err := client.ReadPageRequest(cmd.Flags())
+			if err != nil {
+				return err
+			}
+
+			req := &types.QueryTokenPairsRequest{
+				Pagination: pageReq,
+			}
+
+			res, err := queryClient.TokenPairs(context.Background(), req)
+			if err != nil {
+				return err
+			}
+
+			return clientCtx.PrintProto(res)
+		},
+	}
+
+	flags.AddQueryFlagsToCmd(cmd)
+	return cmd
+}
+
+// GetTokenPairsCmd queries a registered token pair
+func GetTokenPairCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "token-pair [token]",
+		Short: "Get a registered token pair",
+		Long:  "Get a registered token pair",
+		Args:  cobra.ExactArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clientCtx, err := client.GetClientQueryContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			queryClient := types.NewQueryClient(clientCtx)
+
+			req := &types.QueryTokenPairRequest{
+				Token: args[0],
+			}
+
+			res, err := queryClient.TokenPair(context.Background(), req)
+			if err != nil {
+				return err
+			}
+
+			return clientCtx.PrintProto(res)
+		},
+	}
+
+	flags.AddQueryFlagsToCmd(cmd)
+	return cmd
+}
+
+// GetParamsCmd queries erc20 module params
+func GetParamsCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "params",
+		Short: "Gets erc20 params",
+		Long:  "Gets erc20 params",
+		Args:  cobra.NoArgs,
+		RunE: func(cmd *cobra.Command, _ []string) error {
+			clientCtx, err := client.GetClientQueryContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			queryClient := types.NewQueryClient(clientCtx)
+
+			req := &types.QueryParamsRequest{}
+
+			res, err := queryClient.Params(context.Background(), req)
+			if err != nil {
+				return err
+			}
+
+			return clientCtx.PrintProto(res)
+		},
+	}
+
+	flags.AddQueryFlagsToCmd(cmd)
+	return cmd
+}
diff --git a/x/erc20/client/cli/tx.go b/x/erc20/client/cli/tx.go
new file mode 100644
index 00000000..a41a7166
--- /dev/null
+++ b/x/erc20/client/cli/tx.go
@@ -0,0 +1,363 @@
+package cli
+
+import (
+	"fmt"
+
+	"github.com/spf13/cobra"
+
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/client/flags"
+	"github.com/cosmos/cosmos-sdk/client/tx"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/version"
+	"github.com/cosmos/cosmos-sdk/x/gov/client/cli"
+	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
+
+	"github.com/ethereum/go-ethereum/common"
+
+	ethermint "github.com/evmos/ethermint/types"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+// NewTxCmd returns a root CLI command handler for erc20 transaction commands
+func NewTxCmd() *cobra.Command {
+	txCmd := &cobra.Command{
+		Use:                        types.ModuleName,
+		Short:                      "erc20 subcommands",
+		DisableFlagParsing:         true,
+		SuggestionsMinimumDistance: 2,
+		RunE:                       client.ValidateCmd,
+	}
+
+	txCmd.AddCommand(
+		NewConvertCoinCmd(),
+		NewConvertERC20Cmd(),
+	)
+	return txCmd
+}
+
+// NewConvertCoinCmd returns a CLI command handler for converting a Cosmos coin
+func NewConvertCoinCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "convert-coin [coin] [receiver_hex]",
+		Short: "Convert a Cosmos coin to ERC20. When the receiver [optional] is omitted, the ERC20 tokens are transferred to the sender.",
+		Args:  cobra.RangeArgs(1, 2),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			cliCtx, err := client.GetClientTxContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			coin, err := sdk.ParseCoinNormalized(args[0])
+			if err != nil {
+				return err
+			}
+
+			var receiver string
+			sender := cliCtx.GetFromAddress()
+
+			if len(args) == 2 {
+				receiver = args[1]
+				if err := ethermint.ValidateAddress(receiver); err != nil {
+					return fmt.Errorf("invalid receiver hex address %w", err)
+				}
+			} else {
+				receiver = common.BytesToAddress(sender).Hex()
+			}
+
+			msg := &types.MsgConvertCoin{
+				Coin:     coin,
+				Receiver: receiver,
+				Sender:   sender.String(),
+			}
+
+			if err := msg.ValidateBasic(); err != nil {
+				return err
+			}
+
+			return tx.GenerateOrBroadcastTxCLI(cliCtx, cmd.Flags(), msg)
+		},
+	}
+
+	flags.AddTxFlagsToCmd(cmd)
+	return cmd
+}
+
+// NewConvertERC20Cmd returns a CLI command handler for converting an ERC20
+func NewConvertERC20Cmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "convert-erc20 [contract-address] [amount] [receiver]",
+		Short: "Convert an ERC20 token to Cosmos coin.  When the receiver [optional] is omitted, the Cosmos coins are transferred to the sender.",
+		Args:  cobra.RangeArgs(2, 3),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			cliCtx, err := client.GetClientTxContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			contract := args[0]
+			if err := ethermint.ValidateAddress(contract); err != nil {
+				return fmt.Errorf("invalid ERC20 contract address %w", err)
+			}
+
+			amount, ok := sdk.NewIntFromString(args[1])
+			if !ok {
+				return fmt.Errorf("invalid amount %s", args[1])
+			}
+
+			from := common.BytesToAddress(cliCtx.GetFromAddress().Bytes())
+
+			receiver := cliCtx.GetFromAddress()
+			if len(args) == 3 {
+				receiver, err = sdk.AccAddressFromBech32(args[2])
+				if err != nil {
+					return err
+				}
+			}
+
+			msg := &types.MsgConvertERC20{
+				ContractAddress: contract,
+				Amount:          amount,
+				Receiver:        receiver.String(),
+				Sender:          from.Hex(),
+			}
+
+			if err := msg.ValidateBasic(); err != nil {
+				return err
+			}
+
+			return tx.GenerateOrBroadcastTxCLI(cliCtx, cmd.Flags(), msg)
+		},
+	}
+
+	flags.AddTxFlagsToCmd(cmd)
+	return cmd
+}
+
+// NewRegisterCoinProposalCmd implements the command to submit a community-pool-spend proposal
+func NewRegisterCoinProposalCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "register-coin [metadata]",
+		Args:  cobra.ExactArgs(1),
+		Short: "Submit a register coin proposal",
+		Long: `Submit a proposal to register a Cosmos coin to the erc20 along with an initial deposit.
+Upon passing, the
+The proposal details must be supplied via a JSON file.`,
+		Example: fmt.Sprintf(`$ %s tx gov submit-proposal register-coin <path/to/metadata.json> --from=<key_or_address>
+
+Where metadata.json contains (example):
+
+{
+	"description": "The native staking and governance token of the Osmosis chain",
+	"denom_units": [
+		{
+				"denom": "ibc/<HASH>",
+				"exponent": 0,
+				"aliases": ["ibcuosmo"]
+		},
+		{
+				"denom": "OSMO",
+				"exponent": 6
+		}
+	],
+	"base": "ibc/<HASH>",
+	"display": "OSMO",
+	"name": "Osmo",
+	"symbol": "OSMO"
+}`, version.AppName,
+		),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clientCtx, err := client.GetClientTxContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			title, err := cmd.Flags().GetString(cli.FlagTitle)
+			if err != nil {
+				return err
+			}
+
+			description, err := cmd.Flags().GetString(cli.FlagDescription)
+			if err != nil {
+				return err
+			}
+
+			depositStr, err := cmd.Flags().GetString(cli.FlagDeposit)
+			if err != nil {
+				return err
+			}
+
+			deposit, err := sdk.ParseCoinsNormalized(depositStr)
+			if err != nil {
+				return err
+			}
+
+			metadata, err := ParseMetadata(clientCtx.Codec, args[0])
+			if err != nil {
+				return err
+			}
+
+			from := clientCtx.GetFromAddress()
+
+			content := types.NewRegisterCoinProposal(title, description, metadata)
+
+			msg, err := govv1beta1.NewMsgSubmitProposal(content, deposit, from)
+			if err != nil {
+				return err
+			}
+
+			if err := msg.ValidateBasic(); err != nil {
+				return err
+			}
+
+			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
+		},
+	}
+
+	cmd.Flags().String(cli.FlagTitle, "", "title of proposal")
+	cmd.Flags().String(cli.FlagDescription, "", "description of proposal")
+	cmd.Flags().String(cli.FlagDeposit, "1acanto", "deposit of proposal")
+	if err := cmd.MarkFlagRequired(cli.FlagTitle); err != nil {
+		panic(err)
+	}
+	if err := cmd.MarkFlagRequired(cli.FlagDescription); err != nil {
+		panic(err)
+	}
+	if err := cmd.MarkFlagRequired(cli.FlagDeposit); err != nil {
+		panic(err)
+	}
+	return cmd
+}
+
+// NewRegisterERC20ProposalCmd implements the command to submit a community-pool-spend proposal
+func NewRegisterERC20ProposalCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:     "register-erc20 [erc20-address]",
+		Args:    cobra.ExactArgs(1),
+		Short:   "Submit a proposal to register an ERC20 token",
+		Long:    "Submit a proposal to register an ERC20 token to the erc20 along with an initial deposit.",
+		Example: fmt.Sprintf("$ %s tx gov submit-proposal register-erc20 <contract-address> --from=<key_or_address>", version.AppName),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clientCtx, err := client.GetClientTxContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			title, err := cmd.Flags().GetString(cli.FlagTitle)
+			if err != nil {
+				return err
+			}
+
+			description, err := cmd.Flags().GetString(cli.FlagDescription)
+			if err != nil {
+				return err
+			}
+
+			depositStr, err := cmd.Flags().GetString(cli.FlagDeposit)
+			if err != nil {
+				return err
+			}
+
+			deposit, err := sdk.ParseCoinsNormalized(depositStr)
+			if err != nil {
+				return err
+			}
+
+			erc20Addr := args[0]
+			from := clientCtx.GetFromAddress()
+			content := types.NewRegisterERC20Proposal(title, description, erc20Addr)
+
+			msg, err := govv1beta1.NewMsgSubmitProposal(content, deposit, from)
+			if err != nil {
+				return err
+			}
+
+			if err := msg.ValidateBasic(); err != nil {
+				return err
+			}
+
+			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
+		},
+	}
+
+	cmd.Flags().String(cli.FlagTitle, "", "title of proposal")
+	cmd.Flags().String(cli.FlagDescription, "", "description of proposal")
+	cmd.Flags().String(cli.FlagDeposit, "1acanto", "deposit of proposal")
+	if err := cmd.MarkFlagRequired(cli.FlagTitle); err != nil {
+		panic(err)
+	}
+	if err := cmd.MarkFlagRequired(cli.FlagDescription); err != nil {
+		panic(err)
+	}
+	if err := cmd.MarkFlagRequired(cli.FlagDeposit); err != nil {
+		panic(err)
+	}
+	return cmd
+}
+
+// NewToggleTokenConversionProposalCmd implements the command to submit a community-pool-spend proposal
+func NewToggleTokenConversionProposalCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:     "toggle-token-conversion [token]",
+		Args:    cobra.ExactArgs(1),
+		Short:   "Submit a toggle token conversion proposal",
+		Long:    "Submit a proposal to toggle the conversion of a token pair along with an initial deposit.",
+		Example: fmt.Sprintf("$ %s tx gov submit-proposal toggle-token-conversion <denom_or_contract> --from=<key_or_address>", version.AppName),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clientCtx, err := client.GetClientTxContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			title, err := cmd.Flags().GetString(cli.FlagTitle)
+			if err != nil {
+				return err
+			}
+
+			description, err := cmd.Flags().GetString(cli.FlagDescription)
+			if err != nil {
+				return err
+			}
+
+			depositStr, err := cmd.Flags().GetString(cli.FlagDeposit)
+			if err != nil {
+				return err
+			}
+
+			deposit, err := sdk.ParseCoinsNormalized(depositStr)
+			if err != nil {
+				return err
+			}
+
+			from := clientCtx.GetFromAddress()
+			token := args[0]
+			content := types.NewToggleTokenConversionProposal(title, description, token)
+
+			msg, err := govv1beta1.NewMsgSubmitProposal(content, deposit, from)
+			if err != nil {
+				return err
+			}
+
+			if err := msg.ValidateBasic(); err != nil {
+				return err
+			}
+
+			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
+		},
+	}
+
+	cmd.Flags().String(cli.FlagTitle, "", "title of proposal")
+	cmd.Flags().String(cli.FlagDescription, "", "description of proposal")
+	cmd.Flags().String(cli.FlagDeposit, "1acanto", "deposit of proposal")
+	if err := cmd.MarkFlagRequired(cli.FlagTitle); err != nil {
+		panic(err)
+	}
+	if err := cmd.MarkFlagRequired(cli.FlagDescription); err != nil {
+		panic(err)
+	}
+	if err := cmd.MarkFlagRequired(cli.FlagDeposit); err != nil {
+		panic(err)
+	}
+	return cmd
+}
diff --git a/x/erc20/client/cli/utils.go b/x/erc20/client/cli/utils.go
new file mode 100644
index 00000000..f9f77de3
--- /dev/null
+++ b/x/erc20/client/cli/utils.go
@@ -0,0 +1,25 @@
+package cli
+
+import (
+	"io/ioutil"
+	"path/filepath"
+
+	"github.com/cosmos/cosmos-sdk/codec"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+)
+
+// ParseRegisterCoinProposal reads and parses a ParseRegisterCoinProposal from a file.
+func ParseMetadata(cdc codec.JSONCodec, metadataFile string) (banktypes.Metadata, error) {
+	metadata := banktypes.Metadata{}
+
+	contents, err := ioutil.ReadFile(filepath.Clean(metadataFile))
+	if err != nil {
+		return metadata, err
+	}
+
+	if err = cdc.UnmarshalJSON(contents, &metadata); err != nil {
+		return metadata, err
+	}
+
+	return metadata, nil
+}
diff --git a/x/erc20/client/proposal_handler.go b/x/erc20/client/proposal_handler.go
new file mode 100644
index 00000000..04e81e99
--- /dev/null
+++ b/x/erc20/client/proposal_handler.go
@@ -0,0 +1,13 @@
+package client
+
+import (
+	govclient "github.com/cosmos/cosmos-sdk/x/gov/client"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/client/cli"
+)
+
+var (
+	RegisterCoinProposalHandler          = govclient.NewProposalHandler(cli.NewRegisterCoinProposalCmd)
+	RegisterERC20ProposalHandler         = govclient.NewProposalHandler(cli.NewRegisterERC20ProposalCmd)
+	ToggleTokenConversionProposalHandler = govclient.NewProposalHandler(cli.NewToggleTokenConversionProposalCmd)
+)
diff --git a/x/erc20/genesis.go b/x/erc20/genesis.go
new file mode 100644
index 00000000..1ca47c0b
--- /dev/null
+++ b/x/erc20/genesis.go
@@ -0,0 +1,40 @@
+package erc20
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/keeper"
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+// InitGenesis import module genesis
+func InitGenesis(
+	ctx sdk.Context,
+	k keeper.Keeper,
+	accountKeeper authkeeper.AccountKeeper,
+	data types.GenesisState,
+) {
+	k.SetParams(ctx, data.Params)
+
+	// ensure erc20 module account is set on genesis
+	if acc := accountKeeper.GetModuleAccount(ctx, types.ModuleName); acc == nil {
+		// NOTE: shouldn't occur
+		panic("the erc20 module account has not been set")
+	}
+
+	for _, pair := range data.TokenPairs {
+		id := pair.GetID()
+		k.SetTokenPair(ctx, pair)
+		k.SetDenomMap(ctx, pair.Denom, id)
+		k.SetERC20Map(ctx, pair.GetERC20Contract(), id)
+	}
+}
+
+// ExportGenesis export module status
+func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState {
+	return &types.GenesisState{
+		Params:     k.GetParams(ctx),
+		TokenPairs: k.GetTokenPairs(ctx),
+	}
+}
diff --git a/x/erc20/genesis_test.go b/x/erc20/genesis_test.go
new file mode 100644
index 00000000..bebe536b
--- /dev/null
+++ b/x/erc20/genesis_test.go
@@ -0,0 +1,160 @@
+package erc20_test
+
+import (
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/suite"
+
+	"github.com/tendermint/tendermint/crypto/tmhash"
+	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
+	tmversion "github.com/tendermint/tendermint/proto/tendermint/version"
+	"github.com/tendermint/tendermint/version"
+
+	"github.com/cosmos/cosmos-sdk/simapp"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"
+
+	althea "github.com/AltheaFoundation/althea-L1/app"
+	"github.com/AltheaFoundation/althea-L1/x/erc20"
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+type GenesisTestSuite struct {
+	suite.Suite
+	ctx sdk.Context
+	app *althea.AltheaApp
+}
+
+func TestGenesisTestSuite(t *testing.T) {
+	suite.Run(t, new(GenesisTestSuite))
+}
+
+func (suite *GenesisTestSuite) SetupTest() {
+	// init app
+	suite.app = althea.NewSetup(false, func(aa *althea.AltheaApp, gs simapp.GenesisState) simapp.GenesisState {
+		// setup feemarketGenesis params
+		feemarketGenesis := feemarkettypes.DefaultGenesisState()
+		feemarketGenesis.Params.EnableHeight = 1
+		feemarketGenesis.Params.NoBaseFee = false
+		gs[feemarkettypes.ModuleName] = aa.AppCodec().MustMarshalJSON(feemarketGenesis)
+		return gs
+	})
+
+	suite.ctx = suite.app.BaseApp.NewContext(false, tmproto.Header{
+		Height:          1,
+		ChainID:         "althea_7357-1",
+		Time:            time.Now().UTC(),
+		ProposerAddress: althea.ValidatorPubKey.Address().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")),
+	})
+}
+
+func (suite *GenesisTestSuite) TestERC20InitGenesis() {
+	testCases := []struct {
+		name         string
+		genesisState types.GenesisState
+	}{
+		{
+			"empty genesis",
+			types.GenesisState{},
+		},
+		{
+			"default genesis",
+			*types.DefaultGenesisState(),
+		},
+		{
+			"custom genesis",
+			types.NewGenesisState(
+				types.DefaultParams(),
+				[]types.TokenPair{
+					{
+						Erc20Address:  "0x5dCA2483280D9727c80b5518faC4556617fb19ZZ",
+						Denom:         "coin",
+						Enabled:       true,
+						ContractOwner: types.OWNER_MODULE,
+					},
+				}),
+		},
+	}
+
+	for _, tc := range testCases {
+
+		suite.Require().NotPanics(func() {
+			erc20.InitGenesis(suite.ctx, *suite.app.Erc20Keeper, *suite.app.AccountKeeper, tc.genesisState)
+		})
+		params := suite.app.Erc20Keeper.GetParams(suite.ctx)
+
+		tokenPairs := suite.app.Erc20Keeper.GetTokenPairs(suite.ctx)
+		suite.Require().Equal(tc.genesisState.Params, params)
+		if len(tokenPairs) > 0 {
+			suite.Require().Equal(tc.genesisState.TokenPairs, tokenPairs)
+		} else {
+			suite.Require().Len(tc.genesisState.TokenPairs, 0)
+		}
+	}
+}
+
+func (suite *GenesisTestSuite) TestErc20ExportGenesis() {
+	testGenCases := []struct {
+		name         string
+		genesisState types.GenesisState
+	}{
+		{
+			"empty genesis",
+			types.GenesisState{},
+		},
+		{
+			"default genesis",
+			*types.DefaultGenesisState(),
+		},
+		{
+			"custom genesis",
+			types.NewGenesisState(
+				types.DefaultParams(),
+				[]types.TokenPair{
+					{
+						Erc20Address:  "0x5dCA2483280D9727c80b5518faC4556617fb19ZZ",
+						Denom:         "coin",
+						Enabled:       true,
+						ContractOwner: types.OWNER_MODULE,
+					},
+				}),
+		},
+	}
+
+	for _, tc := range testGenCases {
+		erc20.InitGenesis(suite.ctx, *suite.app.Erc20Keeper, *suite.app.AccountKeeper, tc.genesisState)
+		suite.Require().NotPanics(func() {
+			genesisExported := erc20.ExportGenesis(suite.ctx, *suite.app.Erc20Keeper)
+			params := suite.app.Erc20Keeper.GetParams(suite.ctx)
+			suite.Require().Equal(genesisExported.Params, params)
+
+			tokenPairs := suite.app.Erc20Keeper.GetTokenPairs(suite.ctx)
+			if len(tokenPairs) > 0 {
+				suite.Require().Equal(genesisExported.TokenPairs, tokenPairs)
+			} else {
+				suite.Require().Len(genesisExported.TokenPairs, 0)
+			}
+		})
+		// }
+	}
+}
diff --git a/x/erc20/handler.go b/x/erc20/handler.go
new file mode 100644
index 00000000..0ad2c3ca
--- /dev/null
+++ b/x/erc20/handler.go
@@ -0,0 +1,27 @@
+package erc20
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+// NewHandler defines the erc20 module handler instance
+func NewHandler(server types.MsgServer) sdk.Handler {
+	return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
+		ctx = ctx.WithEventManager(sdk.NewEventManager())
+
+		switch msg := msg.(type) {
+		case *types.MsgConvertCoin:
+			res, err := server.ConvertCoin(sdk.WrapSDKContext(ctx), msg)
+			return sdk.WrapServiceResult(ctx, res, err)
+		case *types.MsgConvertERC20:
+			res, err := server.ConvertERC20(sdk.WrapSDKContext(ctx), msg)
+			return sdk.WrapServiceResult(ctx, res, err)
+		default:
+			err := sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg)
+			return nil, err
+		}
+	}
+}
diff --git a/x/erc20/keeper/evm.go b/x/erc20/keeper/evm.go
new file mode 100644
index 00000000..5ded21dd
--- /dev/null
+++ b/x/erc20/keeper/evm.go
@@ -0,0 +1,241 @@
+package keeper
+
+import (
+	"encoding/json"
+	"math/big"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+	"github.com/ethereum/go-ethereum/accounts/abi"
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/hexutil"
+	ethtypes "github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/crypto"
+	"github.com/evmos/ethermint/server/config"
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+
+	"github.com/AltheaFoundation/althea-L1/contracts"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+// DeployERC20Contract creates and deploys an ERC20 contract on the EVM with the
+// erc20 module account as owner.
+func (k Keeper) DeployERC20Contract(
+	ctx sdk.Context,
+	coinMetadata banktypes.Metadata,
+) (common.Address, error) {
+	decimals := uint8(0)
+	if len(coinMetadata.DenomUnits) > 0 {
+		decimalsIdx := len(coinMetadata.DenomUnits) - 1
+		decimals = uint8(coinMetadata.DenomUnits[decimalsIdx].Exponent)
+	}
+	ctorArgs, err := contracts.ERC20MinterBurnerDecimalsContract.ABI.Pack(
+		"",
+		coinMetadata.Name,
+		coinMetadata.Symbol,
+		decimals,
+	)
+	if err != nil {
+		return common.Address{}, sdkerrors.Wrapf(types.ErrABIPack, "coin metadata is invalid %s: %s", coinMetadata.Name, err.Error())
+	}
+
+	data := make([]byte, len(contracts.ERC20MinterBurnerDecimalsContract.Bin)+len(ctorArgs))
+	copy(data[:len(contracts.ERC20MinterBurnerDecimalsContract.Bin)], contracts.ERC20MinterBurnerDecimalsContract.Bin)
+	copy(data[len(contracts.ERC20MinterBurnerDecimalsContract.Bin):], ctorArgs)
+
+	nonce, err := k.accountKeeper.GetSequence(ctx, types.ModuleAddress.Bytes())
+	if err != nil {
+		return common.Address{}, err
+	}
+
+	contractAddr := crypto.CreateAddress(types.ModuleAddress, nonce)
+	_, err = k.CallEVMWithData(ctx, types.ModuleAddress, nil, data, true)
+	if err != nil {
+		return common.Address{}, sdkerrors.Wrapf(err, "failed to deploy contract for %s", coinMetadata.Name)
+	}
+
+	return contractAddr, nil
+}
+
+// QueryERC20 returns the data of a deployed ERC20 contract
+func (k Keeper) QueryERC20(
+	ctx sdk.Context,
+	contract common.Address,
+) (types.ERC20Data, error) {
+	var (
+		nameRes    types.ERC20StringResponse
+		symbolRes  types.ERC20StringResponse
+		decimalRes types.ERC20Uint8Response
+	)
+
+	erc20 := contracts.ERC20MinterBurnerDecimalsContract.ABI
+
+	// Name
+	res, err := k.CallEVM(ctx, erc20, types.ModuleAddress, contract, false, "name")
+	if err != nil {
+		return types.ERC20Data{}, err
+	}
+
+	if err := erc20.UnpackIntoInterface(&nameRes, "name", res.Ret); err != nil {
+		return types.ERC20Data{}, sdkerrors.Wrapf(
+			types.ErrABIUnpack, "failed to unpack name: %s", err.Error(),
+		)
+	}
+
+	// Symbol
+	res, err = k.CallEVM(ctx, erc20, types.ModuleAddress, contract, false, "symbol")
+	if err != nil {
+		return types.ERC20Data{}, err
+	}
+
+	if err := erc20.UnpackIntoInterface(&symbolRes, "symbol", res.Ret); err != nil {
+		return types.ERC20Data{}, sdkerrors.Wrapf(
+			types.ErrABIUnpack, "failed to unpack symbol: %s", err.Error(),
+		)
+	}
+
+	// Decimals
+	res, err = k.CallEVM(ctx, erc20, types.ModuleAddress, contract, false, "decimals")
+	if err != nil {
+		return types.ERC20Data{}, err
+	}
+
+	if err := erc20.UnpackIntoInterface(&decimalRes, "decimals", res.Ret); err != nil {
+		return types.ERC20Data{}, sdkerrors.Wrapf(
+			types.ErrABIUnpack, "failed to unpack decimals: %s", err.Error(),
+		)
+	}
+
+	return types.NewERC20Data(nameRes.Value, symbolRes.Value, decimalRes.Value), nil
+}
+
+// BalanceOf queries an account's balance for a given ERC20 contract
+func (k Keeper) BalanceOf(
+	ctx sdk.Context,
+	abi abi.ABI,
+	contract, account common.Address,
+) *big.Int {
+	res, err := k.CallEVM(ctx, abi, types.ModuleAddress, contract, false, "balanceOf", account)
+	if err != nil {
+		return nil
+	}
+
+	unpacked, err := abi.Unpack("balanceOf", res.Ret)
+	if err != nil || len(unpacked) == 0 {
+		return nil
+	}
+
+	balance, ok := unpacked[0].(*big.Int)
+	if !ok {
+		return nil
+	}
+
+	return balance
+}
+
+// CallEVM performs a smart contract method call using given args
+func (k Keeper) CallEVM(
+	ctx sdk.Context,
+	abi abi.ABI,
+	from, contract common.Address,
+	commit bool,
+	method string,
+	args ...interface{},
+) (*evmtypes.MsgEthereumTxResponse, error) {
+	data, err := abi.Pack(method, args...)
+	if err != nil {
+		return nil, sdkerrors.Wrap(
+			types.ErrABIPack,
+			sdkerrors.Wrap(err, "failed to create transaction data").Error(),
+		)
+	}
+
+	resp, err := k.CallEVMWithData(ctx, from, &contract, data, commit)
+	if err != nil {
+		return nil, sdkerrors.Wrapf(err, "contract call failed: method '%s', contract '%s'", method, contract)
+	}
+	return resp, nil
+}
+
+// CallEVMWithData performs a smart contract method call using contract data
+func (k Keeper) CallEVMWithData(
+	ctx sdk.Context,
+	from common.Address,
+	contract *common.Address,
+	data []byte,
+	commit bool,
+) (*evmtypes.MsgEthereumTxResponse, error) {
+	nonce, err := k.accountKeeper.GetSequence(ctx, from.Bytes())
+	if err != nil {
+		return nil, err
+	}
+
+	gasCap := config.DefaultGasCap
+	if commit {
+		args, err := json.Marshal(evmtypes.TransactionArgs{
+			From: &from,
+			To:   contract,
+			Data: (*hexutil.Bytes)(&data),
+		})
+		if err != nil {
+			return nil, sdkerrors.Wrapf(sdkerrors.ErrJSONMarshal, "failed to marshal tx args: %s", err.Error())
+		}
+
+		gasRes, err := k.evmKeeper.EstimateGas(sdk.WrapSDKContext(ctx), &evmtypes.EthCallRequest{
+			Args:   args,
+			GasCap: config.DefaultGasCap,
+		})
+		if err != nil {
+			return nil, err
+		}
+		gasCap = gasRes.Gas
+	}
+
+	msg := ethtypes.NewMessage(
+		from,
+		contract,
+		nonce,
+		big.NewInt(0), // amount
+		gasCap,        // gasLimit
+		big.NewInt(0), // gasFeeCap
+		big.NewInt(0), // gasTipCap
+		big.NewInt(0), // gasPrice
+		data,
+		ethtypes.AccessList{}, // AccessList
+		!commit,               // isFake
+	)
+
+	res, err := k.evmKeeper.ApplyMessage(ctx, msg, evmtypes.NewNoOpTracer(), commit)
+	if err != nil {
+		return nil, err
+	}
+
+	if res.Failed() {
+		return nil, sdkerrors.Wrap(evmtypes.ErrVMExecution, res.VmError)
+	}
+
+	return res, nil
+}
+
+// monitorApprovalEvent returns an error if the given transactions logs include
+// an unexpected `Approval` event
+func (k Keeper) monitorApprovalEvent(res *evmtypes.MsgEthereumTxResponse) error {
+	if res == nil || len(res.Logs) == 0 {
+		return nil
+	}
+
+	logApprovalSig := []byte("Approval(address,address,uint256)")
+	logApprovalSigHash := crypto.Keccak256Hash(logApprovalSig)
+
+	for _, log := range res.Logs {
+		if log.Topics[0] == logApprovalSigHash.Hex() {
+			return sdkerrors.Wrapf(
+				types.ErrUnexpectedEvent, "unexpected Approval event",
+			)
+		}
+	}
+
+	return nil
+}
diff --git a/x/erc20/keeper/evm_hooks.go b/x/erc20/keeper/evm_hooks.go
new file mode 100644
index 00000000..2736224d
--- /dev/null
+++ b/x/erc20/keeper/evm_hooks.go
@@ -0,0 +1,157 @@
+package keeper
+
+import (
+	"bytes"
+	// nolint: typecheck
+	"math/big"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core"
+	ethtypes "github.com/ethereum/go-ethereum/core/types"
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+
+	"github.com/AltheaFoundation/althea-L1/contracts"
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+var _ evmtypes.EvmHooks = Hooks{}
+
+// Hooks wrapper struct for erc20 keeper
+type Hooks struct {
+	k Keeper
+}
+
+// Return the wrapper struct
+func (k Keeper) Hooks() Hooks {
+	return Hooks{k}
+}
+
+// PostTxProcessing implements EvmHooks.PostTxProcessing. The EVM hooks allows
+// users to convert ERC20s to Cosmos Coins by sending an Ethereum tx transfer to
+// the module account address. This hook applies to both token pairs that have
+// been registered through a native Cosmos coin or an ERC20 token. If token pair
+// has been registered with:
+//   - coin -> burn tokens and transfer escrowed coins on module to sender
+//   - token -> escrow tokens on module account and mint & transfer coins to sender
+//
+// Note that the PostTxProcessing hook is only called by sending an EVM
+// transaction that triggers `ApplyTransaction`. A cosmos tx with a
+// `ConvertERC20` msg does not trigger the hook as it only calls `ApplyMessage`.
+func (h Hooks) PostTxProcessing(
+	ctx sdk.Context,
+	msg core.Message,
+	receipt *ethtypes.Receipt,
+) error {
+	params := h.k.GetParams(ctx)
+	if !params.EnableErc20 || !params.EnableEVMHook {
+		// no error is returned to avoid reverting the tx and allow for other post
+		// processing txs to pass and
+		return nil
+	}
+
+	erc20 := contracts.ERC20MinterBurnerDecimalsContract.ABI
+
+	for i, log := range receipt.Logs {
+		// Note: the `Transfer` event contains 3 topics (id, from, to)
+		if len(log.Topics) != 3 {
+			continue
+		}
+
+		// Check if event is included in ERC20
+		eventID := log.Topics[0]
+		event, err := erc20.EventByID(eventID)
+		if err != nil {
+			continue
+		}
+
+		// Check if event is a `Transfer` event.
+		if event.Name != types.ERC20EventTransfer {
+			h.k.Logger(ctx).Info("emitted event", "name", event.Name, "signature", event.Sig)
+			continue
+		}
+
+		transferEvent, err := erc20.Unpack(event.Name, log.Data)
+		if err != nil {
+			h.k.Logger(ctx).Error("failed to unpack transfer event", "error", err.Error())
+			continue
+		}
+
+		if len(transferEvent) == 0 {
+			continue
+		}
+
+		tokens, ok := transferEvent[0].(*big.Int)
+		// safety check and ignore if amount not positive
+		if !ok || tokens == nil || tokens.Sign() != 1 {
+			continue
+		}
+
+		// Check that the contract is a registered token pair
+		contractAddr := log.Address
+		id := h.k.GetERC20Map(ctx, contractAddr)
+		if len(id) == 0 {
+			continue
+		}
+
+		pair, found := h.k.GetTokenPair(ctx, id)
+		if !found {
+			continue
+		}
+
+		// Check if tokens are sent to module address
+		to := common.BytesToAddress(log.Topics[2].Bytes())
+		if !bytes.Equal(to.Bytes(), types.ModuleAddress.Bytes()) {
+			continue
+		}
+
+		// Check that conversion for the pair is enabled. Fail
+		if !pair.Enabled {
+			// continue to allow transfers for the ERC20 in case the token pair is
+			// disabled
+			h.k.Logger(ctx).Debug(
+				"ERC20 token -> Cosmos coin conversion is disabled for pair",
+				"coin", pair.Denom, "contract", pair.Erc20Address,
+			)
+			continue
+		}
+
+		// create the corresponding sdk.Coin that is paired with ERC20
+		coins := sdk.Coins{{Denom: pair.Denom, Amount: sdk.NewIntFromBigInt(tokens)}}
+
+		// Perform token conversion. We can now assume that the sender of a
+		// registered token wants to mint a Cosmos coin.
+		switch pair.ContractOwner {
+		case types.OWNER_MODULE:
+			_, err = h.k.CallEVM(ctx, erc20, types.ModuleAddress, contractAddr, true, "burn", tokens)
+		case types.OWNER_EXTERNAL:
+			err = h.k.bankKeeper.MintCoins(ctx, types.ModuleName, coins)
+		default:
+			err = types.ErrUndefinedOwner
+		}
+
+		if err != nil {
+			h.k.Logger(ctx).Debug(
+				"failed to process EVM hook for ER20 -> coin conversion",
+				"coin", pair.Denom, "contract", pair.Erc20Address, "error", err.Error(),
+			)
+			continue
+		}
+
+		// Only need last 20 bytes from log.topics
+		from := common.BytesToAddress(log.Topics[1].Bytes())
+		recipient := sdk.AccAddress(from.Bytes())
+
+		// transfer the tokens from ModuleAccount to sender address
+		if err := h.k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, recipient, coins); err != nil {
+			h.k.Logger(ctx).Debug(
+				"failed to process EVM hook for ER20 -> coin conversion",
+				"tx-hash", receipt.TxHash.Hex(), "log-idx", i,
+				"coin", pair.Denom, "contract", pair.Erc20Address, "error", err.Error(),
+			)
+			continue
+		}
+	}
+
+	return nil
+}
diff --git a/x/erc20/keeper/evm_hooks_test.go b/x/erc20/keeper/evm_hooks_test.go
new file mode 100644
index 00000000..174dc2e8
--- /dev/null
+++ b/x/erc20/keeper/evm_hooks_test.go
@@ -0,0 +1,431 @@
+package keeper_test
+
+import (
+	"fmt"
+	"math/big"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/ethereum/go-ethereum/common"
+	ethtypes "github.com/ethereum/go-ethereum/core/types"
+
+	"github.com/AltheaFoundation/althea-L1/contracts"
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+	"github.com/evmos/ethermint/tests"
+)
+
+// ensureHooksSet tries to set the hooks on EVMKeeper, this will fail if the erc20 hook is already set
+func (suite *KeeperTestSuite) ensureHooksSet() {
+	// TODO: PR to Ethermint to add the functionality `GetHooks` or `areHooksSet` to avoid catching a panic
+	defer func() {
+		err := recover()
+		suite.Require().NotNil(err)
+	}()
+	suite.app.EvmKeeper.SetHooks(suite.app.Erc20Keeper.Hooks())
+}
+
+func (suite *KeeperTestSuite) TestEvmHooksRegisteredERC20() {
+	testCases := []struct {
+		name     string
+		malleate func(common.Address)
+		result   bool
+	}{
+		{
+			"correct execution",
+			func(contractAddr common.Address) {
+				_, err := suite.app.Erc20Keeper.RegisterERC20(suite.ctx, contractAddr)
+				suite.Require().NoError(err)
+
+				// Mint 10 tokens to suite.address (owner)
+				_ = suite.MintERC20Token(contractAddr, suite.address, suite.address, big.NewInt(10))
+				suite.Commit()
+
+				// Burn the 10 tokens of suite.address (owner)
+				_ = suite.TransferERC20TokenToModule(contractAddr, suite.address, big.NewInt(10))
+			},
+			true,
+		},
+		{
+			"unregistered pair",
+			func(contractAddr common.Address) {
+				// Mint 10 tokens to suite.address (owner)
+				_ = suite.MintERC20Token(contractAddr, suite.address, suite.address, big.NewInt(10))
+				suite.Commit()
+
+				// Burn the 10 tokens of suite.address (owner)
+				_ = suite.TransferERC20TokenToModule(contractAddr, suite.address, big.NewInt(10))
+			},
+			false,
+		},
+		{
+			"wrong event",
+			func(contractAddr common.Address) {
+				_, err := suite.app.Erc20Keeper.RegisterERC20(suite.ctx, contractAddr)
+				suite.Require().NoError(err)
+
+				// Mint 10 tokens to suite.address (owner)
+				_ = suite.MintERC20Token(contractAddr, suite.address, suite.address, big.NewInt(10))
+			},
+			false,
+		},
+		{
+			"Pair is disabled",
+			func(contractAddr common.Address) {
+				pair, err := suite.app.Erc20Keeper.RegisterERC20(suite.ctx, contractAddr)
+				suite.Require().NoError(err)
+
+				pair.Enabled = false
+				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, *pair)
+				// Mint 10 tokens to suite.address (owner)
+				_ = suite.MintERC20Token(contractAddr, suite.address, suite.address, big.NewInt(10))
+				suite.Commit()
+
+				// Burn the 10 tokens of suite.address (owner)
+				_ = suite.TransferERC20TokenToModule(contractAddr, suite.address, big.NewInt(10))
+			},
+			false,
+		},
+		{
+			"Pair is incorrectly loaded",
+			func(contractAddr common.Address) {
+				pair, err := suite.app.Erc20Keeper.RegisterERC20(suite.ctx, contractAddr)
+				suite.Require().NoError(err)
+
+				suite.app.Erc20Keeper.DeleteTokenPair(suite.ctx, *pair)
+
+				suite.app.Erc20Keeper.SetDenomMap(suite.ctx, pair.Denom, pair.GetID())
+				suite.app.Erc20Keeper.SetERC20Map(suite.ctx, pair.GetERC20Contract(), pair.GetID())
+				// Mint 10 tokens to suite.address (owner)
+				_ = suite.MintERC20Token(contractAddr, suite.address, suite.address, big.NewInt(10))
+				suite.Commit()
+
+				// Burn the 10 tokens of suite.address (owner)
+				_ = suite.TransferERC20TokenToModule(contractAddr, suite.address, big.NewInt(10))
+			},
+			false,
+		},
+	}
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.mintFeeCollector = true
+			suite.SetupTest()
+
+			suite.ensureHooksSet()
+
+			contractAddr, err := suite.DeployContract("coin test erc20", "token", erc20Decimals)
+			suite.Require().NoError(err)
+			suite.Commit()
+
+			tc.malleate(contractAddr)
+
+			balance := suite.app.BankKeeper.GetBalance(suite.ctx, sdk.AccAddress(suite.address.Bytes()), types.CreateDenom(contractAddr.String()))
+			suite.Commit()
+			if tc.result {
+				// Check if the execution was successful
+				suite.Require().Equal(int64(10), balance.Amount.Int64())
+			} else {
+				// Check that no changes were made to the account
+				suite.Require().Equal(int64(0), balance.Amount.Int64())
+			}
+		})
+	}
+	suite.mintFeeCollector = false
+}
+
+func (suite *KeeperTestSuite) TestEvmHooksRegisteredCoin() {
+	testCases := []struct {
+		name      string
+		mint      int64
+		burn      int64
+		reconvert int64
+
+		result bool
+	}{
+		{"correct execution", 100, 10, 5, true},
+	}
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.mintFeeCollector = true
+			suite.SetupTest()
+
+			suite.ensureHooksSet()
+
+			metadata, pair := suite.setupRegisterCoin()
+			suite.Require().NotNil(metadata)
+			suite.Require().NotNil(pair)
+
+			sender := sdk.AccAddress(suite.address.Bytes())
+			contractAddr := common.HexToAddress(pair.Erc20Address)
+
+			coins := sdk.NewCoins(sdk.NewCoin(cosmosTokenBase, sdk.NewInt(tc.mint)))
+			suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
+			suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+
+			convertCoin := types.NewMsgConvertCoin(
+				sdk.NewCoin(cosmosTokenBase, sdk.NewInt(tc.burn)),
+				suite.address,
+				sender,
+			)
+
+			ctx := sdk.WrapSDKContext(suite.ctx)
+			_, err := suite.app.Erc20Keeper.ConvertCoin(ctx, convertCoin)
+			suite.Require().NoError(err, tc.name)
+			suite.Commit()
+
+			balance := suite.BalanceOf(common.HexToAddress(pair.Erc20Address), suite.address)
+			cosmosBalance := suite.app.BankKeeper.GetBalance(suite.ctx, sender, metadata.Base)
+			suite.Require().Equal(cosmosBalance.Amount.Int64(), sdk.NewInt(tc.mint-tc.burn).Int64())
+			suite.Require().Equal(balance, big.NewInt(tc.burn))
+
+			// Burn the 10 tokens of suite.address (owner)
+			_ = suite.TransferERC20TokenToModule(contractAddr, suite.address, big.NewInt(tc.reconvert))
+
+			balance = suite.BalanceOf(common.HexToAddress(pair.Erc20Address), suite.address)
+			cosmosBalance = suite.app.BankKeeper.GetBalance(suite.ctx, sender, metadata.Base)
+
+			if tc.result {
+				// Check if the execution was successful
+				suite.Require().NoError(err)
+				suite.Require().Equal(cosmosBalance.Amount, sdk.NewInt(tc.mint-tc.burn+tc.reconvert))
+			} else {
+				// Check that no changes were made to the account
+				suite.Require().Error(err)
+				suite.Require().Equal(cosmosBalance.Amount, sdk.NewInt(tc.mint-tc.burn))
+			}
+		})
+	}
+	suite.mintFeeCollector = false
+}
+
+func (suite *KeeperTestSuite) TestPostTxProcessing() {
+	var (
+		receipt *ethtypes.Receipt
+		pair    *types.TokenPair
+	)
+
+	msg := ethtypes.NewMessage(
+		types.ModuleAddress,
+		&common.Address{},
+		0,
+		big.NewInt(0), // amount
+		uint64(0),     // gasLimit
+		big.NewInt(0), // gasFeeCap
+		big.NewInt(0), // gasTipCap
+		big.NewInt(0), // gasPrice
+		[]byte{},
+		ethtypes.AccessList{}, // AccessList
+		true,                  // checkNonce
+	)
+
+	account := tests.GenerateAddress()
+
+	transferData := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+	transferData[31] = uint8(10)
+	erc20 := contracts.ERC20BurnableContract.ABI
+
+	transferEvent := erc20.Events["Transfer"]
+
+	testCases := []struct {
+		name          string
+		malleate      func()
+		expConversion bool
+	}{
+		{
+			"Empty logs",
+			func() {
+				log := ethtypes.Log{}
+				receipt = &ethtypes.Receipt{
+					Logs: []*ethtypes.Log{&log},
+				}
+			},
+			false,
+		},
+		{
+			"No log data",
+			func() {
+				topics := []common.Hash{transferEvent.ID, account.Hash(), types.ModuleAddress.Hash()}
+				log := ethtypes.Log{
+					Topics: topics,
+				}
+				receipt = &ethtypes.Receipt{
+					Logs: []*ethtypes.Log{&log},
+				}
+			},
+			false,
+		},
+		{
+			"Non recognized event",
+			func() {
+				topics := []common.Hash{{}, account.Hash(), account.Hash()}
+				log := ethtypes.Log{
+					Topics: topics,
+					Data:   transferData,
+				}
+				receipt = &ethtypes.Receipt{
+					Logs: []*ethtypes.Log{&log},
+				}
+			},
+			false,
+		},
+		{
+			"Non transfer event",
+			func() {
+				aprovalEvent := erc20.Events["Approval"]
+				topics := []common.Hash{aprovalEvent.ID, account.Hash(), account.Hash()}
+				log := ethtypes.Log{
+					Topics: topics,
+					Data:   transferData,
+				}
+				receipt = &ethtypes.Receipt{
+					Logs: []*ethtypes.Log{&log},
+				}
+			},
+			false,
+		},
+		{
+			"No log address",
+			func() {
+				topics := []common.Hash{transferEvent.ID, account.Hash(), types.ModuleAddress.Hash()}
+				log := ethtypes.Log{
+					Topics: topics,
+					Data:   transferData,
+				}
+				receipt = &ethtypes.Receipt{
+					Logs: []*ethtypes.Log{&log},
+				}
+			},
+			false,
+		},
+		{
+			"No data on topic",
+			func() {
+				topics := []common.Hash{transferEvent.ID}
+				log := ethtypes.Log{
+					Topics: topics,
+					Data:   transferData,
+				}
+				receipt = &ethtypes.Receipt{
+					Logs: []*ethtypes.Log{&log},
+				}
+			},
+			false,
+		},
+		{
+			"transfer to non-evm-module account",
+			func() {
+				contractAddr, err := suite.DeployContract("coin", "token", erc20Decimals)
+				suite.Require().NoError(err)
+				suite.Commit()
+
+				_, err = suite.app.Erc20Keeper.RegisterERC20(suite.ctx, contractAddr)
+				suite.Require().NoError(err)
+
+				topics := []common.Hash{transferEvent.ID, account.Hash(), account.Hash()}
+				log := ethtypes.Log{
+					Topics:  topics,
+					Data:    transferData,
+					Address: contractAddr,
+				}
+				receipt = &ethtypes.Receipt{
+					Logs: []*ethtypes.Log{&log},
+				}
+			},
+			false,
+		},
+		{
+			"correct burn",
+			func() {
+				contractAddr, err := suite.DeployContract("coin", "token", erc20Decimals)
+				suite.Require().NoError(err)
+				suite.Commit()
+
+				pair, err = suite.app.Erc20Keeper.RegisterERC20(suite.ctx, contractAddr)
+				suite.Require().NoError(err)
+
+				topics := []common.Hash{transferEvent.ID, account.Hash(), types.ModuleAddress.Hash()}
+				log := ethtypes.Log{
+					Topics:  topics,
+					Data:    transferData,
+					Address: contractAddr,
+				}
+				receipt = &ethtypes.Receipt{
+					Logs: []*ethtypes.Log{&log},
+				}
+			},
+			true,
+		},
+		{
+			"Unspecified Owner",
+			func() {
+				contractAddr, err := suite.DeployContract("coin", "token", erc20Decimals)
+				suite.Require().NoError(err)
+				suite.Commit()
+
+				pair, err := suite.app.Erc20Keeper.RegisterERC20(suite.ctx, contractAddr)
+				suite.Require().NoError(err)
+
+				pair.ContractOwner = types.OWNER_UNSPECIFIED
+				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, *pair)
+
+				topics := []common.Hash{transferEvent.ID, account.Hash(), types.ModuleAddress.Hash()}
+				log := ethtypes.Log{
+					Topics:  topics,
+					Data:    transferData,
+					Address: contractAddr,
+				}
+				receipt = &ethtypes.Receipt{
+					Logs: []*ethtypes.Log{&log},
+				}
+			},
+			false,
+		},
+		{
+			"Fail Evm",
+			func() {
+				contractAddr, err := suite.DeployContract("coin", "token", erc20Decimals)
+				suite.Require().NoError(err)
+				suite.Commit()
+
+				pair, err := suite.app.Erc20Keeper.RegisterERC20(suite.ctx, contractAddr)
+				suite.Require().NoError(err)
+
+				pair.ContractOwner = types.OWNER_MODULE
+				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, *pair)
+
+				topics := []common.Hash{transferEvent.ID, account.Hash(), types.ModuleAddress.Hash()}
+				log := ethtypes.Log{
+					Topics:  topics,
+					Data:    transferData,
+					Address: contractAddr,
+				}
+				receipt = &ethtypes.Receipt{
+					Logs: []*ethtypes.Log{&log},
+				}
+			},
+			false,
+		},
+	}
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.mintFeeCollector = true
+			suite.SetupTest()
+			suite.ensureHooksSet()
+
+			tc.malleate()
+
+			err := suite.app.Erc20Keeper.Hooks().PostTxProcessing(suite.ctx, msg, receipt)
+			suite.Require().NoError(err)
+
+			if tc.expConversion {
+				sender := sdk.AccAddress(account.Bytes())
+				cosmosBalance := suite.app.BankKeeper.GetBalance(suite.ctx, sender, pair.Denom)
+
+				transferEvent, err := erc20.Unpack("Transfer", transferData)
+				suite.Require().NoError(err)
+
+				tokens, _ := transferEvent[0].(*big.Int)
+				suite.Require().Equal(cosmosBalance.Amount.String(), tokens.String())
+			}
+		})
+	}
+	suite.mintFeeCollector = false
+}
diff --git a/x/erc20/keeper/evm_test.go b/x/erc20/keeper/evm_test.go
new file mode 100644
index 00000000..7cc7391e
--- /dev/null
+++ b/x/erc20/keeper/evm_test.go
@@ -0,0 +1,386 @@
+package keeper_test
+
+import (
+	"fmt"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/evmos/ethermint/tests"
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+	"github.com/stretchr/testify/mock"
+
+	"github.com/AltheaFoundation/althea-L1/contracts"
+	"github.com/AltheaFoundation/althea-L1/x/erc20/keeper"
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+func (suite *KeeperTestSuite) TestQueryERC20() {
+	var contract common.Address
+	testCases := []struct {
+		name     string
+		malleate func()
+		res      bool
+	}{
+		{
+			"erc20 not deployed",
+			func() { contract = common.Address{} },
+			false,
+		},
+		{
+			"ok",
+			func() { contract, _ = suite.DeployContract("coin", "token", erc20Decimals) },
+			true,
+		},
+	}
+	for _, tc := range testCases {
+		suite.SetupTest() // reset
+
+		tc.malleate()
+
+		res, err := suite.app.Erc20Keeper.QueryERC20(suite.ctx, contract)
+		if tc.res {
+			suite.Require().NoError(err)
+			suite.Require().Equal(
+				types.ERC20Data{Name: "coin", Symbol: "token", Decimals: erc20Decimals},
+				res,
+			)
+		} else {
+			suite.Require().Error(err)
+		}
+	}
+}
+
+func (suite *KeeperTestSuite) TestBalanceOf() {
+	var mockEVMKeeper *MockEVMKeeper
+	contract := tests.GenerateAddress()
+	testCases := []struct {
+		name       string
+		malleate   func()
+		expBalance int64
+		res        bool
+	}{
+		{
+			"Failed to call Evm",
+			func() {
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("forced ApplyMessage error"))
+			},
+			int64(0),
+			false,
+		},
+		{
+			"Incorrect res",
+			func() {
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: []uint8{0, 0}}, nil).Once()
+			},
+			int64(0),
+			false,
+		},
+		{
+			"Correct Execution",
+			func() {
+				balance := make([]uint8, 32)
+				balance[31] = uint8(10)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+			},
+			int64(10),
+			true,
+		},
+	}
+	for _, tc := range testCases {
+		suite.SetupTest() // reset
+		mockEVMKeeper = &MockEVMKeeper{}
+		sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+		suite.Require().True(found)
+		erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+		suite.app.Erc20Keeper = &erc20Keeper
+
+		tc.malleate()
+
+		abi := contracts.ERC20BurnableContract.ABI
+		balance := suite.app.Erc20Keeper.BalanceOf(suite.ctx, abi, contract, tests.GenerateAddress())
+		if tc.res {
+			suite.Require().Equal(balance.Int64(), tc.expBalance)
+		} else {
+			suite.Require().Nil(balance)
+		}
+	}
+}
+
+func (suite *KeeperTestSuite) TestCallEVM() {
+	testCases := []struct {
+		name    string
+		method  string
+		expPass bool
+	}{
+		{
+			"unknown method",
+			"",
+			false,
+		},
+		{
+			"pass",
+			"balanceOf",
+			true,
+		},
+	}
+	for _, tc := range testCases {
+		suite.SetupTest() // reset
+
+		erc20 := contracts.ERC20MinterBurnerDecimalsContract.ABI
+		contract, err := suite.DeployContract("coin", "token", erc20Decimals)
+		suite.Require().NoError(err)
+		account := tests.GenerateAddress()
+
+		res, err := suite.app.Erc20Keeper.CallEVM(suite.ctx, erc20, types.ModuleAddress, contract, true, tc.method, account)
+		if tc.expPass {
+			suite.Require().IsTypef(&evmtypes.MsgEthereumTxResponse{}, res, tc.name)
+			suite.Require().NoError(err)
+		} else {
+			suite.Require().Error(err)
+		}
+	}
+}
+
+func (suite *KeeperTestSuite) TestCallEVMWithData() {
+	erc20 := contracts.ERC20MinterBurnerDecimalsContract.ABI
+	testCases := []struct {
+		name     string
+		from     common.Address
+		malleate func() ([]byte, *common.Address)
+		expPass  bool
+	}{
+		{
+			"unknown method",
+			types.ModuleAddress,
+			func() ([]byte, *common.Address) {
+				contract, err := suite.DeployContract("coin", "token", erc20Decimals)
+				suite.Require().NoError(err)
+				account := tests.GenerateAddress()
+				data, _ := erc20.Pack("", account)
+				return data, &contract
+			},
+			false,
+		},
+		{
+			"pass",
+			types.ModuleAddress,
+			func() ([]byte, *common.Address) {
+				contract, err := suite.DeployContract("coin", "token", erc20Decimals)
+				suite.Require().NoError(err)
+				account := tests.GenerateAddress()
+				data, _ := erc20.Pack("balanceOf", account)
+				return data, &contract
+			},
+			true,
+		},
+		{
+			"fail empty data",
+			types.ModuleAddress,
+			func() ([]byte, *common.Address) {
+				contract, err := suite.DeployContract("coin", "token", erc20Decimals)
+				suite.Require().NoError(err)
+				return []byte{}, &contract
+			},
+			false,
+		},
+
+		{
+			"fail empty sender",
+			common.Address{},
+			func() ([]byte, *common.Address) {
+				contract, err := suite.DeployContract("coin", "token", erc20Decimals)
+				suite.Require().NoError(err)
+				return []byte{}, &contract
+			},
+			false,
+		},
+		{
+			"deploy",
+			types.ModuleAddress,
+			func() ([]byte, *common.Address) {
+				ctorArgs, _ := contracts.ERC20MinterBurnerDecimalsContract.ABI.Pack("", "test", "test", uint8(18))
+				data := append(contracts.ERC20MinterBurnerDecimalsContract.Bin, ctorArgs...)
+				return data, nil
+			},
+			true,
+		},
+		{
+			"fail deploy",
+			types.ModuleAddress,
+			func() ([]byte, *common.Address) {
+				params := suite.app.EvmKeeper.GetParams(suite.ctx)
+				params.EnableCreate = false
+				suite.app.EvmKeeper.SetParams(suite.ctx, params)
+				ctorArgs, _ := contracts.ERC20MinterBurnerDecimalsContract.ABI.Pack("", "test", "test", uint8(18))
+				data := append(contracts.ERC20MinterBurnerDecimalsContract.Bin, ctorArgs...)
+				return data, nil
+			},
+			false,
+		},
+	}
+
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.SetupTest() // reset
+
+			data, contract := tc.malleate()
+
+			res, err := suite.app.Erc20Keeper.CallEVMWithData(suite.ctx, tc.from, contract, data, true)
+			if tc.expPass {
+				suite.Require().IsTypef(&evmtypes.MsgEthereumTxResponse{}, res, tc.name)
+				suite.Require().NoError(err)
+			} else {
+				suite.Require().Error(err)
+			}
+		})
+	}
+}
+
+func (suite *KeeperTestSuite) TestForceFail() {
+	var mockEVMKeeper *MockEVMKeeper
+	erc20 := contracts.ERC20MinterBurnerDecimalsContract.ABI
+	testCases := []struct {
+		name     string
+		malleate func()
+		commit   bool
+		expPass  bool
+	}{
+		{
+			"Force estimate gas error",
+			func() {
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("forced EstimateGas error"))
+			},
+			true,
+			false,
+		},
+		{
+			"Force ApplyMessage error",
+			func() {
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("forced ApplyMessage error"))
+			},
+			true,
+			false,
+		},
+		{
+			"Force ApplyMessage failed",
+			func() {
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{VmError: "SomeError"}, nil)
+			},
+			true,
+			false,
+		},
+	}
+
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.SetupTest() // reset
+			mockEVMKeeper = &MockEVMKeeper{}
+			sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+			suite.Require().True(found)
+			erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+			suite.app.Erc20Keeper = &erc20Keeper
+
+			tc.malleate()
+
+			contract, err := suite.DeployContract("coin", "token", erc20Decimals)
+			suite.Require().NoError(err)
+			account := tests.GenerateAddress()
+			data, _ := erc20.Pack("balanceOf", account)
+
+			res, err := suite.app.Erc20Keeper.CallEVMWithData(suite.ctx, types.ModuleAddress, &contract, data, tc.commit)
+			if tc.expPass {
+				suite.Require().IsTypef(&evmtypes.MsgEthereumTxResponse{}, res, tc.name)
+				suite.Require().NoError(err)
+			} else {
+				suite.Require().Error(err)
+			}
+		})
+	}
+}
+
+func (suite *KeeperTestSuite) TestQueryERC20ForceFail() {
+	var mockEVMKeeper *MockEVMKeeper
+	contract := tests.GenerateAddress()
+	testCases := []struct {
+		name     string
+		malleate func()
+		res      bool
+	}{
+		{
+			"Failed to call Evm",
+			func() {
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("forced ApplyMessage error"))
+			},
+			false,
+		},
+		{
+			"Incorrect res",
+			func() {
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: []uint8{0, 0}}, nil).Once()
+			},
+			false,
+		},
+		{
+			"Correct res for name - incorrect for symbol",
+			func() {
+				ret := []uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 67, 111, 105, 110, 32, 84, 111, 107, 101, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: ret}, nil).Once()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{VmError: "Error"}, nil).Once()
+			},
+			false,
+		},
+		{
+			"incorrect symbol res",
+			func() {
+				ret := []uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 67, 111, 105, 110, 32, 84, 111, 107, 101, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: ret}, nil).Once()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: []uint8{0, 0}}, nil).Once()
+			},
+			false,
+		},
+		{
+			"Correct res for name - incorrect for symbol",
+			func() {
+				ret := []uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 67, 111, 105, 110, 32, 84, 111, 107, 101, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+				retSymbol := []uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 67, 84, 75, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: ret}, nil).Once()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: retSymbol}, nil).Once()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{VmError: "Error"}, nil).Once()
+			},
+			false,
+		},
+		{
+			"incorrect symbol res",
+			func() {
+				ret := []uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 67, 111, 105, 110, 32, 84, 111, 107, 101, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+				retSymbol := []uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 67, 84, 75, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: ret}, nil).Once()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: retSymbol}, nil).Once()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: []uint8{0, 0}}, nil).Once()
+			},
+			false,
+		},
+	}
+	for _, tc := range testCases {
+		suite.SetupTest() // reset
+		mockEVMKeeper = &MockEVMKeeper{}
+		sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+		suite.Require().True(found)
+		erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+		suite.app.Erc20Keeper = &erc20Keeper
+
+		tc.malleate()
+
+		res, err := suite.app.Erc20Keeper.QueryERC20(suite.ctx, contract)
+		if tc.res {
+			suite.Require().NoError(err)
+			suite.Require().Equal(
+				types.ERC20Data{Name: "coin", Symbol: "token", Decimals: erc20Decimals},
+				res,
+			)
+		} else {
+			suite.Require().Error(err)
+		}
+	}
+}
diff --git a/x/erc20/keeper/grpc_query.go b/x/erc20/keeper/grpc_query.go
new file mode 100644
index 00000000..4c318be7
--- /dev/null
+++ b/x/erc20/keeper/grpc_query.go
@@ -0,0 +1,85 @@
+package keeper
+
+import (
+	"context"
+
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+
+	"github.com/cosmos/cosmos-sdk/store/prefix"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/query"
+	ethermint "github.com/evmos/ethermint/types"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+var _ types.QueryServer = Keeper{}
+
+// TokenPairs returns all registered pairs
+func (k Keeper) TokenPairs(c context.Context, req *types.QueryTokenPairsRequest) (*types.QueryTokenPairsResponse, error) {
+	if req == nil {
+		return nil, status.Error(codes.InvalidArgument, "empty request")
+	}
+
+	ctx := sdk.UnwrapSDKContext(c)
+
+	var pairs []types.TokenPair
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixTokenPair)
+
+	pageRes, err := query.Paginate(store, req.Pagination, func(_, value []byte) error {
+		var pair types.TokenPair
+		if err := k.cdc.Unmarshal(value, &pair); err != nil {
+			return err
+		}
+		pairs = append(pairs, pair)
+		return nil
+	})
+	if err != nil {
+		return nil, status.Error(codes.Internal, err.Error())
+	}
+	return &types.QueryTokenPairsResponse{
+		TokenPairs: pairs,
+		Pagination: pageRes,
+	}, nil
+}
+
+// TokenPair returns a given registered token pair
+func (k Keeper) TokenPair(c context.Context, req *types.QueryTokenPairRequest) (*types.QueryTokenPairResponse, error) {
+	if req == nil {
+		return nil, status.Error(codes.InvalidArgument, "empty request")
+	}
+
+	ctx := sdk.UnwrapSDKContext(c)
+
+	// check if the token is a hex address, if not, check if it is a valid SDK
+	// denom
+	if err := ethermint.ValidateAddress(req.Token); err != nil {
+		if err := sdk.ValidateDenom(req.Token); err != nil {
+			return nil, status.Errorf(
+				codes.InvalidArgument,
+				"invalid format for token %s, should be either hex ('0x...') cosmos denom", req.Token,
+			)
+		}
+	}
+
+	id := k.GetTokenPairID(ctx, req.Token)
+
+	if len(id) == 0 {
+		return nil, status.Errorf(codes.NotFound, "token pair with token '%s'", req.Token)
+	}
+
+	pair, found := k.GetTokenPair(ctx, id)
+	if !found {
+		return nil, status.Errorf(codes.NotFound, "token pair with token '%s'", req.Token)
+	}
+
+	return &types.QueryTokenPairResponse{TokenPair: pair}, nil
+}
+
+// Params returns the params of the erc20 module
+func (k Keeper) Params(c context.Context, _ *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
+	ctx := sdk.UnwrapSDKContext(c)
+	params := k.GetParams(ctx)
+	return &types.QueryParamsResponse{Params: params}, nil
+}
diff --git a/x/erc20/keeper/grpc_query_test.go b/x/erc20/keeper/grpc_query_test.go
new file mode 100644
index 00000000..9da65f57
--- /dev/null
+++ b/x/erc20/keeper/grpc_query_test.go
@@ -0,0 +1,170 @@
+package keeper_test
+
+import (
+	"fmt"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/query"
+	"github.com/evmos/ethermint/tests"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+func (suite *KeeperTestSuite) TestTokenPairs() {
+	var (
+		req    *types.QueryTokenPairsRequest
+		expRes *types.QueryTokenPairsResponse
+	)
+
+	testCases := []struct {
+		name     string
+		malleate func()
+		expPass  bool
+	}{
+		{
+			"no pairs registered",
+			func() {
+				req = &types.QueryTokenPairsRequest{}
+				expRes = &types.QueryTokenPairsResponse{Pagination: &query.PageResponse{}}
+			},
+			true,
+		},
+		{
+			"1 pair registered w/pagination",
+			func() {
+				req = &types.QueryTokenPairsRequest{
+					Pagination: &query.PageRequest{Limit: 10, CountTotal: true},
+				}
+				pair := types.NewTokenPair(tests.GenerateAddress(), "coin", true, types.OWNER_MODULE)
+				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, pair)
+
+				expRes = &types.QueryTokenPairsResponse{
+					Pagination: &query.PageResponse{Total: 1},
+					TokenPairs: []types.TokenPair{pair},
+				}
+			},
+			true,
+		},
+		{
+			"2 pairs registered wo/pagination",
+			func() {
+				req = &types.QueryTokenPairsRequest{}
+				pair := types.NewTokenPair(tests.GenerateAddress(), "coin", true, types.OWNER_MODULE)
+				pair2 := types.NewTokenPair(tests.GenerateAddress(), "coin2", true, types.OWNER_MODULE)
+				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, pair)
+				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, pair2)
+
+				expRes = &types.QueryTokenPairsResponse{
+					Pagination: &query.PageResponse{Total: 2},
+					TokenPairs: []types.TokenPair{pair, pair2},
+				}
+			},
+			true,
+		},
+	}
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.SetupTest() // reset
+
+			ctx := sdk.WrapSDKContext(suite.ctx)
+			tc.malleate()
+
+			res, err := suite.queryClient.TokenPairs(ctx, req)
+			if tc.expPass {
+				suite.Require().NoError(err)
+				suite.Require().Equal(expRes.Pagination, res.Pagination)
+				suite.Require().ElementsMatch(expRes.TokenPairs, res.TokenPairs)
+			} else {
+				suite.Require().Error(err)
+			}
+		})
+	}
+}
+
+func (suite *KeeperTestSuite) TestTokenPair() {
+	var (
+		req    *types.QueryTokenPairRequest
+		expRes *types.QueryTokenPairResponse
+	)
+
+	testCases := []struct {
+		name     string
+		malleate func()
+		expPass  bool
+	}{
+		{
+			"invalid token address",
+			func() {
+				req = &types.QueryTokenPairRequest{}
+				expRes = &types.QueryTokenPairResponse{}
+			},
+			false,
+		},
+		{
+			"token pair not found",
+			func() {
+				req = &types.QueryTokenPairRequest{
+					Token: tests.GenerateAddress().Hex(),
+				}
+				expRes = &types.QueryTokenPairResponse{}
+			},
+			false,
+		},
+		{
+			"token pair found",
+			func() {
+				addr := tests.GenerateAddress()
+				pair := types.NewTokenPair(addr, "coin", true, types.OWNER_MODULE)
+				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, pair)
+				suite.app.Erc20Keeper.SetERC20Map(suite.ctx, addr, pair.GetID())
+				suite.app.Erc20Keeper.SetDenomMap(suite.ctx, pair.Denom, pair.GetID())
+
+				req = &types.QueryTokenPairRequest{
+					Token: pair.Erc20Address,
+				}
+				expRes = &types.QueryTokenPairResponse{TokenPair: pair}
+			},
+			true,
+		},
+		{
+			"token pair not found - with erc20 existant",
+			func() {
+				addr := tests.GenerateAddress()
+				pair := types.NewTokenPair(addr, "coin", true, types.OWNER_MODULE)
+				suite.app.Erc20Keeper.SetERC20Map(suite.ctx, addr, pair.GetID())
+				suite.app.Erc20Keeper.SetDenomMap(suite.ctx, pair.Denom, pair.GetID())
+
+				req = &types.QueryTokenPairRequest{
+					Token: pair.Erc20Address,
+				}
+				expRes = &types.QueryTokenPairResponse{TokenPair: pair}
+			},
+			false,
+		},
+	}
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.SetupTest() // reset
+
+			ctx := sdk.WrapSDKContext(suite.ctx)
+			tc.malleate()
+
+			res, err := suite.queryClient.TokenPair(ctx, req)
+			if tc.expPass {
+				suite.Require().NoError(err)
+				suite.Require().Equal(expRes, res)
+			} else {
+				suite.Require().Error(err)
+			}
+		})
+	}
+}
+
+func (suite *KeeperTestSuite) TestQueryParams() {
+	ctx := sdk.WrapSDKContext(suite.ctx)
+	expParams := types.DefaultParams()
+
+	res, err := suite.queryClient.Params(ctx, &types.QueryParamsRequest{})
+	suite.Require().NoError(err)
+	suite.Require().Equal(expParams, res.Params)
+}
diff --git a/x/erc20/keeper/integration_test.go b/x/erc20/keeper/integration_test.go
new file mode 100644
index 00000000..5b00c064
--- /dev/null
+++ b/x/erc20/keeper/integration_test.go
@@ -0,0 +1,302 @@
+package keeper_test
+
+import (
+	"math/big"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
+	abci "github.com/tendermint/tendermint/abci/types"
+
+	"github.com/cosmos/cosmos-sdk/client/tx"
+	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/ethereum/go-ethereum/common"
+
+	"github.com/evmos/ethermint/crypto/ethsecp256k1"
+	"github.com/evmos/ethermint/encoding"
+	ethermint "github.com/evmos/ethermint/types"
+
+	"github.com/AltheaFoundation/althea-L1/testutil"
+
+	althea "github.com/AltheaFoundation/althea-L1/app"
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+var _ = Describe("Performing EVM transactions", Ordered, func() {
+	BeforeEach(func() {
+		s.SetupTest()
+
+		params := s.app.Erc20Keeper.GetParams(s.ctx)
+		params.EnableEVMHook = true
+		params.EnableErc20 = true
+		s.app.Erc20Keeper.SetParams(s.ctx, params)
+	})
+
+	// Epoch mechanism for triggering allocation and distribution
+	Context("with the ERC20 module and EVM Hook disabled", func() {
+		BeforeEach(func() {
+			params := s.app.Erc20Keeper.GetParams(s.ctx)
+			params.EnableEVMHook = false
+			params.EnableErc20 = false
+			s.app.Erc20Keeper.SetParams(s.ctx, params)
+		})
+		It("should be successful", func() {
+			_, err := s.DeployContract("coin", "token", erc20Decimals)
+			Expect(err).To(BeNil())
+		})
+	})
+
+	Context("with the ERC20 module disabled", func() {
+		BeforeEach(func() {
+			params := s.app.Erc20Keeper.GetParams(s.ctx)
+			params.EnableErc20 = false
+			s.app.Erc20Keeper.SetParams(s.ctx, params)
+		})
+		It("should be successful", func() {
+			_, err := s.DeployContract("coin", "token", erc20Decimals)
+			Expect(err).To(BeNil())
+		})
+	})
+
+	Context("with the EVMHook disabled", func() {
+		BeforeEach(func() {
+			params := s.app.Erc20Keeper.GetParams(s.ctx)
+			params.EnableEVMHook = false
+			s.app.Erc20Keeper.SetParams(s.ctx, params)
+		})
+		It("should be successful", func() {
+			_, err := s.DeployContract("coin", "token", erc20Decimals)
+			Expect(err).To(BeNil())
+		})
+	})
+
+	Context("with the ERC20 module and EVM Hook enabled", func() {
+		It("should be successful", func() {
+			_, err := s.DeployContract("coin", "token", erc20Decimals)
+			Expect(err).To(BeNil())
+		})
+	})
+})
+
+var _ = Describe("ERC20: Converting", Ordered, func() {
+	var (
+		pair      *types.TokenPair
+		coin      sdk.Coin
+		moduleAcc sdk.AccAddress
+		amt       = sdk.NewInt(100)
+		priv      *ethsecp256k1.PrivKey
+		addr      common.Address
+		accAddr   sdk.AccAddress
+	)
+
+	BeforeEach(func() {
+		s.SetupTest()
+		priv, _ = ethsecp256k1.GenerateKey()
+		addrBz := priv.PubKey().Address().Bytes()
+		accAddr = sdk.AccAddress(addrBz)
+		addr = common.BytesToAddress(addrBz)
+		moduleAcc = s.app.AccountKeeper.GetModuleAccount(s.ctx, types.ModuleName).GetAddress()
+
+	})
+
+	Context("with a registered coin", func() {
+		BeforeEach(func() {
+			_, pair = s.setupRegisterCoin()
+			coin = sdk.NewCoin(pair.Denom, amt)
+
+			// denom := s.app.ClaimsKeeper.GetParams(s.ctx).ClaimsDenom
+			denom := "aalthea"
+
+			err := testutil.FundAccount(s.app.BankKeeper, s.ctx, accAddr, sdk.NewCoins(sdk.NewCoin(denom, sdk.TokensFromConsensusPower(100, ethermint.PowerReduction))))
+			s.Require().NoError(err)
+			err = testutil.FundAccount(s.app.BankKeeper, s.ctx, accAddr, sdk.NewCoins(coin))
+			s.Require().NoError(err)
+		})
+
+		Describe("a Cosmos coin into an ERC20 token", func() {
+			BeforeEach(func() {
+				convertCoin(priv, coin)
+			})
+
+			It("should decrease coins on the sender account", func() {
+				balanceCoin := s.app.BankKeeper.GetBalance(s.ctx, accAddr, pair.Denom)
+				Expect(balanceCoin.IsZero()).To(BeTrue())
+			})
+
+			It("should escrow coins on the module account", func() {
+				balanceCoin := s.app.BankKeeper.GetBalance(s.ctx, moduleAcc, pair.Denom)
+				Expect(balanceCoin).To(Equal(coin))
+			})
+
+			It("should mint tokens and send to receiver", func() {
+				balanceERC20 := s.BalanceOf(pair.GetERC20Contract(), addr).(*big.Int)
+				Expect(balanceERC20.Int64()).To(Equal(amt.Int64()))
+			})
+		})
+
+		Describe("an ERC20 token into a Cosmos coin", func() {
+			BeforeEach(func() {
+				convertCoin(priv, coin)
+				s.Commit()
+				convertERC20(priv, amt, pair.GetERC20Contract())
+			})
+
+			It("should increase coins on the sender account", func() {
+				balanceCoin := s.app.BankKeeper.GetBalance(s.ctx, accAddr, pair.Denom)
+				Expect(balanceCoin).To(Equal(coin))
+			})
+
+			It("should unescrow coins on the module account", func() {
+				balanceCoin := s.app.BankKeeper.GetBalance(s.ctx, moduleAcc, pair.Denom)
+				Expect(balanceCoin.IsZero()).To(BeTrue())
+			})
+
+			It("should burn the receiver's token", func() {
+				balanceERC20 := s.BalanceOf(pair.GetERC20Contract(), addr).(*big.Int)
+				Expect(balanceERC20.Int64()).To(Equal(int64(0)))
+			})
+		})
+	})
+
+	Context("with a registered ERC20", func() {
+		BeforeEach(func() {
+			contract := s.setupRegisterERC20Pair(contractMinterBurner)
+			id := s.app.Erc20Keeper.GetTokenPairID(s.ctx, contract.String())
+			*pair, _ = s.app.Erc20Keeper.GetTokenPair(s.ctx, id)
+			coin = sdk.NewCoin(pair.Denom, amt)
+
+			// denom := s.app.ClaimsKeeper.GetParams(s.ctx).ClaimsDenom
+			denom := "aalthea" //use default denom for claimsDenom
+
+			err := testutil.FundAccount(s.app.BankKeeper, s.ctx, accAddr, sdk.NewCoins(sdk.NewCoin(denom, sdk.NewInt(1000_000_000))))
+			s.Require().NoError(err)
+
+			_ = s.MintERC20Token(contract, s.address, addr, big.NewInt(amt.Int64()))
+			s.Commit()
+		})
+
+		Describe("an ERC20 token into a Cosmos coin", func() {
+			BeforeEach(func() {
+				convertERC20(priv, amt, pair.GetERC20Contract())
+			})
+
+			It("should decrease tokens on the sender account", func() {
+				balanceERC20 := s.BalanceOf(pair.GetERC20Contract(), addr).(*big.Int)
+				Expect(balanceERC20.Int64()).To(Equal(int64(0)))
+			})
+
+			It("should escrow tokens on the module account", func() {
+				moduleAddr := common.BytesToAddress(moduleAcc.Bytes())
+				balanceERC20 := s.BalanceOf(pair.GetERC20Contract(), moduleAddr).(*big.Int)
+				Expect(balanceERC20.Int64()).To(Equal(amt.Int64()))
+			})
+
+			It("should send coins to the recevier account", func() {
+				balanceCoin := s.app.BankKeeper.GetBalance(s.ctx, accAddr, pair.Denom)
+				Expect(balanceCoin).To(Equal(coin))
+			})
+		})
+
+		Describe("a Cosmos coin into an ERC20 token", func() {
+			BeforeEach(func() {
+				convertERC20(priv, amt, pair.GetERC20Contract())
+				s.Commit()
+				convertCoin(priv, coin)
+			})
+
+			It("should increase tokens on the sender account", func() {
+				balanceERC20 := s.BalanceOf(pair.GetERC20Contract(), addr).(*big.Int)
+				Expect(balanceERC20.Int64()).To(Equal(amt.Int64()))
+			})
+
+			It("should unescrow tokens on the module account", func() {
+				moduleAddr := common.BytesToAddress(moduleAcc.Bytes())
+				balanceERC20 := s.BalanceOf(pair.GetERC20Contract(), moduleAddr).(*big.Int)
+				Expect(balanceERC20.Int64()).To(Equal(int64(0)))
+			})
+
+			It("should burn coins to the recevier account", func() {
+				balanceCoin := s.app.BankKeeper.GetBalance(s.ctx, accAddr, pair.Denom)
+				Expect(balanceCoin.IsZero()).To(BeTrue())
+			})
+		})
+	})
+})
+
+func convertCoin(priv *ethsecp256k1.PrivKey, coin sdk.Coin) {
+	addrBz := priv.PubKey().Address().Bytes()
+
+	convertCoinMsg := types.NewMsgConvertCoin(coin, common.BytesToAddress(addrBz), sdk.AccAddress(addrBz))
+	res := deliverTx(priv, convertCoinMsg)
+	Expect(res.IsOK()).To(BeTrue(), "failed to convert coin: %s", res.Log)
+}
+
+func convertERC20(priv *ethsecp256k1.PrivKey, amt sdk.Int, contract common.Address) {
+	addrBz := priv.PubKey().Address().Bytes()
+
+	convertERC20Msg := types.NewMsgConvertERC20(amt, sdk.AccAddress(addrBz), contract, common.BytesToAddress(addrBz))
+	res := deliverTx(priv, convertERC20Msg)
+	Expect(res.IsOK()).To(BeTrue(), "failed to convert ERC20: %s", res.Log)
+}
+
+func deliverTx(priv *ethsecp256k1.PrivKey, msgs ...sdk.Msg) abci.ResponseDeliverTx {
+	encodingConfig := encoding.MakeConfig(althea.ModuleBasics)
+	accountAddress := sdk.AccAddress(priv.PubKey().Address().Bytes())
+	// denom := s.app.ClaimsKeeper.GetParams(s.ctx).ClaimsDenom
+	denom := "aalthea"
+
+	txBuilder := encodingConfig.TxConfig.NewTxBuilder()
+
+	txBuilder.SetGasLimit(100_000_000)
+	txBuilder.SetFeeAmount(sdk.Coins{{Denom: denom, Amount: sdk.NewInt(100_000_000)}})
+	err := txBuilder.SetMsgs(msgs...)
+	s.Require().NoError(err)
+
+	seq, err := s.app.AccountKeeper.GetSequence(s.ctx, accountAddress)
+	s.Require().NoError(err)
+
+	// First round: we gather all the signer infos. We use the "set empty
+	// signature" hack to do that.
+	sigV2 := signing.SignatureV2{
+		PubKey: priv.PubKey(),
+		Data: &signing.SingleSignatureData{
+			SignMode:  encodingConfig.TxConfig.SignModeHandler().DefaultMode(),
+			Signature: nil,
+		},
+		Sequence: seq,
+	}
+
+	sigsV2 := []signing.SignatureV2{sigV2}
+
+	err = txBuilder.SetSignatures(sigsV2...)
+	s.Require().NoError(err)
+
+	// Second round: all signer infos are set, so each signer can sign.
+	accNumber := s.app.AccountKeeper.GetAccount(s.ctx, accountAddress).GetAccountNumber()
+	signerData := authsigning.SignerData{
+		ChainID:       s.ctx.ChainID(),
+		AccountNumber: accNumber,
+		Sequence:      seq,
+	}
+	sigV2, err = tx.SignWithPrivKey(
+		encodingConfig.TxConfig.SignModeHandler().DefaultMode(), signerData,
+		txBuilder, priv, encodingConfig.TxConfig,
+		seq,
+	)
+	s.Require().NoError(err)
+
+	sigsV2 = []signing.SignatureV2{sigV2}
+	err = txBuilder.SetSignatures(sigsV2...)
+	s.Require().NoError(err)
+
+	// bz are bytes to be broadcasted over the network
+	bz, err := encodingConfig.TxConfig.TxEncoder()(txBuilder.GetTx())
+	s.Require().NoError(err)
+
+	req := abci.RequestDeliverTx{Tx: bz}
+	res := s.app.BaseApp.DeliverTx(req)
+	return res
+}
diff --git a/x/erc20/keeper/keeper.go b/x/erc20/keeper/keeper.go
new file mode 100644
index 00000000..47758afb
--- /dev/null
+++ b/x/erc20/keeper/keeper.go
@@ -0,0 +1,53 @@
+package keeper
+
+import (
+	"fmt"
+
+	"github.com/cosmos/cosmos-sdk/codec"
+	storetypes "github.com/cosmos/cosmos-sdk/store/types"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
+	"github.com/tendermint/tendermint/libs/log"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+// Keeper of this module maintains collections of erc20.
+type Keeper struct {
+	storeKey   storetypes.StoreKey
+	cdc        codec.BinaryCodec
+	paramstore paramtypes.Subspace
+
+	accountKeeper types.AccountKeeper
+	bankKeeper    types.BankKeeper
+	evmKeeper     types.EVMKeeper
+}
+
+// NewKeeper creates new instances of the erc20 Keeper
+func NewKeeper(
+	storeKey storetypes.StoreKey,
+	cdc codec.BinaryCodec,
+	ps paramtypes.Subspace,
+	ak types.AccountKeeper,
+	bk types.BankKeeper,
+	evmKeeper types.EVMKeeper,
+) Keeper {
+	// set KeyTable if it has not already been set
+	if !ps.HasKeyTable() {
+		ps = ps.WithKeyTable(types.ParamKeyTable())
+	}
+
+	return Keeper{
+		storeKey:      storeKey,
+		cdc:           cdc,
+		paramstore:    ps,
+		accountKeeper: ak,
+		bankKeeper:    bk,
+		evmKeeper:     evmKeeper,
+	}
+}
+
+// Logger returns a module-specific logger.
+func (k Keeper) Logger(ctx sdk.Context) log.Logger {
+	return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
+}
diff --git a/x/erc20/keeper/keeper_test.go b/x/erc20/keeper/keeper_test.go
new file mode 100644
index 00000000..61f2ecbc
--- /dev/null
+++ b/x/erc20/keeper/keeper_test.go
@@ -0,0 +1,518 @@
+package keeper_test
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"math/big"
+	"testing"
+	"time"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
+	"github.com/cosmos/cosmos-sdk/baseapp"
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/crypto/keyring"
+	"github.com/cosmos/cosmos-sdk/simapp"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/hexutil"
+	"github.com/ethereum/go-ethereum/core"
+	"github.com/ethereum/go-ethereum/crypto"
+	"github.com/ethereum/go-ethereum/params"
+	"github.com/stretchr/testify/mock"
+	"github.com/stretchr/testify/require"
+	"github.com/stretchr/testify/suite"
+	abci "github.com/tendermint/tendermint/abci/types"
+	"github.com/tendermint/tendermint/crypto/tmhash"
+	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
+	tmversion "github.com/tendermint/tendermint/proto/tendermint/version"
+	"github.com/tendermint/tendermint/version"
+
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+	ethtypes "github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/core/vm"
+	"github.com/evmos/ethermint/app"
+	"github.com/evmos/ethermint/crypto/ethsecp256k1"
+	"github.com/evmos/ethermint/encoding"
+	"github.com/evmos/ethermint/server/config"
+	"github.com/evmos/ethermint/tests"
+	"github.com/evmos/ethermint/x/evm/statedb"
+	evm "github.com/evmos/ethermint/x/evm/types"
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+	feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"
+
+	"github.com/AltheaFoundation/althea-L1/contracts"
+
+	althea "github.com/AltheaFoundation/althea-L1/app"
+	altheacfg "github.com/AltheaFoundation/althea-L1/config"
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+type KeeperTestSuite struct {
+	suite.Suite
+
+	ctx              sdk.Context
+	app              *althea.AltheaApp
+	queryClientEvm   evm.QueryClient
+	queryClient      types.QueryClient
+	address          common.Address
+	clientCtx        client.Context
+	ethSigner        ethtypes.Signer
+	signer           keyring.Signer
+	mintFeeCollector bool
+}
+
+var s *KeeperTestSuite
+
+func TestKeeperTestSuite(t *testing.T) {
+	s = new(KeeperTestSuite)
+	suite.Run(t, s)
+
+	// Run Ginkgo integration tests
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "Keeper Suite")
+}
+
+// Test helpers
+func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) {
+	checkTx := false
+
+	// account key
+	priv, err := ethsecp256k1.GenerateKey()
+	require.NoError(t, err)
+	suite.address = common.BytesToAddress(priv.PubKey().Address().Bytes())
+	suite.signer = tests.NewSigner(priv)
+
+	// init app
+	suite.app = althea.NewSetup(checkTx, func(aa *althea.AltheaApp, gs simapp.GenesisState) simapp.GenesisState {
+		// setup feemarketGenesis params
+		feemarketGenesis := feemarkettypes.DefaultGenesisState()
+		feemarketGenesis.Params.EnableHeight = 1
+		feemarketGenesis.Params.NoBaseFee = false
+		feemarketGenesis.Params.BaseFee = sdk.NewInt(1)
+
+		gs[feemarkettypes.ModuleName] = aa.AppCodec().MustMarshalJSON(feemarketGenesis)
+
+		if suite.mintFeeCollector {
+			// mint some coin to fee collector
+			coins := sdk.NewCoins(sdk.NewCoin(altheacfg.BaseDenom, sdk.NewInt(int64(params.TxGas)-1)))
+			balances := []banktypes.Balance{
+				{
+					Address: suite.app.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName).String(),
+					Coins:   coins,
+				},
+			}
+			// update total supply
+			var bankGenesis banktypes.GenesisState
+			aa.AppCodec().MustUnmarshalJSON(gs[banktypes.ModuleName], &bankGenesis)
+			bankGenesis.Balances = append(bankGenesis.Balances, balances...)
+			bankGenesis.Supply = bankGenesis.Supply.Add(coins...)
+			gs[banktypes.ModuleName] = suite.app.AppCodec().MustMarshalJSON(&bankGenesis)
+
+		}
+
+		return gs
+	})
+
+	suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{
+		Height:          1,
+		ChainID:         "althea_7357-1",
+		Time:            time.Now().UTC(),
+		ProposerAddress: althea.ValidatorPubKey.Address().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")),
+	})
+
+	queryHelperEvm := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry())
+	evm.RegisterQueryServer(queryHelperEvm, suite.app.EvmKeeper)
+	suite.queryClientEvm = evm.NewQueryClient(queryHelperEvm)
+
+	queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry())
+	types.RegisterQueryServer(queryHelper, suite.app.Erc20Keeper)
+	suite.queryClient = types.NewQueryClient(queryHelper)
+
+	encodingConfig := encoding.MakeConfig(app.ModuleBasics)
+	suite.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig)
+	suite.ethSigner = ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
+}
+
+func (suite *KeeperTestSuite) SetupTest() {
+	suite.DoSetupTest(suite.T())
+}
+
+func (suite *KeeperTestSuite) StateDB() *statedb.StateDB {
+	return statedb.New(suite.ctx, suite.app.EvmKeeper, statedb.NewEmptyTxConfig(common.BytesToHash(suite.ctx.HeaderHash().Bytes())))
+}
+
+func (suite *KeeperTestSuite) MintFeeBurner(coins sdk.Coins) {
+	err := suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
+	suite.Require().NoError(err)
+	err = suite.app.BankKeeper.SendCoinsFromModuleToModule(suite.ctx, types.ModuleName, evmtypes.FeeBurner, coins)
+	suite.Require().NoError(err)
+}
+
+// DeployContract deploys the ERC20MinterBurnerDecimalsContract.
+func (suite *KeeperTestSuite) DeployContract(name, symbol string, decimals uint8) (common.Address, error) {
+	ctx := sdk.WrapSDKContext(suite.ctx)
+	chainID := suite.app.EvmKeeper.ChainID()
+
+	ctorArgs, err := contracts.ERC20MinterBurnerDecimalsContract.ABI.Pack("", name, symbol, decimals)
+	if err != nil {
+		return common.Address{}, err
+	}
+
+	data := append(contracts.ERC20MinterBurnerDecimalsContract.Bin, ctorArgs...)
+	args, err := json.Marshal(&evm.TransactionArgs{
+		From: &suite.address,
+		Data: (*hexutil.Bytes)(&data),
+	})
+	if err != nil {
+		return common.Address{}, err
+	}
+
+	res, err := suite.queryClientEvm.EstimateGas(ctx, &evm.EthCallRequest{
+		Args:   args,
+		GasCap: uint64(config.DefaultGasCap),
+	})
+	if err != nil {
+		return common.Address{}, err
+	}
+
+	nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address)
+
+	erc20DeployTx := evm.NewTxContract(
+		chainID,
+		nonce,
+		nil,     // amount
+		res.Gas, // gasLimit
+		nil,     // gasPrice
+		suite.app.FeemarketKeeper.GetBaseFee(suite.ctx),
+		big.NewInt(1),
+		data,                   // input
+		&ethtypes.AccessList{}, // accesses
+	)
+
+	erc20DeployTx.From = suite.address.Hex()
+	err = erc20DeployTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer)
+	if err != nil {
+		return common.Address{}, err
+	}
+
+	rsp, err := suite.app.EvmKeeper.EthereumTx(ctx, erc20DeployTx)
+	if err != nil {
+		return common.Address{}, err
+	}
+
+	suite.Require().Empty(rsp.VmError)
+	return crypto.CreateAddress(suite.address, nonce), nil
+}
+
+func (suite *KeeperTestSuite) DeployContractMaliciousDelayed(name string, symbol string) common.Address {
+	ctx := sdk.WrapSDKContext(suite.ctx)
+	chainID := suite.app.EvmKeeper.ChainID()
+
+	ctorArgs, err := contracts.ERC20MaliciousDelayedContract.ABI.Pack("", big.NewInt(1000000000000000000))
+	suite.Require().NoError(err)
+
+	data := append(contracts.ERC20MaliciousDelayedContract.Bin, ctorArgs...)
+	args, err := json.Marshal(&evm.TransactionArgs{
+		From: &suite.address,
+		Data: (*hexutil.Bytes)(&data),
+	})
+	suite.Require().NoError(err)
+
+	res, err := suite.queryClientEvm.EstimateGas(ctx, &evm.EthCallRequest{
+		Args:   args,
+		GasCap: uint64(config.DefaultGasCap),
+	})
+	suite.Require().NoError(err)
+
+	nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address)
+
+	erc20DeployTx := evm.NewTxContract(
+		chainID,
+		nonce,
+		nil,     // amount
+		res.Gas, // gasLimit
+		nil,     // gasPrice
+		suite.app.FeemarketKeeper.GetBaseFee(suite.ctx),
+		big.NewInt(1),
+		data,                   // input
+		&ethtypes.AccessList{}, // accesses
+	)
+
+	erc20DeployTx.From = suite.address.Hex()
+	err = erc20DeployTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer)
+	suite.Require().NoError(err)
+	rsp, err := suite.app.EvmKeeper.EthereumTx(ctx, erc20DeployTx)
+	suite.Require().NoError(err)
+	suite.Require().Empty(rsp.VmError)
+	return crypto.CreateAddress(suite.address, nonce)
+}
+
+func (suite *KeeperTestSuite) DeployContractDirectBalanceManipulation(name string, symbol string) common.Address {
+	ctx := sdk.WrapSDKContext(suite.ctx)
+	chainID := suite.app.EvmKeeper.ChainID()
+
+	ctorArgs, err := contracts.ERC20DirectBalanceManipulationContract.ABI.Pack("", big.NewInt(1000000000000000000))
+	suite.Require().NoError(err)
+
+	data := append(contracts.ERC20DirectBalanceManipulationContract.Bin, ctorArgs...)
+	args, err := json.Marshal(&evm.TransactionArgs{
+		From: &suite.address,
+		Data: (*hexutil.Bytes)(&data),
+	})
+	suite.Require().NoError(err)
+
+	res, err := suite.queryClientEvm.EstimateGas(ctx, &evm.EthCallRequest{
+		Args:   args,
+		GasCap: uint64(config.DefaultGasCap),
+	})
+	suite.Require().NoError(err)
+
+	nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address)
+
+	erc20DeployTx := evm.NewTxContract(
+		chainID,
+		nonce,
+		nil,     // amount
+		res.Gas, // gasLimit
+		nil,     // gasPrice
+		suite.app.FeemarketKeeper.GetBaseFee(suite.ctx),
+		big.NewInt(1),
+		data,                   // input
+		&ethtypes.AccessList{}, // accesses
+	)
+
+	erc20DeployTx.From = suite.address.Hex()
+	err = erc20DeployTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer)
+	suite.Require().NoError(err)
+	rsp, err := suite.app.EvmKeeper.EthereumTx(ctx, erc20DeployTx)
+	suite.Require().NoError(err)
+	suite.Require().Empty(rsp.VmError)
+	return crypto.CreateAddress(suite.address, nonce)
+}
+
+func (suite *KeeperTestSuite) Commit() {
+	_ = suite.app.Commit()
+	header := suite.ctx.BlockHeader()
+	header.Height += 1
+	suite.app.BeginBlock(abci.RequestBeginBlock{
+		Header: header,
+	})
+
+	// update ctx
+	suite.ctx = suite.app.BaseApp.NewContext(false, header)
+
+	queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry())
+	evm.RegisterQueryServer(queryHelper, suite.app.EvmKeeper)
+	suite.queryClientEvm = evm.NewQueryClient(queryHelper)
+}
+
+func (suite *KeeperTestSuite) MintERC20Token(contractAddr, from, to common.Address, amount *big.Int) *evm.MsgEthereumTx {
+	transferData, err := contracts.ERC20MinterBurnerDecimalsContract.ABI.Pack("mint", to, amount)
+	suite.Require().NoError(err)
+	return suite.sendTx(contractAddr, from, transferData)
+}
+
+func (suite *KeeperTestSuite) TransferERC20TokenToModule(contractAddr, from common.Address, amount *big.Int) *evm.MsgEthereumTx {
+	transferData, err := contracts.ERC20MinterBurnerDecimalsContract.ABI.Pack("transfer", types.ModuleAddress, amount)
+	suite.Require().NoError(err)
+	return suite.sendTx(contractAddr, from, transferData)
+}
+
+func (suite *KeeperTestSuite) GrantERC20Token(contractAddr, from, to common.Address, role_string string) *evm.MsgEthereumTx {
+	// 0xCc508cD0818C85b8b8a1aB4cEEef8d981c8956A6 MINTER_ROLE
+	role := crypto.Keccak256([]byte(role_string))
+	// needs to be an array not a slice
+	var v [32]byte
+	copy(v[:], role)
+
+	transferData, err := contracts.ERC20MinterBurnerDecimalsContract.ABI.Pack("grantRole", v, to)
+	suite.Require().NoError(err)
+	return suite.sendTx(contractAddr, from, transferData)
+}
+
+func (suite *KeeperTestSuite) sendTx(contractAddr, from common.Address, transferData []byte) *evm.MsgEthereumTx {
+	ctx := sdk.WrapSDKContext(suite.ctx)
+	chainID := suite.app.EvmKeeper.ChainID()
+
+	args, err := json.Marshal(&evm.TransactionArgs{To: &contractAddr, From: &from, Data: (*hexutil.Bytes)(&transferData)})
+	suite.Require().NoError(err)
+	res, err := suite.queryClientEvm.EstimateGas(ctx, &evm.EthCallRequest{
+		Args:   args,
+		GasCap: uint64(config.DefaultGasCap),
+	})
+	suite.Require().NoError(err)
+
+	nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address)
+
+	// Mint the max gas to the FeeCollector to ensure balance in case of refund
+	suite.MintFeeBurner(sdk.NewCoins(sdk.NewCoin(altheacfg.BaseDenom, sdk.NewInt(suite.app.FeemarketKeeper.GetBaseFee(suite.ctx).Int64()*int64(res.Gas)))))
+
+	ercTransferTx := evm.NewTx(
+		chainID,
+		nonce,
+		&contractAddr,
+		nil,
+		res.Gas,
+		nil,
+		suite.app.FeemarketKeeper.GetBaseFee(suite.ctx),
+		big.NewInt(1),
+		transferData,
+		&ethtypes.AccessList{}, // accesses
+	)
+
+	ercTransferTx.From = suite.address.Hex()
+	err = ercTransferTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer)
+	suite.Require().NoError(err)
+	rsp, err := suite.app.EvmKeeper.EthereumTx(ctx, ercTransferTx)
+	suite.Require().NoError(err)
+	suite.Require().Empty(rsp.VmError)
+	return ercTransferTx
+}
+
+func (suite *KeeperTestSuite) BalanceOf(contract, account common.Address) interface{} {
+	erc20 := contracts.ERC20MinterBurnerDecimalsContract.ABI
+
+	res, err := suite.app.Erc20Keeper.CallEVM(suite.ctx, erc20, types.ModuleAddress, contract, false, "balanceOf", account)
+	if err != nil {
+		return nil
+	}
+
+	unpacked, err := erc20.Unpack("balanceOf", res.Ret)
+	if len(unpacked) == 0 {
+		return nil
+	}
+
+	return unpacked[0]
+}
+
+func (suite *KeeperTestSuite) NameOf(contract common.Address) string {
+	erc20 := contracts.ERC20MinterBurnerDecimalsContract.ABI
+
+	res, err := suite.app.Erc20Keeper.CallEVM(suite.ctx, erc20, types.ModuleAddress, contract, false, "name")
+	suite.Require().NoError(err)
+	suite.Require().NotNil(res)
+
+	unpacked, err := erc20.Unpack("name", res.Ret)
+	suite.Require().NoError(err)
+	suite.Require().NotEmpty(unpacked)
+
+	return fmt.Sprintf("%v", unpacked[0])
+}
+
+func (suite *KeeperTestSuite) TransferERC20Token(contractAddr, from, to common.Address, amount *big.Int) *evm.MsgEthereumTx {
+	transferData, err := contracts.ERC20MinterBurnerDecimalsContract.ABI.Pack("transfer", to, amount)
+	suite.Require().NoError(err)
+	return suite.sendTx(contractAddr, from, transferData)
+}
+
+var _ types.EVMKeeper = &MockEVMKeeper{}
+
+type MockEVMKeeper struct {
+	mock.Mock
+}
+
+func (m *MockEVMKeeper) GetParams(ctx sdk.Context) evmtypes.Params {
+	args := m.Called(mock.Anything)
+	return args.Get(0).(evmtypes.Params)
+}
+
+func (m *MockEVMKeeper) GetAccountWithoutBalance(ctx sdk.Context, addr common.Address) *statedb.Account {
+	args := m.Called(mock.Anything, mock.Anything)
+	if args.Get(0) == nil {
+		return nil
+	}
+	return args.Get(0).(*statedb.Account)
+}
+
+func (m *MockEVMKeeper) EstimateGas(c context.Context, req *evmtypes.EthCallRequest) (*evmtypes.EstimateGasResponse, error) {
+	args := m.Called(mock.Anything, mock.Anything)
+	if args.Get(0) == nil {
+		return nil, args.Error(1)
+	}
+	return args.Get(0).(*evmtypes.EstimateGasResponse), args.Error(1)
+}
+
+func (m *MockEVMKeeper) ApplyMessage(ctx sdk.Context, msg core.Message, tracer vm.EVMLogger, commit bool) (*evmtypes.MsgEthereumTxResponse, error) {
+	args := m.Called(mock.Anything, mock.Anything, mock.Anything, mock.Anything)
+
+	if args.Get(0) == nil {
+		return nil, args.Error(1)
+	}
+	return args.Get(0).(*evmtypes.MsgEthereumTxResponse), args.Error(1)
+}
+
+var _ types.BankKeeper = &MockBankKeeper{}
+
+type MockBankKeeper struct {
+	mock.Mock
+}
+
+func (b *MockBankKeeper) SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error {
+	args := b.Called(mock.Anything, mock.Anything, mock.Anything, mock.Anything)
+	return args.Error(0)
+}
+
+func (b *MockBankKeeper) SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error {
+	args := b.Called(mock.Anything, mock.Anything, mock.Anything, mock.Anything)
+	return args.Error(0)
+}
+
+func (b *MockBankKeeper) MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error {
+	args := b.Called(mock.Anything, mock.Anything, mock.Anything)
+	return args.Error(0)
+}
+
+func (b *MockBankKeeper) BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error {
+	args := b.Called(mock.Anything, mock.Anything, mock.Anything)
+	return args.Error(0)
+}
+
+func (b *MockBankKeeper) IsSendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool {
+	args := b.Called(mock.Anything, mock.Anything)
+	return args.Bool(0)
+}
+
+func (b *MockBankKeeper) BlockedAddr(addr sdk.AccAddress) bool {
+	args := b.Called(mock.Anything)
+	return args.Bool(0)
+}
+
+func (b *MockBankKeeper) GetDenomMetaData(ctx sdk.Context, denom string) (banktypes.Metadata, bool) {
+	args := b.Called(mock.Anything, mock.Anything)
+	return args.Get(0).(banktypes.Metadata), args.Bool(1)
+}
+
+func (b *MockBankKeeper) SetDenomMetaData(ctx sdk.Context, denomMetaData banktypes.Metadata) {
+}
+
+func (b *MockBankKeeper) HasSupply(ctx sdk.Context, denom string) bool {
+	args := b.Called(mock.Anything, mock.Anything)
+	return args.Bool(0)
+}
+
+func (b *MockBankKeeper) GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin {
+	args := b.Called(mock.Anything, mock.Anything)
+	return args.Get(0).(sdk.Coin)
+}
diff --git a/x/erc20/keeper/migrations.go b/x/erc20/keeper/migrations.go
new file mode 100644
index 00000000..a0185291
--- /dev/null
+++ b/x/erc20/keeper/migrations.go
@@ -0,0 +1,27 @@
+package keeper
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/module"
+
+	v2 "github.com/AltheaFoundation/althea-L1/x/erc20/migrations/v2"
+)
+
+var _ module.MigrationHandler = Migrator{}.Migrate1to2
+
+// Migrator is a struct for handling in-place store migrations.
+type Migrator struct {
+	keeper Keeper
+}
+
+// NewMigrator returns a new Migrator.
+func NewMigrator(keeper Keeper) Migrator {
+	return Migrator{
+		keeper: keeper,
+	}
+}
+
+// Migrate1to2 migrates from consensus version 1 to 2.
+func (m Migrator) Migrate1to2(ctx sdk.Context) error {
+	return v2.UpdateParams(ctx, &m.keeper.paramstore)
+}
diff --git a/x/erc20/keeper/mint.go b/x/erc20/keeper/mint.go
new file mode 100644
index 00000000..72d07f96
--- /dev/null
+++ b/x/erc20/keeper/mint.go
@@ -0,0 +1,66 @@
+package keeper
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+// MintingEnabled checks that:
+//   - the global parameter for erc20 conversion is enabled
+//   - minting is enabled for the given (erc20,coin) token pair
+//   - recipient address is not on the blocked list
+//   - bank module transfers are enabled for the Cosmos coin
+func (k Keeper) MintingEnabled(
+	ctx sdk.Context,
+	sender, receiver sdk.AccAddress,
+	token string,
+) (types.TokenPair, error) {
+	params := k.GetParams(ctx)
+	if !params.EnableErc20 {
+		return types.TokenPair{}, sdkerrors.Wrap(
+			types.ErrERC20Disabled, "module is currently disabled by governance",
+		)
+	}
+
+	id := k.GetTokenPairID(ctx, token)
+	if len(id) == 0 {
+		return types.TokenPair{}, sdkerrors.Wrapf(
+			types.ErrTokenPairNotFound, "token '%s' not registered by id", token,
+		)
+	}
+
+	pair, found := k.GetTokenPair(ctx, id)
+	if !found {
+		return types.TokenPair{}, sdkerrors.Wrapf(
+			types.ErrTokenPairNotFound, "token '%s' not registered", token,
+		)
+	}
+
+	if !pair.Enabled {
+		return types.TokenPair{}, sdkerrors.Wrapf(
+			types.ErrERC20TokenPairDisabled, "minting token '%s' is not enabled by governance", token,
+		)
+	}
+
+	if k.bankKeeper.BlockedAddr(receiver.Bytes()) {
+		return types.TokenPair{}, sdkerrors.Wrapf(
+			sdkerrors.ErrUnauthorized, "%s is not allowed to receive transactions", receiver,
+		)
+	}
+
+	// NOTE: ignore amount as only denom is checked on IsSendEnabledCoin
+	coin := sdk.Coin{Denom: pair.Denom}
+
+	// check if minting to a recipient address other than the sender is enabled
+	// for for the given coin denom
+	if !sender.Equals(receiver) && !k.bankKeeper.IsSendEnabledCoin(ctx, coin) {
+		return types.TokenPair{}, sdkerrors.Wrapf(
+			banktypes.ErrSendDisabled, "minting '%s' coins to an external address is currently disabled", token,
+		)
+	}
+
+	return pair, nil
+}
diff --git a/x/erc20/keeper/mint_test.go b/x/erc20/keeper/mint_test.go
new file mode 100644
index 00000000..ce39e144
--- /dev/null
+++ b/x/erc20/keeper/mint_test.go
@@ -0,0 +1,112 @@
+package keeper_test
+
+import (
+	"fmt"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+	"github.com/evmos/ethermint/tests"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+func (suite *KeeperTestSuite) TestMintingEnabled() {
+	sender := sdk.AccAddress(tests.GenerateAddress().Bytes())
+	receiver := sdk.AccAddress(tests.GenerateAddress().Bytes())
+	expPair := types.NewTokenPair(tests.GenerateAddress(), "coin", true, types.OWNER_MODULE)
+	id := expPair.GetID()
+
+	testCases := []struct {
+		name     string
+		malleate func()
+		expPass  bool
+	}{
+		{
+			"conversion is disabled globally",
+			func() {
+				params := types.DefaultParams()
+				params.EnableErc20 = false
+				suite.app.Erc20Keeper.SetParams(suite.ctx, params)
+			},
+			false,
+		},
+		{
+			"token pair not found",
+			func() {},
+			false,
+		},
+		{
+			"conversion is disabled for the given pair",
+			func() {
+				expPair.Enabled = false
+				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, expPair)
+				suite.app.Erc20Keeper.SetDenomMap(suite.ctx, expPair.Denom, id)
+				suite.app.Erc20Keeper.SetERC20Map(suite.ctx, expPair.GetERC20Contract(), id)
+			},
+			false,
+		},
+		{
+			"token transfers are disabled",
+			func() {
+				expPair.Enabled = true
+				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, expPair)
+				suite.app.Erc20Keeper.SetDenomMap(suite.ctx, expPair.Denom, id)
+				suite.app.Erc20Keeper.SetERC20Map(suite.ctx, expPair.GetERC20Contract(), id)
+
+				params := banktypes.DefaultParams()
+				params.SendEnabled = []*banktypes.SendEnabled{
+					{Denom: expPair.Denom, Enabled: false},
+				}
+				suite.app.BankKeeper.SetParams(suite.ctx, params)
+			},
+			false,
+		},
+		{
+			"token not registered",
+			func() {
+				suite.app.Erc20Keeper.SetDenomMap(suite.ctx, expPair.Denom, id)
+				suite.app.Erc20Keeper.SetERC20Map(suite.ctx, expPair.GetERC20Contract(), id)
+			},
+			false,
+		},
+		{
+			"receiver address is blocked (module account)",
+			func() {
+				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, expPair)
+				suite.app.Erc20Keeper.SetDenomMap(suite.ctx, expPair.Denom, id)
+				suite.app.Erc20Keeper.SetERC20Map(suite.ctx, expPair.GetERC20Contract(), id)
+
+				acc := suite.app.AccountKeeper.GetModuleAccount(suite.ctx, types.ModuleName)
+				receiver = acc.GetAddress()
+			},
+			false,
+		},
+		{
+			"ok",
+			func() {
+				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, expPair)
+				suite.app.Erc20Keeper.SetDenomMap(suite.ctx, expPair.Denom, id)
+				suite.app.Erc20Keeper.SetERC20Map(suite.ctx, expPair.GetERC20Contract(), id)
+
+				receiver = sdk.AccAddress(tests.GenerateAddress().Bytes())
+			},
+			true,
+		},
+	}
+
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.SetupTest() // reset
+
+			tc.malleate()
+
+			pair, err := suite.app.Erc20Keeper.MintingEnabled(suite.ctx, sender, receiver, expPair.Erc20Address)
+			if tc.expPass {
+				suite.Require().NoError(err)
+				suite.Require().Equal(expPair, pair)
+			} else {
+				suite.Require().Error(err)
+			}
+		})
+	}
+}
diff --git a/x/erc20/keeper/msg_server.go b/x/erc20/keeper/msg_server.go
new file mode 100644
index 00000000..2ad0be99
--- /dev/null
+++ b/x/erc20/keeper/msg_server.go
@@ -0,0 +1,526 @@
+package keeper
+
+import (
+	"context"
+	"math/big"
+
+	"github.com/armon/go-metrics"
+	"github.com/cosmos/cosmos-sdk/telemetry"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/ethereum/go-ethereum/common"
+
+	"github.com/AltheaFoundation/althea-L1/contracts"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+var _ types.MsgServer = &Keeper{}
+
+// ConvertCoin converts native Cosmos coins into ERC20 tokens for both
+// Cosmos-native and ERC20 TokenPair Owners
+func (k Keeper) ConvertCoin(
+	goCtx context.Context,
+	msg *types.MsgConvertCoin,
+) (*types.MsgConvertCoinResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	// Error checked during msg validation
+	receiver := common.HexToAddress(msg.Receiver)
+	sender := sdk.MustAccAddressFromBech32(msg.Sender)
+
+	pair, err := k.MintingEnabled(ctx, sender, receiver.Bytes(), msg.Coin.Denom)
+	if err != nil {
+		return nil, err
+	}
+
+	// Remove token pair if contract is suicided
+	erc20 := common.HexToAddress(pair.Erc20Address)
+	acc := k.evmKeeper.GetAccountWithoutBalance(ctx, erc20)
+
+	if acc == nil || !acc.IsContract() {
+		k.DeleteTokenPair(ctx, pair)
+		k.Logger(ctx).Debug(
+			"deleting selfdestructed token pair from state",
+			"contract", pair.Erc20Address,
+		)
+		// NOTE: return nil error to persist the changes from the deletion
+		return nil, nil
+	}
+
+	// Check ownership and execute conversion
+	switch {
+	case pair.IsNativeCoin():
+		return k.convertCoinNativeCoin(ctx, pair, msg, receiver, sender) // case 1.1
+	case pair.IsNativeERC20():
+		return k.convertCoinNativeERC20(ctx, pair, msg, receiver, sender) // case 2.2
+	default:
+		return nil, types.ErrUndefinedOwner
+	}
+}
+
+// ConvertERC20 converts ERC20 tokens into native Cosmos coins for both
+// Cosmos-native and ERC20 TokenPair Owners
+func (k Keeper) ConvertERC20(
+	goCtx context.Context,
+	msg *types.MsgConvertERC20,
+) (*types.MsgConvertERC20Response, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	// Error checked during msg validation
+	receiver := sdk.MustAccAddressFromBech32(msg.Receiver)
+	sender := common.HexToAddress(msg.Sender)
+
+	pair, err := k.MintingEnabled(ctx, sender.Bytes(), receiver, msg.ContractAddress)
+	if err != nil {
+		return nil, err
+	}
+
+	// Remove token pair if contract is suicided
+	erc20 := common.HexToAddress(pair.Erc20Address)
+	acc := k.evmKeeper.GetAccountWithoutBalance(ctx, erc20)
+
+	if acc == nil || !acc.IsContract() {
+		k.DeleteTokenPair(ctx, pair)
+		k.Logger(ctx).Debug(
+			"deleting selfdestructed token pair from state",
+			"contract", pair.Erc20Address,
+		)
+		// NOTE: return nil error to persist the changes from the deletion
+		return nil, nil
+	}
+
+	// Check ownership and execute conversion
+	switch {
+	case pair.IsNativeCoin():
+		return k.convertERC20NativeCoin(ctx, pair, msg, receiver, sender) // case 1.2
+	case pair.IsNativeERC20():
+		return k.convertERC20NativeToken(ctx, pair, msg, receiver, sender) // case 2.1
+	default:
+		return nil, types.ErrUndefinedOwner
+	}
+}
+
+// convertCoinNativeCoin handles the coin conversion for a native Cosmos coin
+// token pair:
+//   - escrow coins on module account
+//   - mint tokens and send to receiver
+//   - check if token balance increased by amount
+func (k Keeper) convertCoinNativeCoin(
+	ctx sdk.Context,
+	pair types.TokenPair,
+	msg *types.MsgConvertCoin,
+	receiver common.Address,
+	sender sdk.AccAddress,
+) (*types.MsgConvertCoinResponse, error) {
+	// NOTE: ignore validation from NewCoin constructor
+	coins := sdk.Coins{msg.Coin}
+	erc20 := contracts.ERC20MinterBurnerDecimalsContract.ABI
+	contract := pair.GetERC20Contract()
+	balanceToken := k.BalanceOf(ctx, erc20, contract, receiver)
+	if balanceToken == nil {
+		return nil, sdkerrors.Wrap(types.ErrEVMCall, "failed to retrieve balance")
+	}
+
+	// Escrow coins on module account
+	err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, coins)
+	if err != nil {
+		return nil, sdkerrors.Wrap(err, "failed to escrow coins")
+	}
+
+	// Mint tokens and send to receiver
+	_, err = k.CallEVM(ctx, erc20, types.ModuleAddress, contract, true, "mint", receiver, msg.Coin.Amount.BigInt())
+	if err != nil {
+		return nil, err
+	}
+
+	// Check expected receiver balance after transfer
+	tokens := msg.Coin.Amount.BigInt()
+	balanceTokenAfter := k.BalanceOf(ctx, erc20, contract, receiver)
+	if balanceTokenAfter == nil {
+		return nil, sdkerrors.Wrap(types.ErrEVMCall, "failed to retrieve balance")
+	}
+	expToken := big.NewInt(0).Add(balanceToken, tokens)
+
+	if r := balanceTokenAfter.Cmp(expToken); r != 0 {
+		return nil, sdkerrors.Wrapf(
+			types.ErrBalanceInvariance,
+			"invalid token balance - expected: %v, actual: %v", expToken, balanceTokenAfter,
+		)
+	}
+
+	defer func() {
+		telemetry.IncrCounterWithLabels(
+			[]string{"tx", "msg", "convert", "coin", "total"},
+			1,
+			[]metrics.Label{
+				telemetry.NewLabel("denom", pair.Denom),
+				telemetry.NewLabel("erc20", pair.Erc20Address),
+			},
+		)
+
+		if msg.Coin.Amount.IsInt64() {
+			telemetry.IncrCounterWithLabels(
+				[]string{"tx", "msg", "convert", "coin", "amount", "total"},
+				float32(msg.Coin.Amount.Int64()),
+				[]metrics.Label{
+					telemetry.NewLabel("denom", pair.Denom),
+					telemetry.NewLabel("erc20", pair.Erc20Address),
+				},
+			)
+		}
+	}()
+
+	ctx.EventManager().EmitEvents(
+		sdk.Events{
+			sdk.NewEvent(
+				types.EventTypeConvertCoin,
+				sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender),
+				sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver),
+				sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Coin.Amount.String()),
+				sdk.NewAttribute(types.AttributeKeyCosmosCoin, msg.Coin.Denom),
+				sdk.NewAttribute(types.AttributeKeyERC20Token, pair.Erc20Address),
+			),
+		},
+	)
+
+	return &types.MsgConvertCoinResponse{}, nil
+}
+
+// convertERC20NativeCoin handles the erc20 conversion for a native Cosmos coin
+// token pair:
+//   - burn escrowed tokens
+//   - unescrow coins that have been previously escrowed with ConvertCoin
+//   - check if coin balance increased by amount
+//   - check if token balance decreased by amount
+func (k Keeper) convertERC20NativeCoin(
+	ctx sdk.Context,
+	pair types.TokenPair,
+	msg *types.MsgConvertERC20,
+	receiver sdk.AccAddress,
+	sender common.Address,
+) (*types.MsgConvertERC20Response, error) {
+	// NOTE: coin fields already validated
+	coins := sdk.Coins{sdk.Coin{Denom: pair.Denom, Amount: msg.Amount}}
+	erc20 := contracts.ERC20MinterBurnerDecimalsContract.ABI
+	contract := pair.GetERC20Contract()
+	balanceCoin := k.bankKeeper.GetBalance(ctx, receiver, pair.Denom)
+	balanceToken := k.BalanceOf(ctx, erc20, contract, sender)
+	if balanceToken == nil {
+		return nil, sdkerrors.Wrap(types.ErrEVMCall, "failed to retrieve balance")
+	}
+
+	// Burn escrowed tokens
+	_, err := k.CallEVM(ctx, erc20, types.ModuleAddress, contract, true, "burnCoins", sender, msg.Amount.BigInt())
+	if err != nil {
+		return nil, err
+	}
+
+	// Unescrow coins and send to receiver
+	err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, receiver, coins)
+	if err != nil {
+		return nil, err
+	}
+
+	// Check expected receiver balance after transfer
+	balanceCoinAfter := k.bankKeeper.GetBalance(ctx, receiver, pair.Denom)
+	expCoin := balanceCoin.Add(coins[0])
+	if ok := balanceCoinAfter.IsEqual(expCoin); !ok {
+		return nil, sdkerrors.Wrapf(
+			types.ErrBalanceInvariance,
+			"invalid coin balance - expected: %v, actual: %v",
+			expCoin, balanceCoinAfter,
+		)
+	}
+
+	// Check expected Sender balance after transfer
+	tokens := coins[0].Amount.BigInt()
+	balanceTokenAfter := k.BalanceOf(ctx, erc20, contract, sender)
+	if balanceTokenAfter == nil {
+		return nil, sdkerrors.Wrap(types.ErrEVMCall, "failed to retrieve balance")
+	}
+
+	expToken := big.NewInt(0).Sub(balanceToken, tokens)
+	if r := balanceTokenAfter.Cmp(expToken); r != 0 {
+		return nil, sdkerrors.Wrapf(
+			types.ErrBalanceInvariance,
+			"invalid token balance - expected: %v, actual: %v",
+			expToken, balanceTokenAfter,
+		)
+	}
+
+	defer func() {
+		telemetry.IncrCounterWithLabels(
+			[]string{"tx", "msg", "convert", "erc20", "total"},
+			1,
+			[]metrics.Label{
+				telemetry.NewLabel("denom", pair.Denom),
+				telemetry.NewLabel("erc20", pair.Erc20Address),
+			},
+		)
+
+		if msg.Amount.IsInt64() {
+			telemetry.IncrCounterWithLabels(
+				[]string{"tx", "msg", "convert", "erc20", "amount", "total"},
+				float32(msg.Amount.Int64()),
+				[]metrics.Label{
+					telemetry.NewLabel("denom", pair.Denom),
+					telemetry.NewLabel("erc20", pair.Erc20Address),
+				},
+			)
+		}
+	}()
+
+	ctx.EventManager().EmitEvents(
+		sdk.Events{
+			sdk.NewEvent(
+				types.EventTypeConvertERC20,
+				sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender),
+				sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver),
+				sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
+				sdk.NewAttribute(types.AttributeKeyCosmosCoin, pair.Denom),
+				sdk.NewAttribute(types.AttributeKeyERC20Token, msg.ContractAddress),
+			),
+		},
+	)
+
+	return &types.MsgConvertERC20Response{}, nil
+}
+
+// convertERC20NativeToken handles the erc20 conversion for a native erc20 token
+// pair:
+//   - escrow tokens on module account
+//   - mint coins on bank module
+//   - send minted coins to the receiver
+//   - check if coin balance increased by amount
+//   - check if token balance decreased by amount
+//   - check for unexpected `Approval` event in logs
+func (k Keeper) convertERC20NativeToken(
+	ctx sdk.Context,
+	pair types.TokenPair,
+	msg *types.MsgConvertERC20,
+	receiver sdk.AccAddress,
+	sender common.Address,
+) (*types.MsgConvertERC20Response, error) {
+	// NOTE: coin fields already validated
+	coins := sdk.Coins{sdk.Coin{Denom: pair.Denom, Amount: msg.Amount}}
+	erc20 := contracts.ERC20MinterBurnerDecimalsContract.ABI
+	contract := pair.GetERC20Contract()
+	balanceCoin := k.bankKeeper.GetBalance(ctx, receiver, pair.Denom)
+	balanceToken := k.BalanceOf(ctx, erc20, contract, types.ModuleAddress)
+	if balanceToken == nil {
+		return nil, sdkerrors.Wrap(types.ErrEVMCall, "failed to retrieve balance")
+	}
+
+	// Escrow tokens on module account
+	transferData, err := erc20.Pack("transfer", types.ModuleAddress, msg.Amount.BigInt())
+	if err != nil {
+		return nil, err
+	}
+
+	res, err := k.CallEVMWithData(ctx, sender, &contract, transferData, true)
+	if err != nil {
+		return nil, err
+	}
+
+	// Check evm call response
+	var unpackedRet types.ERC20BoolResponse
+	if err := erc20.UnpackIntoInterface(&unpackedRet, "transfer", res.Ret); err != nil {
+		return nil, err
+	}
+
+	if !unpackedRet.Value {
+		return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "failed to execute transfer")
+	}
+
+	// Check expected escrow balance after transfer execution
+	tokens := coins[0].Amount.BigInt()
+	balanceTokenAfter := k.BalanceOf(ctx, erc20, contract, types.ModuleAddress)
+	if balanceTokenAfter == nil {
+		return nil, sdkerrors.Wrap(types.ErrEVMCall, "failed to retrieve balance")
+	}
+
+	expToken := big.NewInt(0).Add(balanceToken, tokens)
+
+	if r := balanceTokenAfter.Cmp(expToken); r != 0 {
+		return nil, sdkerrors.Wrapf(
+			types.ErrBalanceInvariance,
+			"invalid token balance - expected: %v, actual: %v",
+			expToken, balanceTokenAfter,
+		)
+	}
+
+	// Mint coins
+	if err := k.bankKeeper.MintCoins(ctx, types.ModuleName, coins); err != nil {
+		return nil, err
+	}
+
+	// Send minted coins to the receiver
+	if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, receiver, coins); err != nil {
+		return nil, err
+	}
+
+	// Check expected receiver balance after transfer
+	balanceCoinAfter := k.bankKeeper.GetBalance(ctx, receiver, pair.Denom)
+	expCoin := balanceCoin.Add(coins[0])
+
+	if ok := balanceCoinAfter.IsEqual(expCoin); !ok {
+		return nil, sdkerrors.Wrapf(
+			types.ErrBalanceInvariance,
+			"invalid coin balance - expected: %v, actual: %v",
+			expCoin, balanceCoinAfter,
+		)
+	}
+
+	// Check for unexpected `Approval` event in logs
+	if err := k.monitorApprovalEvent(res); err != nil {
+		return nil, err
+	}
+
+	defer func() {
+		telemetry.IncrCounterWithLabels(
+			[]string{"tx", "msg", "convert", "erc20", "total"},
+			1,
+			[]metrics.Label{
+				telemetry.NewLabel("coin", pair.Denom),
+				telemetry.NewLabel("erc20", pair.Erc20Address),
+			},
+		)
+
+		if msg.Amount.IsInt64() {
+			telemetry.IncrCounterWithLabels(
+				[]string{"tx", "msg", "convert", "erc20", "amount", "total"},
+				float32(msg.Amount.Int64()),
+				[]metrics.Label{
+					telemetry.NewLabel("denom", pair.Denom),
+					telemetry.NewLabel("erc20", pair.Erc20Address),
+				},
+			)
+		}
+	}()
+
+	ctx.EventManager().EmitEvents(
+		sdk.Events{
+			sdk.NewEvent(
+				types.EventTypeConvertERC20,
+				sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender),
+				sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver),
+				sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
+				sdk.NewAttribute(types.AttributeKeyCosmosCoin, pair.Denom),
+				sdk.NewAttribute(types.AttributeKeyERC20Token, msg.ContractAddress),
+			),
+		},
+	)
+
+	return &types.MsgConvertERC20Response{}, nil
+}
+
+// convertCoinNativeERC20 handles the coin conversion for a native ERC20 token
+// pair:
+//   - escrow Coins on module account
+//   - unescrow Tokens that have been previously escrowed with ConvertERC20 and send to receiver
+//   - burn escrowed Coins
+//   - check if token balance increased by amount
+//   - check for unexpected `Approval` event in logs
+func (k Keeper) convertCoinNativeERC20(
+	ctx sdk.Context,
+	pair types.TokenPair,
+	msg *types.MsgConvertCoin,
+	receiver common.Address,
+	sender sdk.AccAddress,
+) (*types.MsgConvertCoinResponse, error) {
+	// NOTE: ignore validation from NewCoin constructor
+	coins := sdk.Coins{msg.Coin}
+
+	erc20 := contracts.ERC20MinterBurnerDecimalsContract.ABI
+	contract := pair.GetERC20Contract()
+	balanceToken := k.BalanceOf(ctx, erc20, contract, receiver)
+	if balanceToken == nil {
+		return nil, sdkerrors.Wrap(types.ErrEVMCall, "failed to retrieve balance")
+	}
+
+	// Escrow Coins on module account
+	if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, coins); err != nil {
+		return nil, sdkerrors.Wrap(err, "failed to escrow coins")
+	}
+
+	// Unescrow Tokens and send to receiver
+	res, err := k.CallEVM(ctx, erc20, types.ModuleAddress, contract, true, "transfer", receiver, msg.Coin.Amount.BigInt())
+	if err != nil {
+		return nil, err
+	}
+
+	// Check unpackedRet execution
+	var unpackedRet types.ERC20BoolResponse
+	if err := erc20.UnpackIntoInterface(&unpackedRet, "transfer", res.Ret); err != nil {
+		return nil, err
+	}
+
+	if !unpackedRet.Value {
+		return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "failed to execute unescrow tokens from user")
+	}
+
+	// Check expected Receiver balance after transfer execution
+	tokens := msg.Coin.Amount.BigInt()
+	balanceTokenAfter := k.BalanceOf(ctx, erc20, contract, receiver)
+	if balanceTokenAfter == nil {
+		return nil, sdkerrors.Wrap(types.ErrEVMCall, "failed to retrieve balance")
+	}
+
+	exp := big.NewInt(0).Add(balanceToken, tokens)
+
+	if r := balanceTokenAfter.Cmp(exp); r != 0 {
+		return nil, sdkerrors.Wrapf(
+			types.ErrBalanceInvariance,
+			"invalid token balance - expected: %v, actual: %v", exp, balanceTokenAfter,
+		)
+	}
+
+	// Burn escrowed Coins
+	err = k.bankKeeper.BurnCoins(ctx, types.ModuleName, coins)
+	if err != nil {
+		return nil, sdkerrors.Wrap(err, "failed to burn coins")
+	}
+
+	// Check for unexpected `Approval` event in logs
+	if err := k.monitorApprovalEvent(res); err != nil {
+		return nil, err
+	}
+
+	defer func() {
+		telemetry.IncrCounterWithLabels(
+			[]string{"tx", "msg", "convert", "coin", "total"},
+			1,
+			[]metrics.Label{
+				telemetry.NewLabel("denom", pair.Denom),
+				telemetry.NewLabel("erc20", pair.Erc20Address),
+			},
+		)
+
+		if msg.Coin.Amount.IsInt64() {
+			telemetry.IncrCounterWithLabels(
+				[]string{"tx", "msg", "convert", "coin", "amount", "total"},
+				float32(msg.Coin.Amount.Int64()),
+				[]metrics.Label{
+					telemetry.NewLabel("denom", pair.Denom),
+					telemetry.NewLabel("erc20", pair.Erc20Address),
+				},
+			)
+		}
+	}()
+
+	ctx.EventManager().EmitEvents(
+		sdk.Events{
+			sdk.NewEvent(
+				types.EventTypeConvertCoin,
+				sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender),
+				sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver),
+				sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Coin.Amount.String()),
+				sdk.NewAttribute(types.AttributeKeyCosmosCoin, msg.Coin.Denom),
+				sdk.NewAttribute(types.AttributeKeyERC20Token, pair.Erc20Address),
+			),
+		},
+	)
+
+	return &types.MsgConvertCoinResponse{}, nil
+}
diff --git a/x/erc20/keeper/msg_server_test.go b/x/erc20/keeper/msg_server_test.go
new file mode 100644
index 00000000..47d1c2c2
--- /dev/null
+++ b/x/erc20/keeper/msg_server_test.go
@@ -0,0 +1,1344 @@
+package keeper_test
+
+import (
+	"fmt"
+	"math/big"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/stretchr/testify/mock"
+
+	"github.com/ethereum/go-ethereum/common"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/keeper"
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+	"github.com/evmos/ethermint/x/evm/statedb"
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+)
+
+func (suite *KeeperTestSuite) TestConvertCoinNativeCoin() {
+	testCases := []struct {
+		name           string
+		mint           int64
+		burn           int64
+		malleate       func(common.Address)
+		extra          func()
+		expPass        bool
+		selfdestructed bool
+	}{
+		{
+			"ok - sufficient funds",
+			100,
+			10,
+			func(common.Address) {},
+			func() {},
+			true,
+			false,
+		},
+		{
+			"ok - equal funds",
+			10,
+			10,
+			func(common.Address) {},
+			func() {},
+			true,
+			false,
+		},
+		{
+			"ok - suicided contract",
+			10,
+			10,
+			func(erc20 common.Address) {
+				stateDb := suite.StateDB()
+				ok := stateDb.Suicide(erc20)
+				suite.Require().True(ok)
+				suite.Require().NoError(stateDb.Commit())
+			},
+			func() {},
+			true,
+			true,
+		},
+		{
+			"fail - insufficient funds",
+			0,
+			10,
+			func(common.Address) {},
+			func() {},
+			false,
+			false,
+		},
+		{
+			"fail - minting disabled",
+			100,
+			10,
+			func(common.Address) {
+				params := types.DefaultParams()
+				params.EnableErc20 = false
+				suite.app.Erc20Keeper.SetParams(suite.ctx, params)
+			},
+			func() {},
+			false,
+			false,
+		},
+		{
+			"fail - deleted module account - force fail", 100, 10, func(common.Address) {},
+			func() {
+				acc := suite.app.AccountKeeper.GetAccount(suite.ctx, types.ModuleAddress.Bytes())
+				suite.app.AccountKeeper.RemoveAccount(suite.ctx, acc)
+			}, false, false,
+		},
+		{
+			"fail - force evm fail", 100, 10, func(common.Address) {},
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("forced ApplyMessage error"))
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			}, false, false,
+		},
+		{
+			"fail - force evm balance error", 100, 10, func(common.Address) {},
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				// first balance of
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				// convert coin
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, nil).Once()
+				// second balance of
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, fmt.Errorf("third")).Once()
+				// Extra call on test
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, nil)
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			}, false, false,
+		},
+		{
+			"fail - force balance error", 100, 10, func(common.Address) {},
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Times(4)
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			}, false, false,
+		},
+	}
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.mintFeeCollector = true
+			suite.SetupTest()
+			metadata, pair := suite.setupRegisterCoin()
+			suite.Require().NotNil(metadata)
+			erc20 := pair.GetERC20Contract()
+			tc.malleate(erc20)
+			suite.Commit()
+
+			ctx := sdk.WrapSDKContext(suite.ctx)
+			coins := sdk.NewCoins(sdk.NewCoin(cosmosTokenBase, sdk.NewInt(tc.mint)))
+			sender := sdk.AccAddress(suite.address.Bytes())
+			msg := types.NewMsgConvertCoin(
+				sdk.NewCoin(cosmosTokenBase, sdk.NewInt(tc.burn)),
+				suite.address,
+				sender,
+			)
+
+			suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
+			suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+
+			tc.extra()
+			res, err := suite.app.Erc20Keeper.ConvertCoin(ctx, msg)
+			expRes := &types.MsgConvertCoinResponse{}
+			suite.Commit()
+			balance := suite.BalanceOf(common.HexToAddress(pair.Erc20Address), suite.address)
+			cosmosBalance := suite.app.BankKeeper.GetBalance(suite.ctx, sender, metadata.Base)
+
+			if tc.expPass {
+				suite.Require().NoError(err, tc.name)
+
+				acc := suite.app.EvmKeeper.GetAccountWithoutBalance(suite.ctx, erc20)
+				if tc.selfdestructed {
+					suite.Require().Nil(acc, "expected contract to be destroyed")
+				} else {
+					suite.Require().NotNil(acc)
+				}
+
+				if tc.selfdestructed || !acc.IsContract() {
+					id := suite.app.Erc20Keeper.GetTokenPairID(suite.ctx, erc20.String())
+					_, found := suite.app.Erc20Keeper.GetTokenPair(suite.ctx, id)
+					suite.Require().False(found)
+				} else {
+					suite.Require().Equal(expRes, res)
+					suite.Require().Equal(cosmosBalance.Amount.Int64(), sdk.NewInt(tc.mint-tc.burn).Int64())
+					suite.Require().Equal(balance.(*big.Int).Int64(), big.NewInt(tc.burn).Int64())
+				}
+			} else {
+				suite.Require().Error(err, tc.name)
+			}
+		})
+	}
+	suite.mintFeeCollector = false
+}
+
+func (suite *KeeperTestSuite) TestConvertERC20NativeCoin() {
+	testCases := []struct {
+		name      string
+		mint      int64
+		burn      int64
+		reconvert int64
+		malleate  func()
+		expPass   bool
+	}{
+		{"ok - sufficient funds", 100, 10, 5, func() {}, true},
+		{"ok - equal funds", 10, 10, 10, func() {}, true},
+		{"fail - insufficient funds", 10, 1, 5, func() {}, false},
+		{"fail ", 10, 1, -5, func() {}, false},
+		{
+			"fail - deleted module account - force fail", 100, 10, 5,
+			func() {
+				acc := suite.app.AccountKeeper.GetAccount(suite.ctx, types.ModuleAddress.Bytes())
+				suite.app.AccountKeeper.RemoveAccount(suite.ctx, acc)
+			},
+			false,
+		},
+		{
+			"fail - force evm fail", 100, 10, 5,
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("forced ApplyMessage error"))
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			},
+			false,
+		},
+		{
+			"fail - force fail second balance", 100, 10, 5,
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				// first balance of
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				// convert coin
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, nil).Once()
+				// second balance of
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, fmt.Errorf("third")).Once()
+				// Extra call on test
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, nil)
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			},
+			false,
+		},
+		{
+			"fail - force fail second balance", 100, 10, 5,
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				// first balance of
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				// convert coin
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, nil).Once()
+				// second balance of
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				// Extra call on test
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, nil)
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			},
+			false,
+		},
+		{
+			"fail - force fail unescrow", 100, 10, 5,
+			func() {
+				mockBankKeeper := &MockBankKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, mockBankKeeper, suite.app.EvmKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				mockBankKeeper.On("SendCoinsFromModuleToAccount", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("failed to unescrow"))
+				mockBankKeeper.On("BlockedAddr", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(false)
+				mockBankKeeper.On("GetBalance", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(sdk.Coin{Denom: "coin", Amount: sdk.OneInt()})
+			},
+			false,
+		},
+		{
+			"fail - force fail balance after transfer", 100, 10, 5,
+			func() {
+				mockBankKeeper := &MockBankKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, mockBankKeeper, suite.app.EvmKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				mockBankKeeper.On("SendCoinsFromModuleToAccount", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
+				mockBankKeeper.On("BlockedAddr", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(false)
+				mockBankKeeper.On("GetBalance", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(sdk.Coin{Denom: "acoin", Amount: sdk.OneInt()})
+			},
+			false,
+		},
+	}
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.mintFeeCollector = true
+			suite.SetupTest()
+			metadata, pair := suite.setupRegisterCoin()
+			suite.Require().NotNil(metadata)
+			suite.Require().NotNil(pair)
+
+			// Precondition: Convert Coin to ERC20
+			coins := sdk.NewCoins(sdk.NewCoin(cosmosTokenBase, sdk.NewInt(tc.mint)))
+			sender := sdk.AccAddress(suite.address.Bytes())
+			suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
+			suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			msg := types.NewMsgConvertCoin(
+				sdk.NewCoin(cosmosTokenBase, sdk.NewInt(tc.burn)),
+				suite.address,
+				sender,
+			)
+
+			ctx := sdk.WrapSDKContext(suite.ctx)
+			_, err := suite.app.Erc20Keeper.ConvertCoin(ctx, msg)
+			suite.Require().NoError(err, tc.name)
+			suite.Commit()
+			balance := suite.BalanceOf(common.HexToAddress(pair.Erc20Address), suite.address)
+			cosmosBalance := suite.app.BankKeeper.GetBalance(suite.ctx, sender, metadata.Base)
+			suite.Require().Equal(cosmosBalance.Amount.Int64(), sdk.NewInt(tc.mint-tc.burn).Int64())
+			suite.Require().Equal(balance, big.NewInt(tc.burn))
+
+			// Convert ERC20s back to Coins
+			ctx = sdk.WrapSDKContext(suite.ctx)
+			contractAddr := common.HexToAddress(pair.Erc20Address)
+			msgConvertERC20 := types.NewMsgConvertERC20(
+				sdk.NewInt(tc.reconvert),
+				sender,
+				contractAddr,
+				suite.address,
+			)
+
+			tc.malleate()
+			res, err := suite.app.Erc20Keeper.ConvertERC20(ctx, msgConvertERC20)
+			expRes := &types.MsgConvertERC20Response{}
+			suite.Commit()
+			balance = suite.BalanceOf(contractAddr, suite.address)
+			cosmosBalance = suite.app.BankKeeper.GetBalance(suite.ctx, sender, pair.Denom)
+			if tc.expPass {
+				suite.Require().NoError(err, tc.name)
+				suite.Require().Equal(expRes, res)
+				suite.Require().Equal(cosmosBalance.Amount.Int64(), sdk.NewInt(tc.mint-tc.burn+tc.reconvert).Int64())
+				suite.Require().Equal(balance.(*big.Int).Int64(), big.NewInt(tc.burn-tc.reconvert).Int64())
+			} else {
+				suite.Require().Error(err, tc.name)
+			}
+		})
+	}
+	suite.mintFeeCollector = false
+}
+
+func (suite *KeeperTestSuite) TestConvertERC20NativeERC20() {
+	var contractAddr common.Address
+	var coinName string
+
+	testCases := []struct {
+		name           string
+		mint           int64
+		transfer       int64
+		malleate       func(common.Address)
+		extra          func()
+		contractType   int
+		expPass        bool
+		selfdestructed bool
+	}{
+		{
+			"ok - sufficient funds",
+			100,
+			10,
+			func(common.Address) {},
+			func() {},
+			contractMinterBurner,
+			true,
+			false,
+		},
+		{
+			"ok - equal funds",
+			10,
+			10,
+			func(common.Address) {},
+			func() {},
+			contractMinterBurner,
+			true,
+			false,
+		},
+		{
+			"ok - equal funds",
+			10,
+			10,
+			func(common.Address) {},
+			func() {},
+			contractMinterBurner,
+			true,
+			false,
+		},
+		{
+			"ok - suicided contract",
+			10,
+			10,
+			func(erc20 common.Address) {
+				stateDb := suite.StateDB()
+				ok := stateDb.Suicide(erc20)
+				suite.Require().True(ok)
+				suite.Require().NoError(stateDb.Commit())
+			},
+			func() {},
+			contractMinterBurner,
+			true,
+			true,
+		},
+		{
+			"fail - insufficient funds - callEVM",
+			0,
+			10,
+			func(common.Address) {},
+			func() {},
+			contractMinterBurner,
+			false,
+			false,
+		},
+		{
+			"fail - minting disabled",
+			100,
+			10,
+			func(common.Address) {
+				params := types.DefaultParams()
+				params.EnableErc20 = false
+				suite.app.Erc20Keeper.SetParams(suite.ctx, params)
+			},
+			func() {},
+			contractMinterBurner,
+			false,
+			false,
+		},
+		{
+			"fail - direct balance manipulation contract",
+			100,
+			10,
+			func(common.Address) {},
+			func() {},
+			contractDirectBalanceManipulation,
+			false,
+			false,
+		},
+		{
+			"fail - delayed malicious contract",
+			10,
+			10,
+			func(common.Address) {},
+			func() {},
+			contractMaliciousDelayed,
+			false,
+			false,
+		},
+		{
+			"fail - negative transfer contract",
+			10,
+			-10,
+			func(common.Address) {},
+			func() {},
+			contractMinterBurner,
+			false,
+			false,
+		},
+		{
+			"fail - no module address",
+			100,
+			10,
+			func(common.Address) {
+			},
+			func() {
+				acc := suite.app.AccountKeeper.GetAccount(suite.ctx, types.ModuleAddress.Bytes())
+				suite.app.AccountKeeper.RemoveAccount(suite.ctx, acc)
+			},
+			contractMinterBurner,
+			false,
+			false,
+		},
+		{
+			"fail - force evm fail",
+			100,
+			10,
+			func(common.Address) {},
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("forced ApplyMessage error"))
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			},
+			contractMinterBurner,
+			false,
+			false,
+		},
+		{
+			"fail - force get balance fail",
+			100,
+			10,
+			func(common.Address) {},
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				balance[31] = uint8(1)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Twice()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("forced balance error"))
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			},
+			contractMinterBurner,
+			false,
+			false,
+		},
+		{
+			"fail - force transfer unpack fail",
+			100,
+			10,
+			func(common.Address) {},
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, nil)
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			},
+			contractMinterBurner,
+			false,
+			false,
+		},
+
+		{
+			"fail - force invalid transfer fail",
+			100,
+			10,
+			func(common.Address) {},
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil)
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			},
+			contractMinterBurner,
+			false,
+			false,
+		},
+		{
+			"fail - force mint fail",
+			100,
+			10,
+			func(common.Address) {},
+			func() {
+				mockBankKeeper := &MockBankKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, mockBankKeeper, suite.app.EvmKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				mockBankKeeper.On("MintCoins", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("failed to mint"))
+				mockBankKeeper.On("SendCoinsFromModuleToAccount", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("failed to unescrow"))
+				mockBankKeeper.On("BlockedAddr", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(false)
+				mockBankKeeper.On("GetBalance", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(sdk.Coin{Denom: "coin", Amount: sdk.OneInt()})
+			},
+			contractMinterBurner,
+			false,
+			false,
+		},
+		{
+			"fail - force send minted fail",
+			100,
+			10,
+			func(common.Address) {},
+			func() {
+				mockBankKeeper := &MockBankKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, mockBankKeeper, suite.app.EvmKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				mockBankKeeper.On("MintCoins", mock.Anything, mock.Anything, mock.Anything).Return(nil)
+				mockBankKeeper.On("SendCoinsFromModuleToAccount", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("failed to unescrow"))
+				mockBankKeeper.On("BlockedAddr", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(false)
+				mockBankKeeper.On("GetBalance", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(sdk.Coin{Denom: "coin", Amount: sdk.OneInt()})
+			},
+			contractMinterBurner,
+			false,
+			false,
+		},
+		{
+			"fail - force bank balance fail",
+			100,
+			10,
+			func(common.Address) {},
+			func() {
+				mockBankKeeper := &MockBankKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, mockBankKeeper, suite.app.EvmKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				mockBankKeeper.On("MintCoins", mock.Anything, mock.Anything, mock.Anything).Return(nil)
+				mockBankKeeper.On("SendCoinsFromModuleToAccount", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
+				mockBankKeeper.On("BlockedAddr", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(false)
+				mockBankKeeper.On("GetBalance", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(sdk.Coin{Denom: coinName, Amount: sdk.NewInt(int64(10))})
+			},
+			contractMinterBurner,
+			false,
+			false,
+		},
+	}
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.mintFeeCollector = true
+			suite.SetupTest()
+
+			contractAddr = suite.setupRegisterERC20Pair(tc.contractType)
+
+			tc.malleate(contractAddr)
+			suite.Require().NotNil(contractAddr)
+			suite.Commit()
+
+			coinName = types.CreateDenom(contractAddr.String())
+			sender := sdk.AccAddress(suite.address.Bytes())
+			msg := types.NewMsgConvertERC20(
+				sdk.NewInt(tc.transfer),
+				sender,
+				contractAddr,
+				suite.address,
+			)
+
+			suite.MintERC20Token(contractAddr, suite.address, suite.address, big.NewInt(tc.mint))
+			suite.Commit()
+			ctx := sdk.WrapSDKContext(suite.ctx)
+
+			tc.extra()
+			res, err := suite.app.Erc20Keeper.ConvertERC20(ctx, msg)
+
+			expRes := &types.MsgConvertERC20Response{}
+			suite.Commit()
+			balance := suite.BalanceOf(contractAddr, suite.address)
+			cosmosBalance := suite.app.BankKeeper.GetBalance(suite.ctx, sender, coinName)
+			if tc.expPass {
+				suite.Require().NoError(err, tc.name)
+
+				acc := suite.app.EvmKeeper.GetAccountWithoutBalance(suite.ctx, contractAddr)
+				if tc.selfdestructed {
+					suite.Require().Nil(acc, "expected contract to be destroyed")
+				} else {
+					suite.Require().NotNil(acc)
+				}
+
+				if tc.selfdestructed || !acc.IsContract() {
+					id := suite.app.Erc20Keeper.GetTokenPairID(suite.ctx, contractAddr.String())
+					_, found := suite.app.Erc20Keeper.GetTokenPair(suite.ctx, id)
+					suite.Require().False(found)
+				} else {
+					suite.Require().Equal(expRes, res)
+					suite.Require().Equal(cosmosBalance.Amount, sdk.NewInt(tc.transfer))
+					suite.Require().Equal(balance.(*big.Int).Int64(), big.NewInt(tc.mint-tc.transfer).Int64())
+				}
+			} else {
+				suite.Require().Error(err, tc.name)
+			}
+		})
+	}
+	suite.mintFeeCollector = false
+}
+
+func (suite *KeeperTestSuite) TestConvertCoinNativeERC20() {
+	var contractAddr common.Address
+
+	testCases := []struct {
+		name         string
+		mint         int64
+		convert      int64
+		malleate     func(common.Address)
+		extra        func()
+		contractType int
+		expPass      bool
+	}{
+		{
+			"ok - sufficient funds",
+			100,
+			10,
+			func(common.Address) {},
+			func() {},
+			contractMinterBurner,
+			true,
+		},
+		{
+			"ok - equal funds",
+			100,
+			100,
+			func(common.Address) {},
+			func() {},
+			contractMinterBurner,
+			true,
+		},
+		{
+			"fail - insufficient funds",
+			100,
+			200,
+			func(common.Address) {},
+			func() {},
+			contractMinterBurner,
+			false,
+		},
+		{
+			"fail - direct balance manipulation contract",
+			100,
+			10,
+			func(common.Address) {},
+			func() {},
+			contractDirectBalanceManipulation,
+			false,
+		},
+		{
+			"fail - malicious delayed contract",
+			100,
+			10,
+			func(common.Address) {},
+			func() {},
+			contractMaliciousDelayed,
+			false,
+		},
+		{
+			"fail - deleted module address - force fail",
+			100,
+			10,
+			func(common.Address) {},
+			func() {
+				acc := suite.app.AccountKeeper.GetAccount(suite.ctx, types.ModuleAddress.Bytes())
+				suite.app.AccountKeeper.RemoveAccount(suite.ctx, acc)
+			},
+			contractMinterBurner,
+			false,
+		},
+		{
+			"fail - force evm fail",
+			100,
+			10,
+			func(common.Address) {},
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("forced ApplyMessage error"))
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			},
+			contractMinterBurner,
+			false,
+		},
+		{
+			"fail - force invalid transfer",
+			100,
+			10,
+			func(common.Address) {},
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, nil)
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			},
+			contractMinterBurner,
+			false,
+		},
+		{
+			"fail - force fail second balance",
+			100,
+			10,
+			func(common.Address) {},
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				balance[31] = uint8(1)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Twice()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("fail second balance"))
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			},
+			contractMinterBurner,
+			false,
+		},
+		{
+			"fail - force fail transfer",
+			100,
+			10,
+			func(common.Address) {},
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil)
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			},
+			contractMinterBurner,
+			false,
+		},
+	}
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.mintFeeCollector = true
+			suite.SetupTest()
+			contractAddr = suite.setupRegisterERC20Pair(tc.contractType)
+			suite.Require().NotNil(contractAddr)
+
+			id := suite.app.Erc20Keeper.GetTokenPairID(suite.ctx, contractAddr.String())
+			pair, _ := suite.app.Erc20Keeper.GetTokenPair(suite.ctx, id)
+			coins := sdk.NewCoins(sdk.NewCoin(pair.Denom, sdk.NewInt(tc.mint)))
+			coinName := types.CreateDenom(contractAddr.String())
+			sender := sdk.AccAddress(suite.address.Bytes())
+
+			// Precondition: Mint Coins to convert on sender account
+			suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
+			suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			cosmosBalance := suite.app.BankKeeper.GetBalance(suite.ctx, sender, coinName)
+			suite.Require().Equal(sdk.NewInt(tc.mint), cosmosBalance.Amount)
+
+			// Precondition: Mint escrow tokens on module account
+			suite.GrantERC20Token(contractAddr, suite.address, types.ModuleAddress, "MINTER_ROLE")
+			suite.MintERC20Token(contractAddr, types.ModuleAddress, types.ModuleAddress, big.NewInt(tc.mint))
+			tokenBalance := suite.BalanceOf(contractAddr, types.ModuleAddress)
+			suite.Require().Equal(big.NewInt(tc.mint), tokenBalance)
+
+			tc.malleate(contractAddr)
+			suite.Commit()
+
+			// Convert Coins back to ERC20s
+			receiver := suite.address
+			ctx := sdk.WrapSDKContext(suite.ctx)
+			msg := types.NewMsgConvertCoin(
+				sdk.NewCoin(coinName, sdk.NewInt(tc.convert)),
+				receiver,
+				sender,
+			)
+
+			tc.extra()
+			res, err := suite.app.Erc20Keeper.ConvertCoin(ctx, msg)
+
+			expRes := &types.MsgConvertCoinResponse{}
+			suite.Commit()
+			tokenBalance = suite.BalanceOf(contractAddr, suite.address)
+			cosmosBalance = suite.app.BankKeeper.GetBalance(suite.ctx, sender, coinName)
+			if tc.expPass {
+				suite.Require().NoError(err, tc.name)
+				suite.Require().Equal(expRes, res)
+				suite.Require().Equal(sdk.NewInt(tc.mint-tc.convert), cosmosBalance.Amount)
+				suite.Require().Equal(big.NewInt(tc.convert), tokenBalance.(*big.Int))
+			} else {
+				suite.Require().Error(err, tc.name)
+			}
+		})
+	}
+	suite.mintFeeCollector = false
+}
+
+func (suite *KeeperTestSuite) TestWrongPairOwnerERC20NativeCoin() {
+	testCases := []struct {
+		name      string
+		mint      int64
+		burn      int64
+		reconvert int64
+		expPass   bool
+	}{
+		{"ok - sufficient funds", 100, 10, 5, true},
+	}
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.mintFeeCollector = true
+			suite.SetupTest()
+			metadata, pair := suite.setupRegisterCoin()
+			suite.Require().NotNil(metadata)
+			suite.Require().NotNil(pair)
+
+			// Precondition: Convert Coin to ERC20
+			coins := sdk.NewCoins(sdk.NewCoin(cosmosTokenBase, sdk.NewInt(tc.mint)))
+			sender := sdk.AccAddress(suite.address.Bytes())
+			suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
+			suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			msg := types.NewMsgConvertCoin(
+				sdk.NewCoin(cosmosTokenBase, sdk.NewInt(tc.burn)),
+				suite.address,
+				sender,
+			)
+
+			pair.ContractOwner = types.OWNER_UNSPECIFIED
+			suite.app.Erc20Keeper.SetTokenPair(suite.ctx, *pair)
+
+			ctx := sdk.WrapSDKContext(suite.ctx)
+			_, err := suite.app.Erc20Keeper.ConvertCoin(ctx, msg)
+			suite.Require().Error(err, tc.name)
+
+			// Convert ERC20s back to Coins
+			ctx = sdk.WrapSDKContext(suite.ctx)
+			contractAddr := common.HexToAddress(pair.Erc20Address)
+			msgConvertERC20 := types.NewMsgConvertERC20(
+				sdk.NewInt(tc.reconvert),
+				sender,
+				contractAddr,
+				suite.address,
+			)
+
+			_, err = suite.app.Erc20Keeper.ConvertERC20(ctx, msgConvertERC20)
+			suite.Require().Error(err, tc.name)
+		})
+	}
+}
+
+func (suite *KeeperTestSuite) TestConvertCoinNativeIBCVoucher() {
+	testCases := []struct {
+		name           string
+		mint           int64
+		burn           int64
+		malleate       func(common.Address)
+		extra          func()
+		expPass        bool
+		selfdestructed bool
+	}{
+		{
+			"ok - sufficient funds",
+			100,
+			10,
+			func(common.Address) {},
+			func() {},
+			true,
+			false,
+		},
+		{
+			"ok - equal funds",
+			10,
+			10,
+			func(common.Address) {},
+			func() {},
+			true,
+			false,
+		},
+		{
+			"ok - suicided contract",
+			10,
+			10,
+			func(erc20 common.Address) {
+				stateDb := suite.StateDB()
+				ok := stateDb.Suicide(erc20)
+				suite.Require().True(ok)
+				suite.Require().NoError(stateDb.Commit())
+			},
+			func() {},
+			true,
+			true,
+		},
+		{
+			"fail - insufficient funds",
+			0,
+			10,
+			func(common.Address) {},
+			func() {},
+			false,
+			false,
+		},
+		{
+			"fail - minting disabled",
+			100,
+			10,
+			func(common.Address) {
+				params := types.DefaultParams()
+				params.EnableErc20 = false
+				suite.app.Erc20Keeper.SetParams(suite.ctx, params)
+			},
+			func() {},
+			false,
+			false,
+		},
+		{
+			"fail - deleted module account - force fail", 100, 10, func(common.Address) {},
+			func() {
+				acc := suite.app.AccountKeeper.GetAccount(suite.ctx, types.ModuleAddress.Bytes())
+				suite.app.AccountKeeper.RemoveAccount(suite.ctx, acc)
+			}, false, false,
+		},
+		{
+			"fail - force evm fail", 100, 10, func(common.Address) {},
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("forced ApplyMessage error"))
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			}, false, false,
+		},
+		{
+			"fail - force evm balance error", 100, 10, func(common.Address) {},
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				// first balance of
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				// convert coin
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, nil).Once()
+				// second balance of
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, fmt.Errorf("third")).Once()
+				// Extra call on test
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, nil)
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			}, false, false,
+		},
+		{
+			"fail - force balance error", 100, 10, func(common.Address) {},
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Times(4)
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			}, false, false,
+		},
+	}
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.mintFeeCollector = true
+			suite.SetupTest()
+			metadata, pair := suite.setupRegisterIBCVoucher()
+			suite.Require().NotNil(metadata)
+			erc20 := pair.GetERC20Contract()
+			tc.malleate(erc20)
+			suite.Commit()
+
+			ctx := sdk.WrapSDKContext(suite.ctx)
+			coins := sdk.NewCoins(sdk.NewCoin(ibcBase, sdk.NewInt(tc.mint)))
+			sender := sdk.AccAddress(suite.address.Bytes())
+			msg := types.NewMsgConvertCoin(
+				sdk.NewCoin(ibcBase, sdk.NewInt(tc.burn)),
+				suite.address,
+				sender,
+			)
+
+			suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
+			suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+
+			tc.extra()
+			res, err := suite.app.Erc20Keeper.ConvertCoin(ctx, msg)
+			expRes := &types.MsgConvertCoinResponse{}
+			suite.Commit()
+			balance := suite.BalanceOf(common.HexToAddress(pair.Erc20Address), suite.address)
+			cosmosBalance := suite.app.BankKeeper.GetBalance(suite.ctx, sender, metadata.Base)
+
+			if tc.expPass {
+				suite.Require().NoError(err, tc.name)
+
+				acc := suite.app.EvmKeeper.GetAccountWithoutBalance(suite.ctx, erc20)
+				if tc.selfdestructed {
+					suite.Require().Nil(acc, "expected contract to be destroyed")
+				} else {
+					suite.Require().NotNil(acc)
+				}
+
+				if tc.selfdestructed || !acc.IsContract() {
+					id := suite.app.Erc20Keeper.GetTokenPairID(suite.ctx, erc20.String())
+					_, found := suite.app.Erc20Keeper.GetTokenPair(suite.ctx, id)
+					suite.Require().False(found)
+				} else {
+					suite.Require().Equal(expRes, res)
+					suite.Require().Equal(cosmosBalance.Amount.Int64(), sdk.NewInt(tc.mint-tc.burn).Int64())
+					suite.Require().Equal(balance.(*big.Int).Int64(), big.NewInt(tc.burn).Int64())
+				}
+			} else {
+				suite.Require().Error(err, tc.name)
+			}
+		})
+	}
+	suite.mintFeeCollector = false
+}
+
+func (suite *KeeperTestSuite) TestConvertERC20NativeIBCVoucher() {
+	testCases := []struct {
+		name      string
+		mint      int64
+		burn      int64
+		reconvert int64
+		malleate  func()
+		expPass   bool
+	}{
+		{"ok - sufficient funds", 100, 10, 5, func() {}, true},
+		{"ok - equal funds", 10, 10, 10, func() {}, true},
+		{"fail - insufficient funds", 10, 1, 5, func() {}, false},
+		{"fail ", 10, 1, -5, func() {}, false},
+		{
+			"fail - deleted module account - force fail", 100, 10, 5,
+			func() {
+				acc := suite.app.AccountKeeper.GetAccount(suite.ctx, types.ModuleAddress.Bytes())
+				suite.app.AccountKeeper.RemoveAccount(suite.ctx, acc)
+			},
+			false,
+		},
+		{
+			"fail - force evm fail", 100, 10, 5,
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("forced ApplyMessage error"))
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			},
+			false,
+		},
+		{
+			"fail - force fail second balance", 100, 10, 5,
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				// first balance of
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				// convert coin
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, nil).Once()
+				// second balance of
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, fmt.Errorf("third")).Once()
+				// Extra call on test
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, nil)
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			},
+			false,
+		},
+		{
+			"fail - force fail second balance", 100, 10, 5,
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
+				balance := make([]uint8, 32)
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				// first balance of
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				// convert coin
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, nil).Once()
+				// second balance of
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
+				// Extra call on test
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, nil)
+				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
+			},
+			false,
+		},
+		{
+			"fail - force fail unescrow", 100, 10, 5,
+			func() {
+				mockBankKeeper := &MockBankKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, mockBankKeeper, suite.app.EvmKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				mockBankKeeper.On("SendCoinsFromModuleToAccount", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("failed to unescrow"))
+				mockBankKeeper.On("BlockedAddr", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(false)
+				mockBankKeeper.On("GetBalance", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(sdk.Coin{Denom: "coin", Amount: sdk.OneInt()})
+			},
+			false,
+		},
+		{
+			"fail - force fail balance after transfer", 100, 10, 5,
+			func() {
+				mockBankKeeper := &MockBankKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, mockBankKeeper, suite.app.EvmKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+
+				mockBankKeeper.On("SendCoinsFromModuleToAccount", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
+				mockBankKeeper.On("BlockedAddr", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(false)
+				mockBankKeeper.On("GetBalance", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(sdk.Coin{Denom: ibcBase, Amount: sdk.OneInt()})
+			},
+			false,
+		},
+	}
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.mintFeeCollector = true
+			suite.SetupTest()
+			metadata, pair := suite.setupRegisterIBCVoucher()
+			suite.Require().NotNil(metadata)
+			suite.Require().NotNil(pair)
+
+			// Precondition: Convert Coin to ERC20
+			coins := sdk.NewCoins(sdk.NewCoin(ibcBase, sdk.NewInt(tc.mint)))
+			sender := sdk.AccAddress(suite.address.Bytes())
+			suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
+			suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			msg := types.NewMsgConvertCoin(
+				sdk.NewCoin(ibcBase, sdk.NewInt(tc.burn)),
+				suite.address,
+				sender,
+			)
+
+			ctx := sdk.WrapSDKContext(suite.ctx)
+			_, err := suite.app.Erc20Keeper.ConvertCoin(ctx, msg)
+			suite.Require().NoError(err, tc.name)
+			suite.Commit()
+			balance := suite.BalanceOf(common.HexToAddress(pair.Erc20Address), suite.address)
+			cosmosBalance := suite.app.BankKeeper.GetBalance(suite.ctx, sender, metadata.Base)
+			suite.Require().Equal(cosmosBalance.Amount.Int64(), sdk.NewInt(tc.mint-tc.burn).Int64())
+			suite.Require().Equal(balance, big.NewInt(tc.burn))
+
+			// Convert ERC20s back to Coins
+			ctx = sdk.WrapSDKContext(suite.ctx)
+			contractAddr := common.HexToAddress(pair.Erc20Address)
+			msgConvertERC20 := types.NewMsgConvertERC20(
+				sdk.NewInt(tc.reconvert),
+				sender,
+				contractAddr,
+				suite.address,
+			)
+
+			tc.malleate()
+			res, err := suite.app.Erc20Keeper.ConvertERC20(ctx, msgConvertERC20)
+			expRes := &types.MsgConvertERC20Response{}
+			suite.Commit()
+			balance = suite.BalanceOf(contractAddr, suite.address)
+			cosmosBalance = suite.app.BankKeeper.GetBalance(suite.ctx, sender, pair.Denom)
+			if tc.expPass {
+				suite.Require().NoError(err, tc.name)
+				suite.Require().Equal(expRes, res)
+				suite.Require().Equal(cosmosBalance.Amount.Int64(), sdk.NewInt(tc.mint-tc.burn+tc.reconvert).Int64())
+				suite.Require().Equal(balance.(*big.Int).Int64(), big.NewInt(tc.burn-tc.reconvert).Int64())
+			} else {
+				suite.Require().Error(err, tc.name)
+			}
+		})
+	}
+	suite.mintFeeCollector = false
+}
diff --git a/x/erc20/keeper/params.go b/x/erc20/keeper/params.go
new file mode 100644
index 00000000..ff5c294e
--- /dev/null
+++ b/x/erc20/keeper/params.go
@@ -0,0 +1,18 @@
+package keeper
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+// GetParams returns the total set of erc20 parameters.
+func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
+	k.paramstore.GetParamSet(ctx, &params)
+	return params
+}
+
+// SetParams sets the erc20 parameters to the param space.
+func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
+	k.paramstore.SetParamSet(ctx, &params)
+}
diff --git a/x/erc20/keeper/params_test.go b/x/erc20/keeper/params_test.go
new file mode 100644
index 00000000..aee894c1
--- /dev/null
+++ b/x/erc20/keeper/params_test.go
@@ -0,0 +1,12 @@
+package keeper_test
+
+import "github.com/AltheaFoundation/althea-L1/x/erc20/types"
+
+func (suite *KeeperTestSuite) TestParams() {
+	params := suite.app.Erc20Keeper.GetParams(suite.ctx)
+	suite.Require().Equal(types.DefaultParams(), params)
+	params.EnableErc20 = false
+	suite.app.Erc20Keeper.SetParams(suite.ctx, params)
+	newParams := suite.app.Erc20Keeper.GetParams(suite.ctx)
+	suite.Require().Equal(newParams, params)
+}
diff --git a/x/erc20/keeper/proposals.go b/x/erc20/keeper/proposals.go
new file mode 100644
index 00000000..dbbee327
--- /dev/null
+++ b/x/erc20/keeper/proposals.go
@@ -0,0 +1,216 @@
+package keeper
+
+import (
+	"strings"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+	"github.com/ethereum/go-ethereum/common"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+// RegisterCoin deploys an erc20 contract and creates the token pair for the
+// existing cosmos coin
+func (k Keeper) RegisterCoin(
+	ctx sdk.Context,
+	coinMetadata banktypes.Metadata,
+) (*types.TokenPair, error) {
+	// Check if the conversion is globally enabled
+	params := k.GetParams(ctx)
+	if !params.EnableErc20 {
+		return nil, sdkerrors.Wrap(
+			types.ErrERC20Disabled, "registration is currently disabled by governance",
+		)
+	}
+
+	// Prohibit denominations that contain the evm denom
+	if strings.Contains(coinMetadata.Base, "CANTO") {
+		return nil, sdkerrors.Wrapf(
+			types.ErrEVMDenom, "cannot register the EVM denomination %s", coinMetadata.Base,
+		)
+	}
+
+	// Check if denomination is already registered
+	if k.IsDenomRegistered(ctx, coinMetadata.Name) {
+		return nil, sdkerrors.Wrapf(
+			types.ErrTokenPairAlreadyExists, "coin denomination already registered: %s", coinMetadata.Name,
+		)
+	}
+
+	// Check if the coin exists by ensuring the supply is set
+	if !k.bankKeeper.HasSupply(ctx, coinMetadata.Base) {
+		return nil, sdkerrors.Wrapf(
+			sdkerrors.ErrInvalidCoins, "base denomination '%s' cannot have a supply of 0", coinMetadata.Base,
+		)
+	}
+
+	if err := k.verifyMetadata(ctx, coinMetadata); err != nil {
+		return nil, sdkerrors.Wrapf(
+			types.ErrInternalTokenPair, "coin metadata is invalid %s", coinMetadata.Name,
+		)
+	}
+
+	addr, err := k.DeployERC20Contract(ctx, coinMetadata)
+	if err != nil {
+		return nil, sdkerrors.Wrap(
+			err, "failed to create wrapped coin denom metadata for ERC20",
+		)
+	}
+
+	pair := types.NewTokenPair(addr, coinMetadata.Base, true, types.OWNER_MODULE)
+	k.SetTokenPair(ctx, pair)
+	k.SetDenomMap(ctx, pair.Denom, pair.GetID())
+	k.SetERC20Map(ctx, common.HexToAddress(pair.Erc20Address), pair.GetID())
+
+	return &pair, nil
+}
+
+// RegisterERC20 creates a Cosmos coin and registers the token pair between the
+// coin and the ERC20
+func (k Keeper) RegisterERC20(
+	ctx sdk.Context,
+	contract common.Address,
+) (*types.TokenPair, error) {
+	// Check if the conversion is globally enabled
+	params := k.GetParams(ctx)
+	if !params.EnableErc20 {
+		return nil, sdkerrors.Wrap(
+			types.ErrERC20Disabled, "registration is currently disabled by governance",
+		)
+	}
+
+	// Check if ERC20 is already registered
+	if k.IsERC20Registered(ctx, contract) {
+		return nil, sdkerrors.Wrapf(
+			types.ErrTokenPairAlreadyExists, "token ERC20 contract already registered: %s", contract.String(),
+		)
+	}
+
+	metadata, err := k.CreateCoinMetadata(ctx, contract)
+	if err != nil {
+		return nil, sdkerrors.Wrap(
+			err, "failed to create wrapped coin denom metadata for ERC20",
+		)
+	}
+
+	pair := types.NewTokenPair(contract, metadata.Name, true, types.OWNER_EXTERNAL)
+	k.SetTokenPair(ctx, pair)
+	k.SetDenomMap(ctx, pair.Denom, pair.GetID())
+	k.SetERC20Map(ctx, common.HexToAddress(pair.Erc20Address), pair.GetID())
+	return &pair, nil
+}
+
+// CreateCoinMetadata generates the metadata to represent the ERC20 token on
+// canto.
+func (k Keeper) CreateCoinMetadata(
+	ctx sdk.Context,
+	contract common.Address,
+) (*banktypes.Metadata, error) {
+	strContract := contract.String()
+
+	erc20Data, err := k.QueryERC20(ctx, contract)
+	if err != nil {
+		return nil, err
+	}
+
+	// Check if metadata already exists
+	_, found := k.bankKeeper.GetDenomMetaData(ctx, types.CreateDenom(strContract))
+	if found {
+		return nil, sdkerrors.Wrap(
+			types.ErrInternalTokenPair, "denom metadata already registered",
+		)
+	}
+
+	if k.IsDenomRegistered(ctx, types.CreateDenom(strContract)) {
+		return nil, sdkerrors.Wrapf(
+			types.ErrInternalTokenPair, "coin denomination already registered: %s", erc20Data.Name,
+		)
+	}
+
+	// base denomination
+	base := types.CreateDenom(strContract)
+
+	// create a bank denom metadata based on the ERC20 token ABI details
+	// metadata name is should always be the contract since it's the key
+	// to the bank store
+	metadata := banktypes.Metadata{
+		Description: types.CreateDenomDescription(strContract),
+		Base:        base,
+		// NOTE: Denom units MUST be increasing
+		DenomUnits: []*banktypes.DenomUnit{
+			{
+				Denom:    base,
+				Exponent: 0,
+			},
+		},
+		Name:    types.CreateDenom(strContract),
+		Symbol:  erc20Data.Symbol,
+		Display: base,
+	}
+
+	// only append metadata if decimals > 0, otherwise validation fails
+	if erc20Data.Decimals > 0 {
+		nameSanitized := types.SanitizeERC20Name(erc20Data.Name)
+		metadata.DenomUnits = append(
+			metadata.DenomUnits,
+			&banktypes.DenomUnit{
+				Denom:    nameSanitized,
+				Exponent: uint32(erc20Data.Decimals),
+			},
+		)
+		metadata.Display = nameSanitized
+	}
+
+	if err := metadata.Validate(); err != nil {
+		return nil, sdkerrors.Wrapf(
+			err, "ERC20 token data is invalid for contract %s", strContract,
+		)
+	}
+
+	k.bankKeeper.SetDenomMetaData(ctx, metadata)
+
+	return &metadata, nil
+}
+
+// ToggleConversion toggles conversion for a given token pair
+func (k Keeper) ToggleConversion(
+	ctx sdk.Context,
+	token string,
+) (types.TokenPair, error) {
+	id := k.GetTokenPairID(ctx, token)
+	if len(id) == 0 {
+		return types.TokenPair{}, sdkerrors.Wrapf(
+			types.ErrTokenPairNotFound, "token '%s' not registered by id", token,
+		)
+	}
+
+	pair, found := k.GetTokenPair(ctx, id)
+	if !found {
+		return types.TokenPair{}, sdkerrors.Wrapf(
+			types.ErrTokenPairNotFound, "token '%s' not registered", token,
+		)
+	}
+
+	pair.Enabled = !pair.Enabled
+
+	k.SetTokenPair(ctx, pair)
+	return pair, nil
+}
+
+// verifyMetadata verifies if the metadata matches the existing one, if not it
+// sets it to the store
+func (k Keeper) verifyMetadata(
+	ctx sdk.Context,
+	coinMetadata banktypes.Metadata,
+) error {
+	meta, found := k.bankKeeper.GetDenomMetaData(ctx, coinMetadata.Base)
+	if !found {
+		k.bankKeeper.SetDenomMetaData(ctx, coinMetadata)
+		return nil
+	}
+
+	// If it already existed, check that is equal to what is stored
+	return types.EqualMetadata(meta, coinMetadata)
+}
diff --git a/x/erc20/keeper/proposals_test.go b/x/erc20/keeper/proposals_test.go
new file mode 100644
index 00000000..7910fe8c
--- /dev/null
+++ b/x/erc20/keeper/proposals_test.go
@@ -0,0 +1,473 @@
+package keeper_test
+
+import (
+	"fmt"
+
+	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"
+	"github.com/stretchr/testify/mock"
+
+	"github.com/ethereum/go-ethereum/common"
+
+	"github.com/evmos/ethermint/tests"
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/keeper"
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+const (
+	contractMinterBurner = iota + 1
+	contractDirectBalanceManipulation
+	contractMaliciousDelayed
+)
+
+const (
+	erc20Name          = "Coin Token"
+	erc20Symbol        = "CTKN"
+	erc20Decimals      = uint8(18)
+	cosmosTokenBase    = "acoin"
+	cosmosTokenDisplay = "coin"
+	cosmosDecimals     = uint8(6)
+	defaultExponent    = uint32(18)
+	zeroExponent       = uint32(0)
+	ibcBase            = "ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2"
+)
+
+func (suite *KeeperTestSuite) setupRegisterERC20Pair(contractType int) common.Address {
+	var contract common.Address
+	// Deploy contract
+	switch contractType {
+	case contractDirectBalanceManipulation:
+		contract = suite.DeployContractDirectBalanceManipulation(erc20Name, erc20Symbol)
+	case contractMaliciousDelayed:
+		contract = suite.DeployContractMaliciousDelayed(erc20Name, erc20Symbol)
+	default:
+		contract, _ = suite.DeployContract(erc20Name, erc20Symbol, erc20Decimals)
+	}
+	suite.Commit()
+
+	_, err := suite.app.Erc20Keeper.RegisterERC20(suite.ctx, contract)
+	suite.Require().NoError(err)
+	return contract
+}
+
+func (suite *KeeperTestSuite) setupRegisterCoin() (banktypes.Metadata, *types.TokenPair) {
+	validMetadata := banktypes.Metadata{
+		Description: "description of the token",
+		Base:        cosmosTokenBase,
+		// NOTE: Denom units MUST be increasing
+		DenomUnits: []*banktypes.DenomUnit{
+			{
+				Denom:    cosmosTokenBase,
+				Exponent: 0,
+			},
+			{
+				Denom:    cosmosTokenBase[1:],
+				Exponent: uint32(18),
+			},
+		},
+		Name:    cosmosTokenBase,
+		Symbol:  erc20Symbol,
+		Display: cosmosTokenBase,
+	}
+
+	err := suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, sdk.Coins{sdk.NewInt64Coin(validMetadata.Base, 1)})
+	suite.Require().NoError(err)
+
+	// pair := types.NewTokenPair(contractAddr, cosmosTokenBase, true, types.OWNER_MODULE)
+	pair, err := suite.app.Erc20Keeper.RegisterCoin(suite.ctx, validMetadata)
+	suite.Require().NoError(err)
+	suite.Commit()
+	return validMetadata, pair
+}
+
+func (suite *KeeperTestSuite) setupRegisterIBCVoucher() (banktypes.Metadata, *types.TokenPair) {
+	suite.SetupTest()
+
+	validMetadata := banktypes.Metadata{
+		Description: "ATOM IBC voucher (channel 14)",
+		Base:        ibcBase,
+		// NOTE: Denom units MUST be increasing
+		DenomUnits: []*banktypes.DenomUnit{
+			{
+				Denom:    ibcBase,
+				Exponent: 0,
+			},
+		},
+		Name:    "ATOM channel-14",
+		Symbol:  "ibcATOM-14",
+		Display: ibcBase,
+	}
+
+	err := suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, sdk.Coins{sdk.NewInt64Coin(validMetadata.Base, 1)})
+	suite.Require().NoError(err)
+
+	// pair := types.NewTokenPair(contractAddr, cosmosTokenBase, true, types.OWNER_MODULE)
+	pair, err := suite.app.Erc20Keeper.RegisterCoin(suite.ctx, validMetadata)
+	suite.Require().NoError(err)
+	suite.Commit()
+	return validMetadata, pair
+}
+
+func (suite KeeperTestSuite) TestRegisterCoin() {
+	metadata := banktypes.Metadata{
+		Description: "description",
+		Base:        cosmosTokenBase,
+		// NOTE: Denom units MUST be increasing
+		DenomUnits: []*banktypes.DenomUnit{
+			{
+				Denom:    cosmosTokenBase,
+				Exponent: 0,
+			},
+			{
+				Denom:    cosmosTokenDisplay,
+				Exponent: defaultExponent,
+			},
+		},
+		Name:    cosmosTokenBase,
+		Symbol:  erc20Symbol,
+		Display: cosmosTokenDisplay,
+	}
+
+	testCases := []struct {
+		name     string
+		malleate func()
+		expPass  bool
+	}{
+		{
+			"conversion is disabled globally",
+			func() {
+				params := types.DefaultParams()
+				params.EnableErc20 = false
+				suite.app.Erc20Keeper.SetParams(suite.ctx, params)
+			},
+			false,
+		},
+		{
+			"denom already registered",
+			func() {
+				regPair := types.NewTokenPair(tests.GenerateAddress(), metadata.Base, true, types.OWNER_MODULE)
+				suite.app.Erc20Keeper.SetDenomMap(suite.ctx, regPair.Denom, regPair.GetID())
+				suite.Commit()
+			},
+			false,
+		},
+		{
+			"token doesn't have supply",
+			func() {
+			},
+			false,
+		},
+		{
+			"metadata different that stored",
+			func() {
+				metadata.Base = cosmosTokenBase
+				validMetadata := banktypes.Metadata{
+					Description: "description",
+					Base:        cosmosTokenBase,
+					// NOTE: Denom units MUST be increasing
+					DenomUnits: []*banktypes.DenomUnit{
+						{
+							Denom:    cosmosTokenBase,
+							Exponent: 0,
+						},
+						{
+							Denom:    cosmosTokenDisplay,
+							Exponent: uint32(18),
+						},
+					},
+					Name:    erc20Name,
+					Symbol:  erc20Symbol,
+					Display: cosmosTokenDisplay,
+				}
+
+				err := suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, sdk.Coins{sdk.NewInt64Coin(validMetadata.Base, 1)})
+				suite.Require().NoError(err)
+				suite.app.BankKeeper.SetDenomMetaData(suite.ctx, validMetadata)
+			},
+			false,
+		},
+		{
+			"evm denom registration - CANTO",
+			func() {
+				metadata.Base = "CANTO"
+				err := suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, sdk.Coins{sdk.NewInt64Coin(metadata.Base, 1)})
+				suite.Require().NoError(err)
+			},
+			false,
+		},
+		{
+			"evm denom registration - CANTO",
+			func() {
+				metadata.Base = "CANTO"
+				err := suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, sdk.Coins{sdk.NewInt64Coin(metadata.Base, 1)})
+				suite.Require().NoError(err)
+			},
+			false,
+		},
+		{
+			"evm denom registration - aCANTO",
+			func() {
+				metadata.Base = "aCANTO"
+				err := suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, sdk.Coins{sdk.NewInt64Coin(metadata.Base, 1)})
+				suite.Require().NoError(err)
+			},
+			false,
+		},
+		{
+			"evm denom registration - wCANTO",
+			func() {
+				metadata.Base = "wCANTO"
+				err := suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, sdk.Coins{sdk.NewInt64Coin(metadata.Base, 1)})
+				suite.Require().NoError(err)
+			},
+			false,
+		},
+		{
+			"ok",
+			func() {
+				metadata.Base = cosmosTokenBase
+				err := suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, sdk.Coins{sdk.NewInt64Coin(metadata.Base, 1)})
+				suite.Require().NoError(err)
+			},
+			true,
+		},
+		{
+			"force fail evm",
+			func() {
+				metadata.Base = cosmosTokenBase
+				err := suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, sdk.Coins{sdk.NewInt64Coin(metadata.Base, 1)})
+				suite.Require().NoError(err)
+
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("forced ApplyMessage error"))
+			},
+			false,
+		},
+		{
+			"force delete module account evm",
+			func() {
+				metadata.Base = cosmosTokenBase
+				err := suite.app.BankKeeper.MintCoins(suite.ctx, minttypes.ModuleName, sdk.Coins{sdk.NewInt64Coin(metadata.Base, 1)})
+				suite.Require().NoError(err)
+
+				acc := suite.app.AccountKeeper.GetAccount(suite.ctx, types.ModuleAddress.Bytes())
+				suite.app.AccountKeeper.RemoveAccount(suite.ctx, acc)
+			},
+			false,
+		},
+	}
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.SetupTest() // reset
+
+			tc.malleate()
+
+			pair, err := suite.app.Erc20Keeper.RegisterCoin(suite.ctx, metadata)
+			suite.Commit()
+
+			expPair := &types.TokenPair{
+				Erc20Address:  "0x80b5a32E4F032B2a058b4F29EC95EEfEEB87aDcd",
+				Denom:         "acoin",
+				Enabled:       true,
+				ContractOwner: 1,
+			}
+
+			if tc.expPass {
+				suite.Require().NoError(err, tc.name)
+				suite.Require().Equal(pair, expPair)
+			} else {
+				suite.Require().Error(err, tc.name)
+			}
+		})
+	}
+}
+
+func (suite KeeperTestSuite) TestRegisterERC20() {
+	var (
+		contractAddr common.Address
+		pair         types.TokenPair
+	)
+	testCases := []struct {
+		name     string
+		malleate func()
+		expPass  bool
+	}{
+		{
+			"conversion is disabled globally",
+			func() {
+				params := types.DefaultParams()
+				params.EnableErc20 = false
+				suite.app.Erc20Keeper.SetParams(suite.ctx, params)
+			},
+			false,
+		},
+		{
+			"token ERC20 already registered",
+			func() {
+				suite.app.Erc20Keeper.SetERC20Map(suite.ctx, pair.GetERC20Contract(), pair.GetID())
+			},
+			false,
+		},
+		{
+			"denom already registered",
+			func() {
+				suite.app.Erc20Keeper.SetDenomMap(suite.ctx, pair.Denom, pair.GetID())
+			},
+			false,
+		},
+		{
+			"meta data already stored",
+			func() {
+				suite.app.Erc20Keeper.CreateCoinMetadata(suite.ctx, contractAddr)
+			},
+			false,
+		},
+		{
+			"ok",
+			func() {},
+			true,
+		},
+		{
+			"force fail evm",
+			func() {
+				mockEVMKeeper := &MockEVMKeeper{}
+				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
+				suite.Require().True(found)
+				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
+				suite.app.Erc20Keeper = &erc20Keeper
+				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("forced ApplyMessage error"))
+			},
+			false,
+		},
+	}
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.SetupTest() // reset
+
+			var err error
+			contractAddr, err = suite.DeployContract(erc20Name, erc20Symbol, cosmosDecimals)
+			suite.Require().NoError(err)
+			suite.Commit()
+			coinName := types.CreateDenom(contractAddr.String())
+			pair = types.NewTokenPair(contractAddr, coinName, true, types.OWNER_EXTERNAL)
+
+			tc.malleate()
+
+			_, err = suite.app.Erc20Keeper.RegisterERC20(suite.ctx, contractAddr)
+			metadata, found := suite.app.BankKeeper.GetDenomMetaData(suite.ctx, coinName)
+			if tc.expPass {
+				suite.Require().NoError(err, tc.name)
+				// Metadata variables
+				suite.Require().True(found)
+				suite.Require().Equal(coinName, metadata.Base)
+				suite.Require().Equal(coinName, metadata.Name)
+				suite.Require().Equal(types.SanitizeERC20Name(erc20Name), metadata.Display)
+				suite.Require().Equal(erc20Symbol, metadata.Symbol)
+				// Denom units
+				suite.Require().Equal(len(metadata.DenomUnits), 2)
+				suite.Require().Equal(coinName, metadata.DenomUnits[0].Denom)
+				suite.Require().Equal(uint32(zeroExponent), metadata.DenomUnits[0].Exponent)
+				suite.Require().Equal(types.SanitizeERC20Name(erc20Name), metadata.DenomUnits[1].Denom)
+				// Custom exponent at contract creation matches coin with token
+				suite.Require().Equal(metadata.DenomUnits[1].Exponent, uint32(cosmosDecimals))
+			} else {
+				suite.Require().Error(err, tc.name)
+			}
+		})
+	}
+}
+
+func (suite *KeeperTestSuite) TestRegisterIBCVoucher() {
+	suite.setupRegisterIBCVoucher()
+}
+
+func (suite KeeperTestSuite) TestToggleConverision() {
+	var (
+		contractAddr common.Address
+		id           []byte
+		pair         types.TokenPair
+	)
+
+	testCases := []struct {
+		name              string
+		malleate          func()
+		expPass           bool
+		conversionEnabled bool
+	}{
+		{
+			"token not registered",
+			func() {
+				contractAddr, err := suite.DeployContract(erc20Name, erc20Symbol, erc20Decimals)
+				suite.Require().NoError(err)
+				suite.Commit()
+				pair = types.NewTokenPair(contractAddr, cosmosTokenBase, true, types.OWNER_MODULE)
+			},
+			false,
+			false,
+		},
+		{
+			"token not registered - pair not found",
+			func() {
+				contractAddr, err := suite.DeployContract(erc20Name, erc20Symbol, erc20Decimals)
+				suite.Require().NoError(err)
+				suite.Commit()
+				pair = types.NewTokenPair(contractAddr, cosmosTokenBase, true, types.OWNER_MODULE)
+				suite.app.Erc20Keeper.SetERC20Map(suite.ctx, common.HexToAddress(pair.Erc20Address), pair.GetID())
+			},
+			false,
+			false,
+		},
+		{
+			"disable conversion",
+			func() {
+				contractAddr = suite.setupRegisterERC20Pair(contractMinterBurner)
+				id = suite.app.Erc20Keeper.GetTokenPairID(suite.ctx, contractAddr.String())
+				pair, _ = suite.app.Erc20Keeper.GetTokenPair(suite.ctx, id)
+			},
+			true,
+			false,
+		},
+		{
+			"disable and enable conversion",
+			func() {
+				contractAddr = suite.setupRegisterERC20Pair(contractMinterBurner)
+				id = suite.app.Erc20Keeper.GetTokenPairID(suite.ctx, contractAddr.String())
+				pair, _ = suite.app.Erc20Keeper.GetTokenPair(suite.ctx, id)
+				pair, _ = suite.app.Erc20Keeper.ToggleConversion(suite.ctx, contractAddr.String())
+			},
+			true,
+			true,
+		},
+	}
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.SetupTest() // reset
+
+			tc.malleate()
+
+			var err error
+			pair, err = suite.app.Erc20Keeper.ToggleConversion(suite.ctx, contractAddr.String())
+			// Request the pair using the GetPairToken func to make sure that is updated on the db
+			pair, _ = suite.app.Erc20Keeper.GetTokenPair(suite.ctx, id)
+			if tc.expPass {
+				suite.Require().NoError(err, tc.name)
+				if tc.conversionEnabled {
+					suite.Require().True(pair.Enabled)
+				} else {
+					suite.Require().False(pair.Enabled)
+				}
+			} else {
+				suite.Require().Error(err, tc.name)
+			}
+		})
+	}
+}
diff --git a/x/erc20/keeper/token_pairs.go b/x/erc20/keeper/token_pairs.go
new file mode 100644
index 00000000..702ca05a
--- /dev/null
+++ b/x/erc20/keeper/token_pairs.go
@@ -0,0 +1,129 @@
+package keeper
+
+import (
+	"github.com/cosmos/cosmos-sdk/store/prefix"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/ethereum/go-ethereum/common"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+// GetTokenPairs - get all registered token tokenPairs
+func (k Keeper) GetTokenPairs(ctx sdk.Context) []types.TokenPair {
+	tokenPairs := []types.TokenPair{}
+
+	store := ctx.KVStore(k.storeKey)
+	iterator := sdk.KVStorePrefixIterator(store, types.KeyPrefixTokenPair)
+	defer iterator.Close()
+
+	for ; iterator.Valid(); iterator.Next() {
+		var tokenPair types.TokenPair
+		k.cdc.MustUnmarshal(iterator.Value(), &tokenPair)
+
+		tokenPairs = append(tokenPairs, tokenPair)
+	}
+
+	return tokenPairs
+}
+
+// GetTokenPairID returns the pair id from either of the registered tokens.
+func (k Keeper) GetTokenPairID(ctx sdk.Context, token string) []byte {
+	if common.IsHexAddress(token) {
+		addr := common.HexToAddress(token)
+		return k.GetERC20Map(ctx, addr)
+	}
+	return k.GetDenomMap(ctx, token)
+}
+
+// GetTokenPair - get registered token pair from the identifier
+func (k Keeper) GetTokenPair(ctx sdk.Context, id []byte) (types.TokenPair, bool) {
+	if id == nil {
+		return types.TokenPair{}, false
+	}
+
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixTokenPair)
+	var tokenPair types.TokenPair
+	bz := store.Get(id)
+	if len(bz) == 0 {
+		return types.TokenPair{}, false
+	}
+
+	k.cdc.MustUnmarshal(bz, &tokenPair)
+	return tokenPair, true
+}
+
+// SetTokenPair stores a token pair
+func (k Keeper) SetTokenPair(ctx sdk.Context, tokenPair types.TokenPair) {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixTokenPair)
+	key := tokenPair.GetID()
+	bz := k.cdc.MustMarshal(&tokenPair)
+	store.Set(key, bz)
+}
+
+// DeleteTokenPair removes a token pair.
+func (k Keeper) DeleteTokenPair(ctx sdk.Context, tokenPair types.TokenPair) {
+	id := tokenPair.GetID()
+	k.deleteTokenPair(ctx, id)
+	k.deleteERC20Map(ctx, tokenPair.GetERC20Contract())
+	k.deleteDenomMap(ctx, tokenPair.Denom)
+}
+
+// deleteTokenPair deletes the token pair for the given id
+func (k Keeper) deleteTokenPair(ctx sdk.Context, id []byte) {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixTokenPair)
+	store.Delete(id)
+}
+
+// GetERC20Map returns the token pair id for the given address
+func (k Keeper) GetERC20Map(ctx sdk.Context, erc20 common.Address) []byte {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixTokenPairByERC20)
+	return store.Get(erc20.Bytes())
+}
+
+// GetDenomMap returns the token pair id for the given denomination
+func (k Keeper) GetDenomMap(ctx sdk.Context, denom string) []byte {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixTokenPairByDenom)
+	return store.Get([]byte(denom))
+}
+
+// SetERC20Map sets the token pair id for the given address
+func (k Keeper) SetERC20Map(ctx sdk.Context, erc20 common.Address, id []byte) {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixTokenPairByERC20)
+	store.Set(erc20.Bytes(), id)
+}
+
+// deleteERC20Map deletes the token pair id for the given address
+func (k Keeper) deleteERC20Map(ctx sdk.Context, erc20 common.Address) {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixTokenPairByERC20)
+	store.Delete(erc20.Bytes())
+}
+
+// SetDenomMap sets the token pair id for the denomination
+func (k Keeper) SetDenomMap(ctx sdk.Context, denom string, id []byte) {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixTokenPairByDenom)
+	store.Set([]byte(denom), id)
+}
+
+// deleteDenomMap deletes the token pair id for the given denom
+func (k Keeper) deleteDenomMap(ctx sdk.Context, denom string) {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixTokenPairByDenom)
+	store.Delete([]byte(denom))
+}
+
+// IsTokenPairRegistered - check if registered token tokenPair is registered
+func (k Keeper) IsTokenPairRegistered(ctx sdk.Context, id []byte) bool {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixTokenPair)
+	return store.Has(id)
+}
+
+// IsERC20Registered check if registered ERC20 token is registered
+func (k Keeper) IsERC20Registered(ctx sdk.Context, erc20 common.Address) bool {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixTokenPairByERC20)
+	return store.Has(erc20.Bytes())
+}
+
+// IsDenomRegistered check if registered coin denom is registered
+func (k Keeper) IsDenomRegistered(ctx sdk.Context, denom string) bool {
+	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixTokenPairByDenom)
+	return store.Has([]byte(denom))
+}
diff --git a/x/erc20/keeper/token_pairs_test.go b/x/erc20/keeper/token_pairs_test.go
new file mode 100644
index 00000000..56f44543
--- /dev/null
+++ b/x/erc20/keeper/token_pairs_test.go
@@ -0,0 +1,234 @@
+package keeper_test
+
+import (
+	"fmt"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/evmos/ethermint/tests"
+
+	altheaconfig "github.com/AltheaFoundation/althea-L1/config"
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+func (suite *KeeperTestSuite) TestGetTokenPairs() {
+	var expRes []types.TokenPair
+
+	testCases := []struct {
+		name     string
+		malleate func()
+	}{
+		{
+			"no pair registered", func() { expRes = []types.TokenPair{} },
+		},
+		{
+			"1 pair registered",
+			func() {
+				pair := types.NewTokenPair(tests.GenerateAddress(), "coin", true, types.OWNER_MODULE)
+				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, pair)
+
+				expRes = []types.TokenPair{pair}
+			},
+		},
+		{
+			"2 pairs registered",
+			func() {
+				pair := types.NewTokenPair(tests.GenerateAddress(), "coin", true, types.OWNER_MODULE)
+				pair2 := types.NewTokenPair(tests.GenerateAddress(), "coin2", true, types.OWNER_MODULE)
+				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, pair)
+				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, pair2)
+
+				expRes = []types.TokenPair{pair, pair2}
+			},
+		},
+	}
+	for _, tc := range testCases {
+		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			suite.SetupTest() // reset
+
+			tc.malleate()
+			res := suite.app.Erc20Keeper.GetTokenPairs(suite.ctx)
+
+			suite.Require().ElementsMatch(expRes, res, tc.name)
+		})
+	}
+}
+
+func (suite *KeeperTestSuite) TestGetTokenPairID() {
+	pair := types.NewTokenPair(tests.GenerateAddress(), altheaconfig.BaseDenom, true, types.OWNER_MODULE)
+	suite.app.Erc20Keeper.SetTokenPair(suite.ctx, pair)
+
+	testCases := []struct {
+		name  string
+		token string
+		expId []byte
+	}{
+		{"nil token", "", nil},
+		{"valid hex token", tests.GenerateAddress().Hex(), []byte{}},
+		{"valid hex token", tests.GenerateAddress().String(), []byte{}},
+	}
+	for _, tc := range testCases {
+		id := suite.app.Erc20Keeper.GetTokenPairID(suite.ctx, tc.token)
+		if id != nil {
+			suite.Require().Equal(tc.expId, id, tc.name)
+		} else {
+			suite.Require().Nil(id)
+		}
+	}
+}
+
+func (suite *KeeperTestSuite) TestGetTokenPair() {
+	pair := types.NewTokenPair(tests.GenerateAddress(), altheaconfig.BaseDenom, true, types.OWNER_MODULE)
+	suite.app.Erc20Keeper.SetTokenPair(suite.ctx, pair)
+
+	testCases := []struct {
+		name string
+		id   []byte
+		ok   bool
+	}{
+		{"nil id", nil, false},
+		{"valid id", pair.GetID(), true},
+		{"pair not found", []byte{}, false},
+	}
+	for _, tc := range testCases {
+		p, found := suite.app.Erc20Keeper.GetTokenPair(suite.ctx, tc.id)
+		if tc.ok {
+			suite.Require().True(found, tc.name)
+			suite.Require().Equal(pair, p, tc.name)
+		} else {
+			suite.Require().False(found, tc.name)
+		}
+	}
+}
+
+func (suite *KeeperTestSuite) TestDeleteTokenPair() {
+	pair := types.NewTokenPair(tests.GenerateAddress(), altheaconfig.BaseDenom, true, types.OWNER_MODULE)
+	id := pair.GetID()
+	suite.app.Erc20Keeper.SetTokenPair(suite.ctx, pair)
+	suite.app.Erc20Keeper.SetERC20Map(suite.ctx, pair.GetERC20Contract(), id)
+	suite.app.Erc20Keeper.SetDenomMap(suite.ctx, pair.Denom, id)
+
+	testCases := []struct {
+		name     string
+		id       []byte
+		malleate func()
+		ok       bool
+	}{
+		{"nil id", nil, func() {}, false},
+		{"pair not found", []byte{}, func() {}, false},
+		{"valid id", id, func() {}, true},
+		{
+			"detete tokenpair",
+			id,
+			func() {
+				suite.app.Erc20Keeper.DeleteTokenPair(suite.ctx, pair)
+			},
+			false,
+		},
+	}
+	for _, tc := range testCases {
+		tc.malleate()
+		p, found := suite.app.Erc20Keeper.GetTokenPair(suite.ctx, tc.id)
+		if tc.ok {
+			suite.Require().True(found, tc.name)
+			suite.Require().Equal(pair, p, tc.name)
+		} else {
+			suite.Require().False(found, tc.name)
+		}
+	}
+}
+
+func (suite *KeeperTestSuite) TestIsTokenPairRegistered() {
+	pair := types.NewTokenPair(tests.GenerateAddress(), altheaconfig.BaseDenom, true, types.OWNER_MODULE)
+	suite.app.Erc20Keeper.SetTokenPair(suite.ctx, pair)
+
+	testCases := []struct {
+		name string
+		id   []byte
+		ok   bool
+	}{
+		{"valid id", pair.GetID(), true},
+		{"pair not found", []byte{}, false},
+	}
+	for _, tc := range testCases {
+		found := suite.app.Erc20Keeper.IsTokenPairRegistered(suite.ctx, tc.id)
+		if tc.ok {
+			suite.Require().True(found, tc.name)
+		} else {
+			suite.Require().False(found, tc.name)
+		}
+	}
+}
+
+func (suite *KeeperTestSuite) TestIsERC20Registered() {
+	addr := tests.GenerateAddress()
+	pair := types.NewTokenPair(addr, "coin", true, types.OWNER_MODULE)
+	suite.app.Erc20Keeper.SetTokenPair(suite.ctx, pair)
+	suite.app.Erc20Keeper.SetERC20Map(suite.ctx, addr, pair.GetID())
+	suite.app.Erc20Keeper.SetDenomMap(suite.ctx, pair.Denom, pair.GetID())
+
+	testCases := []struct {
+		name     string
+		erc20    common.Address
+		malleate func()
+		ok       bool
+	}{
+		{"nil erc20 address", common.Address{}, func() {}, false},
+		{"valid erc20 address", pair.GetERC20Contract(), func() {}, true},
+		{
+			"deleted erc20 map",
+			pair.GetERC20Contract(),
+			func() {
+				suite.app.Erc20Keeper.DeleteTokenPair(suite.ctx, pair)
+			},
+			false,
+		},
+	}
+	for _, tc := range testCases {
+		tc.malleate()
+
+		found := suite.app.Erc20Keeper.IsERC20Registered(suite.ctx, tc.erc20)
+
+		if tc.ok {
+			suite.Require().True(found, tc.name)
+		} else {
+			suite.Require().False(found, tc.name)
+		}
+	}
+}
+
+func (suite *KeeperTestSuite) TestIsDenomRegistered() {
+	addr := tests.GenerateAddress()
+	pair := types.NewTokenPair(addr, "coin", true, types.OWNER_MODULE)
+	suite.app.Erc20Keeper.SetTokenPair(suite.ctx, pair)
+	suite.app.Erc20Keeper.SetERC20Map(suite.ctx, addr, pair.GetID())
+	suite.app.Erc20Keeper.SetDenomMap(suite.ctx, pair.Denom, pair.GetID())
+
+	testCases := []struct {
+		name     string
+		denom    string
+		malleate func()
+		ok       bool
+	}{
+		{"empty denom", "", func() {}, false},
+		{"valid denom", pair.GetDenom(), func() {}, true},
+		{
+			"deleted denom map",
+			pair.GetDenom(),
+			func() {
+				suite.app.Erc20Keeper.DeleteTokenPair(suite.ctx, pair)
+			},
+			false,
+		},
+	}
+	for _, tc := range testCases {
+		tc.malleate()
+
+		found := suite.app.Erc20Keeper.IsDenomRegistered(suite.ctx, tc.denom)
+
+		if tc.ok {
+			suite.Require().True(found, tc.name)
+		} else {
+			suite.Require().False(found, tc.name)
+		}
+	}
+}
diff --git a/x/erc20/migrations/v2/migration.go b/x/erc20/migrations/v2/migration.go
new file mode 100644
index 00000000..2d42a4da
--- /dev/null
+++ b/x/erc20/migrations/v2/migration.go
@@ -0,0 +1,21 @@
+package v2
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+// UpdateParams updates the module parameters EnableERC20 and EnableEMVHook
+// values to true.
+func UpdateParams(ctx sdk.Context, paramstore *paramtypes.Subspace) error {
+	if !paramstore.HasKeyTable() {
+		ps := paramstore.WithKeyTable(types.ParamKeyTable())
+		paramstore = &ps
+	}
+
+	paramstore.Set(ctx, types.ParamStoreKeyEnableErc20, true)
+	paramstore.Set(ctx, types.ParamStoreKeyEnableEVMHook, true)
+	return nil
+}
diff --git a/x/erc20/migrations/v2/migration_test.go b/x/erc20/migrations/v2/migration_test.go
new file mode 100644
index 00000000..3a0b0c86
--- /dev/null
+++ b/x/erc20/migrations/v2/migration_test.go
@@ -0,0 +1,54 @@
+package v2_test
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	"github.com/cosmos/cosmos-sdk/testutil"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
+
+	"github.com/evmos/ethermint/encoding"
+
+	althea "github.com/AltheaFoundation/althea-L1/app"
+	v2 "github.com/AltheaFoundation/althea-L1/x/erc20/migrations/v2"
+	erc20types "github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+func TestUpdateParams(t *testing.T) {
+	encCfg := encoding.MakeConfig(althea.ModuleBasics)
+	erc20Key := sdk.NewKVStoreKey(erc20types.StoreKey)
+	tErc20Key := sdk.NewTransientStoreKey(fmt.Sprintf("%s_test", erc20types.StoreKey))
+	ctx := testutil.DefaultContext(erc20Key, tErc20Key)
+	paramstore := paramtypes.NewSubspace(
+		encCfg.Codec, encCfg.Amino, erc20Key, tErc20Key, "erc20",
+	)
+	paramstore = paramstore.WithKeyTable(erc20types.ParamKeyTable())
+	require.True(t, paramstore.HasKeyTable())
+
+	// check no params
+	require.False(t, paramstore.Has(ctx, erc20types.ParamStoreKeyEnableErc20))
+	require.False(t, paramstore.Has(ctx, erc20types.ParamStoreKeyEnableEVMHook))
+
+	// Run migrations
+	err := v2.UpdateParams(ctx, &paramstore)
+	require.NoError(t, err)
+
+	// Make sure the params are set
+	require.True(t, paramstore.Has(ctx, erc20types.ParamStoreKeyEnableErc20))
+	require.True(t, paramstore.Has(ctx, erc20types.ParamStoreKeyEnableEVMHook))
+
+	var enableERC20, enableEVMHook bool
+
+	// Make sure the new params are set
+	require.NotPanics(t, func() {
+		paramstore.Get(ctx, erc20types.ParamStoreKeyEnableErc20, &enableERC20)
+		paramstore.Get(ctx, erc20types.ParamStoreKeyEnableEVMHook, &enableEVMHook)
+	})
+
+	// check the params are updated
+	require.True(t, enableERC20)
+	require.True(t, enableEVMHook)
+}
diff --git a/x/erc20/module.go b/x/erc20/module.go
new file mode 100644
index 00000000..948158cb
--- /dev/null
+++ b/x/erc20/module.go
@@ -0,0 +1,181 @@
+package erc20
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"math/rand"
+
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/codec"
+	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/module"
+	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+	authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
+	"github.com/gorilla/mux"
+	"github.com/grpc-ecosystem/grpc-gateway/runtime"
+	"github.com/spf13/cobra"
+	abci "github.com/tendermint/tendermint/abci/types"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/client/cli"
+	"github.com/AltheaFoundation/althea-L1/x/erc20/keeper"
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+// type check to ensure the interface is properly implemented
+var (
+	_ module.AppModule           = AppModule{}
+	_ module.AppModuleBasic      = AppModuleBasic{}
+	_ module.AppModuleSimulation = AppModule{}
+)
+
+// app module Basics object
+type AppModuleBasic struct{}
+
+func (AppModuleBasic) Name() string {
+	return types.ModuleName
+}
+
+// RegisterLegacyAminoCodec performs a no-op as the erc20 doesn't support Amino encoding
+func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
+	types.RegisterLegacyAminoCodec(cdc)
+}
+
+// ConsensusVersion returns the consensus state-breaking version for the module.
+func (AppModuleBasic) ConsensusVersion() uint64 {
+	return 2
+}
+
+// RegisterInterfaces registers interfaces and implementations of the erc20 module.
+func (AppModuleBasic) RegisterInterfaces(interfaceRegistry codectypes.InterfaceRegistry) {
+	types.RegisterInterfaces(interfaceRegistry)
+}
+
+// DefaultGenesis returns default genesis state as raw bytes for the erc20
+// module.
+func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage {
+	return cdc.MustMarshalJSON(types.DefaultGenesisState())
+}
+
+func (b AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error {
+	var genesisState types.GenesisState
+	if err := cdc.UnmarshalJSON(bz, &genesisState); err != nil {
+		return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err)
+	}
+
+	return genesisState.Validate()
+}
+
+// RegisterRESTRoutes performs a no-op as the erc20 module doesn't expose REST
+// endpoints
+func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) {}
+
+func (b AppModuleBasic) RegisterGRPCGatewayRoutes(c client.Context, serveMux *runtime.ServeMux) {
+	if err := types.RegisterQueryHandlerClient(context.Background(), serveMux, types.NewQueryClient(c)); err != nil {
+		panic(err)
+	}
+}
+
+// GetTxCmd returns the root tx command for the erc20 module.
+func (AppModuleBasic) GetTxCmd() *cobra.Command {
+	return cli.NewTxCmd()
+}
+
+// GetQueryCmd returns no root query command for the erc20 module.
+func (AppModuleBasic) GetQueryCmd() *cobra.Command {
+	return cli.GetQueryCmd()
+}
+
+type AppModule struct {
+	AppModuleBasic
+	keeper keeper.Keeper
+	ak     authkeeper.AccountKeeper
+}
+
+// NewAppModule creates a new AppModule Object
+func NewAppModule(
+	k keeper.Keeper,
+	ak authkeeper.AccountKeeper,
+) AppModule {
+	return AppModule{
+		AppModuleBasic: AppModuleBasic{},
+		keeper:         k,
+		ak:             ak,
+	}
+}
+
+func (AppModule) Name() string {
+	return types.ModuleName
+}
+
+func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {}
+
+func (am AppModule) NewHandler() sdk.Handler {
+	return NewHandler(am.keeper)
+}
+
+func (am AppModule) Route() sdk.Route {
+	return sdk.NewRoute(types.RouterKey, am.NewHandler())
+}
+
+func (am AppModule) QuerierRoute() string {
+	return types.RouterKey
+}
+
+func (am AppModule) LegacyQuerierHandler(amino *codec.LegacyAmino) sdk.Querier {
+	return nil
+}
+
+func (am AppModule) RegisterServices(cfg module.Configurator) {
+	types.RegisterMsgServer(cfg.MsgServer(), am.keeper)
+	types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
+
+	migrator := keeper.NewMigrator(am.keeper)
+
+	// NOTE: the migrations below will only run if the consensus version has changed
+	// since the last release
+
+	// register v1 -> v2 migration
+	if err := cfg.RegisterMigration(types.ModuleName, 1, migrator.Migrate1to2); err != nil {
+		panic(fmt.Errorf("failed to migrate %s to v2: %w", types.ModuleName, err))
+	}
+}
+
+func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {
+}
+
+func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
+	return []abci.ValidatorUpdate{}
+}
+
+func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate {
+	var genesisState types.GenesisState
+
+	cdc.MustUnmarshalJSON(data, &genesisState)
+	InitGenesis(ctx, am.keeper, am.ak, genesisState)
+	return []abci.ValidatorUpdate{}
+}
+
+func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage {
+	gs := ExportGenesis(ctx, am.keeper)
+	return cdc.MustMarshalJSON(gs)
+}
+
+func (am AppModule) GenerateGenesisState(input *module.SimulationState) {
+}
+
+func (am AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent {
+	return []simtypes.WeightedProposalContent{}
+}
+
+func (am AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange {
+	return []simtypes.ParamChange{}
+}
+
+func (am AppModule) RegisterStoreDecoder(decoderRegistry sdk.StoreDecoderRegistry) {
+}
+
+func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
+	return []simtypes.WeightedOperation{}
+}
diff --git a/x/erc20/proposal_handler.go b/x/erc20/proposal_handler.go
new file mode 100644
index 00000000..1e4b15d9
--- /dev/null
+++ b/x/erc20/proposal_handler.go
@@ -0,0 +1,77 @@
+package erc20
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
+	"github.com/ethereum/go-ethereum/common"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/keeper"
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+// NewErc20ProposalHandler creates a governance handler to manage new proposal types.
+func NewErc20ProposalHandler(k *keeper.Keeper) govv1beta1.Handler {
+	return func(ctx sdk.Context, content govv1beta1.Content) error {
+		switch c := content.(type) {
+		case *types.RegisterCoinProposal:
+			return handleRegisterCoinProposal(ctx, k, c)
+		case *types.RegisterERC20Proposal:
+			return handleRegisterERC20Proposal(ctx, k, c)
+		case *types.ToggleTokenConversionProposal:
+			return handleToggleConversionProposal(ctx, k, c)
+
+		default:
+			return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s proposal content type: %T", types.ModuleName, c)
+		}
+	}
+}
+
+func handleRegisterCoinProposal(ctx sdk.Context, k *keeper.Keeper, p *types.RegisterCoinProposal) error {
+	pair, err := k.RegisterCoin(ctx, p.Metadata)
+	if err != nil {
+		return err
+	}
+	ctx.EventManager().EmitEvent(
+		sdk.NewEvent(
+			types.EventTypeRegisterCoin,
+			sdk.NewAttribute(types.AttributeKeyCosmosCoin, pair.Denom),
+			sdk.NewAttribute(types.AttributeKeyERC20Token, pair.Erc20Address),
+		),
+	)
+
+	return nil
+}
+
+func handleRegisterERC20Proposal(ctx sdk.Context, k *keeper.Keeper, p *types.RegisterERC20Proposal) error {
+	pair, err := k.RegisterERC20(ctx, common.HexToAddress(p.Erc20Address))
+	if err != nil {
+		return err
+	}
+	ctx.EventManager().EmitEvent(
+		sdk.NewEvent(
+			types.EventTypeRegisterERC20,
+			sdk.NewAttribute(types.AttributeKeyCosmosCoin, pair.Denom),
+			sdk.NewAttribute(types.AttributeKeyERC20Token, pair.Erc20Address),
+		),
+	)
+
+	return nil
+}
+
+func handleToggleConversionProposal(ctx sdk.Context, k *keeper.Keeper, p *types.ToggleTokenConversionProposal) error {
+	pair, err := k.ToggleConversion(ctx, p.Token)
+	if err != nil {
+		return err
+	}
+
+	ctx.EventManager().EmitEvent(
+		sdk.NewEvent(
+			types.EventTypeToggleTokenConversion,
+			sdk.NewAttribute(types.AttributeKeyCosmosCoin, pair.Denom),
+			sdk.NewAttribute(types.AttributeKeyERC20Token, pair.Erc20Address),
+		),
+	)
+
+	return nil
+}
diff --git a/x/erc20/types/codec.go b/x/erc20/types/codec.go
new file mode 100644
index 00000000..20cec806
--- /dev/null
+++ b/x/erc20/types/codec.go
@@ -0,0 +1,60 @@
+package types
+
+import (
+	"github.com/cosmos/cosmos-sdk/codec"
+	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/msgservice"
+	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
+)
+
+var (
+	amino = codec.NewLegacyAmino()
+
+	// ModuleCdc references the global erc20 module codec. Note, the codec should
+	// ONLY be used in certain instances of tests and for JSON encoding.
+	//
+	// The actual codec used for serialization should be provided to modules/erc20 and
+	// defined at the application level.
+	ModuleCdc = codec.NewProtoCodec(codectypes.NewInterfaceRegistry())
+
+	// AminoCdc is a amino codec created to support amino JSON compatible msgs.
+	AminoCdc = codec.NewAminoCodec(amino)
+)
+
+const (
+	// Amino names
+	convertERC20Name = "canto/MsgConvertERC20"
+	convertCoinName  = "canto/MsgConvertCoin"
+)
+
+// NOTE: This is required for the GetSignBytes function
+func init() {
+	RegisterLegacyAminoCodec(amino)
+	amino.Seal()
+}
+
+// RegisterInterfaces register implementations
+func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
+	registry.RegisterImplementations(
+		(*sdk.Msg)(nil),
+		&MsgConvertCoin{},
+		&MsgConvertERC20{},
+	)
+	registry.RegisterImplementations(
+		(*govv1beta1.Content)(nil),
+		&RegisterCoinProposal{},
+		&RegisterERC20Proposal{},
+		&ToggleTokenConversionProposal{},
+	)
+
+	msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
+}
+
+// RegisterLegacyAminoCodec registers the necessary x/erc20 interfaces and
+// concrete types on the provided LegacyAmino codec. These types are used for
+// Amino JSON serialization and EIP-712 compatibility.
+func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
+	cdc.RegisterConcrete(&MsgConvertERC20{}, convertERC20Name, nil)
+	cdc.RegisterConcrete(&MsgConvertCoin{}, convertCoinName, nil)
+}
diff --git a/x/erc20/types/erc20.pb.go b/x/erc20/types/erc20.pb.go
new file mode 100644
index 00000000..0563b16e
--- /dev/null
+++ b/x/erc20/types/erc20.pb.go
@@ -0,0 +1,1401 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: canto/erc20/v1/erc20.proto
+
+package types
+
+import (
+	fmt "fmt"
+	types "github.com/cosmos/cosmos-sdk/x/bank/types"
+	_ "github.com/gogo/protobuf/gogoproto"
+	proto "github.com/gogo/protobuf/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
+
+// Owner enumerates the ownership of a ERC20 contract.
+type Owner int32
+
+const (
+	// OWNER_UNSPECIFIED defines an invalid/undefined owner.
+	OWNER_UNSPECIFIED Owner = 0
+	// OWNER_MODULE erc20 is owned by the erc20 module account.
+	OWNER_MODULE Owner = 1
+	// EXTERNAL erc20 is owned by an external account.
+	OWNER_EXTERNAL Owner = 2
+)
+
+var Owner_name = map[int32]string{
+	0: "OWNER_UNSPECIFIED",
+	1: "OWNER_MODULE",
+	2: "OWNER_EXTERNAL",
+}
+
+var Owner_value = map[string]int32{
+	"OWNER_UNSPECIFIED": 0,
+	"OWNER_MODULE":      1,
+	"OWNER_EXTERNAL":    2,
+}
+
+func (x Owner) String() string {
+	return proto.EnumName(Owner_name, int32(x))
+}
+
+func (Owner) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_5c364669f6882b8b, []int{0}
+}
+
+// TokenPair defines an instance that records a pairing consisting of a native
+//  Cosmos Coin and an ERC20 token address.
+type TokenPair struct {
+	// address of ERC20 contract token
+	Erc20Address string `protobuf:"bytes,1,opt,name=erc20_address,json=erc20Address,proto3" json:"erc20_address,omitempty"`
+	// cosmos base denomination to be mapped to
+	Denom string `protobuf:"bytes,2,opt,name=denom,proto3" json:"denom,omitempty"`
+	// shows token mapping enable status
+	Enabled bool `protobuf:"varint,3,opt,name=enabled,proto3" json:"enabled,omitempty"`
+	// ERC20 owner address ENUM (0 invalid, 1 ModuleAccount, 2 external address)
+	ContractOwner Owner `protobuf:"varint,4,opt,name=contract_owner,json=contractOwner,proto3,enum=canto.erc20.v1.Owner" json:"contract_owner,omitempty"`
+}
+
+func (m *TokenPair) Reset()         { *m = TokenPair{} }
+func (m *TokenPair) String() string { return proto.CompactTextString(m) }
+func (*TokenPair) ProtoMessage()    {}
+func (*TokenPair) Descriptor() ([]byte, []int) {
+	return fileDescriptor_5c364669f6882b8b, []int{0}
+}
+func (m *TokenPair) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *TokenPair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_TokenPair.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 *TokenPair) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_TokenPair.Merge(m, src)
+}
+func (m *TokenPair) XXX_Size() int {
+	return m.Size()
+}
+func (m *TokenPair) XXX_DiscardUnknown() {
+	xxx_messageInfo_TokenPair.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_TokenPair proto.InternalMessageInfo
+
+func (m *TokenPair) GetErc20Address() string {
+	if m != nil {
+		return m.Erc20Address
+	}
+	return ""
+}
+
+func (m *TokenPair) GetDenom() string {
+	if m != nil {
+		return m.Denom
+	}
+	return ""
+}
+
+func (m *TokenPair) GetEnabled() bool {
+	if m != nil {
+		return m.Enabled
+	}
+	return false
+}
+
+func (m *TokenPair) GetContractOwner() Owner {
+	if m != nil {
+		return m.ContractOwner
+	}
+	return OWNER_UNSPECIFIED
+}
+
+// RegisterCoinProposal is a gov Content type to register a token pair for a
+// native Cosmos coin.
+type RegisterCoinProposal struct {
+	// title of the proposal
+	Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"`
+	// proposal description
+	Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
+	// metadata of the native Cosmos coin
+	Metadata types.Metadata `protobuf:"bytes,3,opt,name=metadata,proto3" json:"metadata"`
+}
+
+func (m *RegisterCoinProposal) Reset()         { *m = RegisterCoinProposal{} }
+func (m *RegisterCoinProposal) String() string { return proto.CompactTextString(m) }
+func (*RegisterCoinProposal) ProtoMessage()    {}
+func (*RegisterCoinProposal) Descriptor() ([]byte, []int) {
+	return fileDescriptor_5c364669f6882b8b, []int{1}
+}
+func (m *RegisterCoinProposal) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *RegisterCoinProposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_RegisterCoinProposal.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 *RegisterCoinProposal) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_RegisterCoinProposal.Merge(m, src)
+}
+func (m *RegisterCoinProposal) XXX_Size() int {
+	return m.Size()
+}
+func (m *RegisterCoinProposal) XXX_DiscardUnknown() {
+	xxx_messageInfo_RegisterCoinProposal.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_RegisterCoinProposal proto.InternalMessageInfo
+
+func (m *RegisterCoinProposal) GetTitle() string {
+	if m != nil {
+		return m.Title
+	}
+	return ""
+}
+
+func (m *RegisterCoinProposal) GetDescription() string {
+	if m != nil {
+		return m.Description
+	}
+	return ""
+}
+
+func (m *RegisterCoinProposal) GetMetadata() types.Metadata {
+	if m != nil {
+		return m.Metadata
+	}
+	return types.Metadata{}
+}
+
+// RegisterERC20Proposal is a gov Content type to register a token pair for an
+// ERC20 token
+type RegisterERC20Proposal struct {
+	// title of the proposa  string title = 1;
+	Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"`
+	// proposal description
+	Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
+	// contract address of ERC20 token
+	Erc20Address string `protobuf:"bytes,3,opt,name=erc20address,proto3" json:"erc20address,omitempty"`
+}
+
+func (m *RegisterERC20Proposal) Reset()         { *m = RegisterERC20Proposal{} }
+func (m *RegisterERC20Proposal) String() string { return proto.CompactTextString(m) }
+func (*RegisterERC20Proposal) ProtoMessage()    {}
+func (*RegisterERC20Proposal) Descriptor() ([]byte, []int) {
+	return fileDescriptor_5c364669f6882b8b, []int{2}
+}
+func (m *RegisterERC20Proposal) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *RegisterERC20Proposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_RegisterERC20Proposal.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 *RegisterERC20Proposal) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_RegisterERC20Proposal.Merge(m, src)
+}
+func (m *RegisterERC20Proposal) XXX_Size() int {
+	return m.Size()
+}
+func (m *RegisterERC20Proposal) XXX_DiscardUnknown() {
+	xxx_messageInfo_RegisterERC20Proposal.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_RegisterERC20Proposal proto.InternalMessageInfo
+
+func (m *RegisterERC20Proposal) GetTitle() string {
+	if m != nil {
+		return m.Title
+	}
+	return ""
+}
+
+func (m *RegisterERC20Proposal) GetDescription() string {
+	if m != nil {
+		return m.Description
+	}
+	return ""
+}
+
+func (m *RegisterERC20Proposal) GetErc20Address() string {
+	if m != nil {
+		return m.Erc20Address
+	}
+	return ""
+}
+
+// ToggleTokenConversionProposal is a gov Content type to toggle the conversion
+// of a token pair.
+type ToggleTokenConversionProposal struct {
+	// title of the proposal
+	Title string `protobuf:"bytes,1,opt,name=Title,proto3" json:"Title,omitempty"`
+	// proposal description
+	Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
+	// token identifier can be either the hex contract address of the ERC20 or the
+	// Cosmos base denomination
+	Token string `protobuf:"bytes,3,opt,name=token,proto3" json:"token,omitempty"`
+}
+
+func (m *ToggleTokenConversionProposal) Reset()         { *m = ToggleTokenConversionProposal{} }
+func (m *ToggleTokenConversionProposal) String() string { return proto.CompactTextString(m) }
+func (*ToggleTokenConversionProposal) ProtoMessage()    {}
+func (*ToggleTokenConversionProposal) Descriptor() ([]byte, []int) {
+	return fileDescriptor_5c364669f6882b8b, []int{3}
+}
+func (m *ToggleTokenConversionProposal) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *ToggleTokenConversionProposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_ToggleTokenConversionProposal.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 *ToggleTokenConversionProposal) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ToggleTokenConversionProposal.Merge(m, src)
+}
+func (m *ToggleTokenConversionProposal) XXX_Size() int {
+	return m.Size()
+}
+func (m *ToggleTokenConversionProposal) XXX_DiscardUnknown() {
+	xxx_messageInfo_ToggleTokenConversionProposal.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ToggleTokenConversionProposal proto.InternalMessageInfo
+
+func (m *ToggleTokenConversionProposal) GetTitle() string {
+	if m != nil {
+		return m.Title
+	}
+	return ""
+}
+
+func (m *ToggleTokenConversionProposal) GetDescription() string {
+	if m != nil {
+		return m.Description
+	}
+	return ""
+}
+
+func (m *ToggleTokenConversionProposal) GetToken() string {
+	if m != nil {
+		return m.Token
+	}
+	return ""
+}
+
+func init() {
+	proto.RegisterEnum("canto.erc20.v1.Owner", Owner_name, Owner_value)
+	proto.RegisterType((*TokenPair)(nil), "canto.erc20.v1.TokenPair")
+	proto.RegisterType((*RegisterCoinProposal)(nil), "canto.erc20.v1.RegisterCoinProposal")
+	proto.RegisterType((*RegisterERC20Proposal)(nil), "canto.erc20.v1.RegisterERC20Proposal")
+	proto.RegisterType((*ToggleTokenConversionProposal)(nil), "canto.erc20.v1.ToggleTokenConversionProposal")
+}
+
+func init() { proto.RegisterFile("canto/erc20/v1/erc20.proto", fileDescriptor_5c364669f6882b8b) }
+
+var fileDescriptor_5c364669f6882b8b = []byte{
+	// 499 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x93, 0x41, 0x6b, 0xdb, 0x3e,
+	0x18, 0xc6, 0xad, 0x36, 0xfd, 0xff, 0x5b, 0xb5, 0x0d, 0x99, 0x48, 0x20, 0x04, 0xea, 0x86, 0xec,
+	0x12, 0x06, 0xb3, 0x9b, 0xec, 0x36, 0x06, 0xa3, 0x75, 0x3d, 0xc8, 0x68, 0x93, 0xe0, 0xb9, 0x6c,
+	0xec, 0x12, 0x64, 0x5b, 0x78, 0x26, 0x89, 0xde, 0x20, 0x69, 0xee, 0xfa, 0x0d, 0x76, 0xdc, 0x65,
+	0xf7, 0xc1, 0xf6, 0x61, 0x7a, 0xec, 0x71, 0xa7, 0x31, 0x92, 0xcb, 0x3e, 0xc6, 0xb0, 0xe4, 0x8c,
+	0xf4, 0x36, 0xd8, 0x4d, 0xcf, 0xf3, 0xbe, 0x96, 0x7e, 0x7e, 0x1f, 0x09, 0xb7, 0x62, 0xca, 0x15,
+	0xb8, 0x4c, 0xc4, 0xfd, 0x13, 0x37, 0xef, 0x99, 0x85, 0xb3, 0x10, 0xa0, 0x80, 0x54, 0x75, 0xcd,
+	0x31, 0x56, 0xde, 0x6b, 0xd5, 0x53, 0x48, 0x41, 0x97, 0xdc, 0x62, 0x65, 0xba, 0x5a, 0x76, 0x0c,
+	0x72, 0x0e, 0xd2, 0x8d, 0x28, 0x9f, 0xba, 0x79, 0x2f, 0x62, 0x8a, 0xf6, 0xb4, 0x30, 0xf5, 0xce,
+	0x37, 0x84, 0xf7, 0x42, 0x98, 0x32, 0x3e, 0xa6, 0x99, 0x20, 0x0f, 0xf1, 0xa1, 0xde, 0x6f, 0x42,
+	0x93, 0x44, 0x30, 0x29, 0x9b, 0xa8, 0x8d, 0xba, 0x7b, 0xc1, 0x81, 0x36, 0x4f, 0x8d, 0x47, 0xea,
+	0x78, 0x27, 0x61, 0x1c, 0xe6, 0xcd, 0x2d, 0x5d, 0x34, 0x82, 0x34, 0xf1, 0xff, 0x8c, 0xd3, 0x68,
+	0xc6, 0x92, 0xe6, 0x76, 0x1b, 0x75, 0x77, 0x83, 0xb5, 0x24, 0xcf, 0x70, 0x35, 0x06, 0xae, 0x04,
+	0x8d, 0xd5, 0x04, 0xae, 0x39, 0x13, 0xcd, 0x4a, 0x1b, 0x75, 0xab, 0xfd, 0x86, 0x73, 0xff, 0x0f,
+	0x9c, 0x51, 0x51, 0x0c, 0x0e, 0xd7, 0xcd, 0x5a, 0x3e, 0xad, 0xfc, 0xfa, 0x72, 0x8c, 0x3a, 0x9f,
+	0x11, 0xae, 0x07, 0x2c, 0xcd, 0xa4, 0x62, 0xc2, 0x83, 0x8c, 0x8f, 0x05, 0x2c, 0x40, 0xd2, 0x59,
+	0x01, 0xa3, 0x32, 0x35, 0x63, 0x25, 0xa9, 0x11, 0xa4, 0x8d, 0xf7, 0x13, 0x26, 0x63, 0x91, 0x2d,
+	0x54, 0x06, 0xbc, 0x04, 0xdd, 0xb4, 0xc8, 0x73, 0xbc, 0x3b, 0x67, 0x8a, 0x26, 0x54, 0x51, 0xcd,
+	0xbb, 0xdf, 0x3f, 0x72, 0xcc, 0xa8, 0x1c, 0x3d, 0x9d, 0x72, 0x54, 0xce, 0x65, 0xd9, 0x74, 0x56,
+	0xb9, 0xfd, 0x71, 0x6c, 0x05, 0x7f, 0x3e, 0xd2, 0x5c, 0x56, 0xe7, 0x06, 0x37, 0xd6, 0x58, 0x7e,
+	0xe0, 0xf5, 0x4f, 0xfe, 0x99, 0xab, 0x83, 0xcd, 0xb0, 0xd7, 0x01, 0x6c, 0x6f, 0x04, 0x50, 0x7a,
+	0xe5, 0xd1, 0x12, 0x1f, 0x85, 0x90, 0xa6, 0x33, 0xa6, 0xe3, 0xf3, 0x80, 0xe7, 0x4c, 0xc8, 0x0c,
+	0xee, 0x8d, 0x26, 0xdc, 0x44, 0x08, 0xff, 0x12, 0xa1, 0x40, 0x2f, 0xb6, 0x2c, 0xcf, 0x36, 0xc2,
+	0xe4, 0xf0, 0xe8, 0x25, 0xde, 0xd1, 0xb1, 0x90, 0x06, 0x7e, 0x30, 0x7a, 0x3d, 0xf4, 0x83, 0xc9,
+	0xd5, 0xf0, 0xd5, 0xd8, 0xf7, 0x06, 0x2f, 0x06, 0xfe, 0x79, 0xcd, 0x22, 0x35, 0x7c, 0x60, 0xec,
+	0xcb, 0xd1, 0xf9, 0xd5, 0x85, 0x5f, 0x43, 0x84, 0xe0, 0xaa, 0x71, 0xfc, 0x37, 0xa1, 0x1f, 0x0c,
+	0x4f, 0x2f, 0x6a, 0x5b, 0xad, 0xca, 0xc7, 0xaf, 0xb6, 0x75, 0x36, 0xb8, 0x5d, 0xda, 0xe8, 0x6e,
+	0x69, 0xa3, 0x9f, 0x4b, 0x1b, 0x7d, 0x5a, 0xd9, 0xd6, 0xdd, 0xca, 0xb6, 0xbe, 0xaf, 0x6c, 0xeb,
+	0xad, 0x9b, 0x66, 0xea, 0xdd, 0xfb, 0xc8, 0x89, 0x61, 0xee, 0x7a, 0xc5, 0x1d, 0x79, 0x3c, 0x64,
+	0xea, 0x1a, 0xc4, 0xd4, 0x28, 0x37, 0xef, 0xbb, 0x1f, 0xca, 0x47, 0xa1, 0x6e, 0x16, 0x4c, 0x46,
+	0xff, 0xe9, 0xcb, 0xfc, 0xe4, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x29, 0x55, 0xba, 0x56, 0x30,
+	0x03, 0x00, 0x00,
+}
+
+func (this *TokenPair) Equal(that interface{}) bool {
+	if that == nil {
+		return this == nil
+	}
+
+	that1, ok := that.(*TokenPair)
+	if !ok {
+		that2, ok := that.(TokenPair)
+		if ok {
+			that1 = &that2
+		} else {
+			return false
+		}
+	}
+	if that1 == nil {
+		return this == nil
+	} else if this == nil {
+		return false
+	}
+	if this.Erc20Address != that1.Erc20Address {
+		return false
+	}
+	if this.Denom != that1.Denom {
+		return false
+	}
+	if this.Enabled != that1.Enabled {
+		return false
+	}
+	if this.ContractOwner != that1.ContractOwner {
+		return false
+	}
+	return true
+}
+func (this *ToggleTokenConversionProposal) Equal(that interface{}) bool {
+	if that == nil {
+		return this == nil
+	}
+
+	that1, ok := that.(*ToggleTokenConversionProposal)
+	if !ok {
+		that2, ok := that.(ToggleTokenConversionProposal)
+		if ok {
+			that1 = &that2
+		} else {
+			return false
+		}
+	}
+	if that1 == nil {
+		return this == nil
+	} else if this == nil {
+		return false
+	}
+	if this.Title != that1.Title {
+		return false
+	}
+	if this.Description != that1.Description {
+		return false
+	}
+	if this.Token != that1.Token {
+		return false
+	}
+	return true
+}
+func (m *TokenPair) 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 *TokenPair) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *TokenPair) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.ContractOwner != 0 {
+		i = encodeVarintErc20(dAtA, i, uint64(m.ContractOwner))
+		i--
+		dAtA[i] = 0x20
+	}
+	if m.Enabled {
+		i--
+		if m.Enabled {
+			dAtA[i] = 1
+		} else {
+			dAtA[i] = 0
+		}
+		i--
+		dAtA[i] = 0x18
+	}
+	if len(m.Denom) > 0 {
+		i -= len(m.Denom)
+		copy(dAtA[i:], m.Denom)
+		i = encodeVarintErc20(dAtA, i, uint64(len(m.Denom)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.Erc20Address) > 0 {
+		i -= len(m.Erc20Address)
+		copy(dAtA[i:], m.Erc20Address)
+		i = encodeVarintErc20(dAtA, i, uint64(len(m.Erc20Address)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *RegisterCoinProposal) 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 *RegisterCoinProposal) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *RegisterCoinProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size, err := m.Metadata.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintErc20(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x1a
+	if len(m.Description) > 0 {
+		i -= len(m.Description)
+		copy(dAtA[i:], m.Description)
+		i = encodeVarintErc20(dAtA, i, uint64(len(m.Description)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.Title) > 0 {
+		i -= len(m.Title)
+		copy(dAtA[i:], m.Title)
+		i = encodeVarintErc20(dAtA, i, uint64(len(m.Title)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *RegisterERC20Proposal) 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 *RegisterERC20Proposal) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *RegisterERC20Proposal) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Erc20Address) > 0 {
+		i -= len(m.Erc20Address)
+		copy(dAtA[i:], m.Erc20Address)
+		i = encodeVarintErc20(dAtA, i, uint64(len(m.Erc20Address)))
+		i--
+		dAtA[i] = 0x1a
+	}
+	if len(m.Description) > 0 {
+		i -= len(m.Description)
+		copy(dAtA[i:], m.Description)
+		i = encodeVarintErc20(dAtA, i, uint64(len(m.Description)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.Title) > 0 {
+		i -= len(m.Title)
+		copy(dAtA[i:], m.Title)
+		i = encodeVarintErc20(dAtA, i, uint64(len(m.Title)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *ToggleTokenConversionProposal) 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 *ToggleTokenConversionProposal) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *ToggleTokenConversionProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Token) > 0 {
+		i -= len(m.Token)
+		copy(dAtA[i:], m.Token)
+		i = encodeVarintErc20(dAtA, i, uint64(len(m.Token)))
+		i--
+		dAtA[i] = 0x1a
+	}
+	if len(m.Description) > 0 {
+		i -= len(m.Description)
+		copy(dAtA[i:], m.Description)
+		i = encodeVarintErc20(dAtA, i, uint64(len(m.Description)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.Title) > 0 {
+		i -= len(m.Title)
+		copy(dAtA[i:], m.Title)
+		i = encodeVarintErc20(dAtA, i, uint64(len(m.Title)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintErc20(dAtA []byte, offset int, v uint64) int {
+	offset -= sovErc20(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *TokenPair) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Erc20Address)
+	if l > 0 {
+		n += 1 + l + sovErc20(uint64(l))
+	}
+	l = len(m.Denom)
+	if l > 0 {
+		n += 1 + l + sovErc20(uint64(l))
+	}
+	if m.Enabled {
+		n += 2
+	}
+	if m.ContractOwner != 0 {
+		n += 1 + sovErc20(uint64(m.ContractOwner))
+	}
+	return n
+}
+
+func (m *RegisterCoinProposal) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Title)
+	if l > 0 {
+		n += 1 + l + sovErc20(uint64(l))
+	}
+	l = len(m.Description)
+	if l > 0 {
+		n += 1 + l + sovErc20(uint64(l))
+	}
+	l = m.Metadata.Size()
+	n += 1 + l + sovErc20(uint64(l))
+	return n
+}
+
+func (m *RegisterERC20Proposal) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Title)
+	if l > 0 {
+		n += 1 + l + sovErc20(uint64(l))
+	}
+	l = len(m.Description)
+	if l > 0 {
+		n += 1 + l + sovErc20(uint64(l))
+	}
+	l = len(m.Erc20Address)
+	if l > 0 {
+		n += 1 + l + sovErc20(uint64(l))
+	}
+	return n
+}
+
+func (m *ToggleTokenConversionProposal) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Title)
+	if l > 0 {
+		n += 1 + l + sovErc20(uint64(l))
+	}
+	l = len(m.Description)
+	if l > 0 {
+		n += 1 + l + sovErc20(uint64(l))
+	}
+	l = len(m.Token)
+	if l > 0 {
+		n += 1 + l + sovErc20(uint64(l))
+	}
+	return n
+}
+
+func sovErc20(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozErc20(x uint64) (n int) {
+	return sovErc20(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *TokenPair) 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 ErrIntOverflowErc20
+			}
+			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: TokenPair: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: TokenPair: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Erc20Address", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowErc20
+				}
+				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 ErrInvalidLengthErc20
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthErc20
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Erc20Address = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowErc20
+				}
+				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 ErrInvalidLengthErc20
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthErc20
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Denom = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowErc20
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				v |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.Enabled = bool(v != 0)
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ContractOwner", wireType)
+			}
+			m.ContractOwner = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowErc20
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.ContractOwner |= Owner(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipErc20(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthErc20
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *RegisterCoinProposal) 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 ErrIntOverflowErc20
+			}
+			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: RegisterCoinProposal: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: RegisterCoinProposal: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowErc20
+				}
+				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 ErrInvalidLengthErc20
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthErc20
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Title = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowErc20
+				}
+				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 ErrInvalidLengthErc20
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthErc20
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Description = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowErc20
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthErc20
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthErc20
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipErc20(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthErc20
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *RegisterERC20Proposal) 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 ErrIntOverflowErc20
+			}
+			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: RegisterERC20Proposal: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: RegisterERC20Proposal: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowErc20
+				}
+				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 ErrInvalidLengthErc20
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthErc20
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Title = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowErc20
+				}
+				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 ErrInvalidLengthErc20
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthErc20
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Description = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Erc20Address", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowErc20
+				}
+				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 ErrInvalidLengthErc20
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthErc20
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Erc20Address = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipErc20(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthErc20
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *ToggleTokenConversionProposal) 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 ErrIntOverflowErc20
+			}
+			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: ToggleTokenConversionProposal: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: ToggleTokenConversionProposal: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowErc20
+				}
+				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 ErrInvalidLengthErc20
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthErc20
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Title = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowErc20
+				}
+				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 ErrInvalidLengthErc20
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthErc20
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Description = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Token", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowErc20
+				}
+				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 ErrInvalidLengthErc20
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthErc20
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Token = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipErc20(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthErc20
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipErc20(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, ErrIntOverflowErc20
+			}
+			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, ErrIntOverflowErc20
+				}
+				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, ErrIntOverflowErc20
+				}
+				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, ErrInvalidLengthErc20
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupErc20
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthErc20
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthErc20        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowErc20          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupErc20 = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/x/erc20/types/errors.go b/x/erc20/types/errors.go
new file mode 100644
index 00000000..09e61938
--- /dev/null
+++ b/x/erc20/types/errors.go
@@ -0,0 +1,21 @@
+package types
+
+import (
+	errorsmod "cosmossdk.io/errors"
+)
+
+// errors
+var (
+	ErrERC20Disabled          = errorsmod.Register(ModuleName, 2, "erc20 module is disabled")
+	ErrInternalTokenPair      = errorsmod.Register(ModuleName, 3, "internal ethereum token mapping error")
+	ErrTokenPairNotFound      = errorsmod.Register(ModuleName, 4, "token pair not found")
+	ErrTokenPairAlreadyExists = errorsmod.Register(ModuleName, 5, "token pair already exists")
+	ErrUndefinedOwner         = errorsmod.Register(ModuleName, 6, "undefined owner of contract pair")
+	ErrBalanceInvariance      = errorsmod.Register(ModuleName, 7, "post transfer balance invariant failed")
+	ErrUnexpectedEvent        = errorsmod.Register(ModuleName, 8, "unexpected event")
+	ErrABIPack                = errorsmod.Register(ModuleName, 9, "contract ABI pack failed")
+	ErrABIUnpack              = errorsmod.Register(ModuleName, 10, "contract ABI unpack failed")
+	ErrEVMDenom               = errorsmod.Register(ModuleName, 11, "EVM denomination registration")
+	ErrEVMCall                = errorsmod.Register(ModuleName, 12, "EVM call unexpected error")
+	ErrERC20TokenPairDisabled = errorsmod.Register(ModuleName, 13, "erc20 token pair is disabled")
+)
diff --git a/x/erc20/types/events.go b/x/erc20/types/events.go
new file mode 100644
index 00000000..867e5ca1
--- /dev/null
+++ b/x/erc20/types/events.go
@@ -0,0 +1,33 @@
+package types
+
+import (
+	"math/big"
+
+	"github.com/ethereum/go-ethereum/common"
+)
+
+// erc20 events
+const (
+	EventTypeTokenLock             = "token_lock"
+	EventTypeTokenUnlock           = "token_unlock"
+	EventTypeMint                  = "mint"
+	EventTypeConvertCoin           = "convert_coin"
+	EventTypeConvertERC20          = "convert_erc20"
+	EventTypeBurn                  = "burn"
+	EventTypeRegisterCoin          = "register_coin"
+	EventTypeRegisterERC20         = "register_erc20"
+	EventTypeToggleTokenConversion = "toggle_token_conversion" // #nosec
+
+	AttributeKeyCosmosCoin = "cosmos_coin"
+	AttributeKeyERC20Token = "erc20_token" // #nosec
+	AttributeKeyReceiver   = "receiver"
+
+	ERC20EventTransfer = "Transfer"
+)
+
+// Event type for Transfer(address from, address to, uint256 value)
+type LogTransfer struct {
+	From   common.Address
+	To     common.Address
+	Tokens *big.Int
+}
diff --git a/x/erc20/types/evm.go b/x/erc20/types/evm.go
new file mode 100644
index 00000000..4e8e7dea
--- /dev/null
+++ b/x/erc20/types/evm.go
@@ -0,0 +1,33 @@
+package types
+
+// ERC20Data represents the ERC20 token details used to map
+// the token to a Cosmos Coin
+type ERC20Data struct {
+	Name     string
+	Symbol   string
+	Decimals uint8
+}
+
+// ERC20StringResponse defines the string value from the call response
+type ERC20StringResponse struct {
+	Value string
+}
+
+// ERC20Uint8Response defines the uint8 value from the call response
+type ERC20Uint8Response struct {
+	Value uint8
+}
+
+// ERC20BoolResponse defines the bool value from the call response
+type ERC20BoolResponse struct {
+	Value bool
+}
+
+// NewERC20Data creates a new ERC20Data instance
+func NewERC20Data(name, symbol string, decimals uint8) ERC20Data {
+	return ERC20Data{
+		Name:     name,
+		Symbol:   symbol,
+		Decimals: decimals,
+	}
+}
diff --git a/x/erc20/types/evm_test.go b/x/erc20/types/evm_test.go
new file mode 100644
index 00000000..83605b6d
--- /dev/null
+++ b/x/erc20/types/evm_test.go
@@ -0,0 +1,13 @@
+package types
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestNewERC20Data(t *testing.T) {
+	data := NewERC20Data("test", "ERC20", uint8(18))
+	exp := ERC20Data(ERC20Data{Name: "test", Symbol: "ERC20", Decimals: 0x12})
+	require.Equal(t, exp, data)
+}
diff --git a/x/erc20/types/genesis.go b/x/erc20/types/genesis.go
new file mode 100644
index 00000000..dab7227c
--- /dev/null
+++ b/x/erc20/types/genesis.go
@@ -0,0 +1,44 @@
+package types
+
+import "fmt"
+
+// NewGenesisState creates a new genesis state.
+func NewGenesisState(params Params, pairs []TokenPair) GenesisState {
+	return GenesisState{
+		Params:     params,
+		TokenPairs: pairs,
+	}
+}
+
+// DefaultGenesisState sets default evm genesis state with empty accounts and
+// default params and chain config values.
+func DefaultGenesisState() *GenesisState {
+	return &GenesisState{
+		Params: DefaultParams(),
+	}
+}
+
+// Validate performs basic genesis state validation returning an error upon any
+// failure.
+func (gs GenesisState) Validate() error {
+	seenErc20 := make(map[string]bool)
+	seenDenom := make(map[string]bool)
+
+	for _, b := range gs.TokenPairs {
+		if seenErc20[b.Erc20Address] {
+			return fmt.Errorf("token ERC20 contract duplicated on genesis '%s'", b.Erc20Address)
+		}
+		if seenDenom[b.Denom] {
+			return fmt.Errorf("coin denomination duplicated on genesis: '%s'", b.Denom)
+		}
+
+		if err := b.Validate(); err != nil {
+			return err
+		}
+
+		seenErc20[b.Erc20Address] = true
+		seenDenom[b.Denom] = true
+	}
+
+	return gs.Params.Validate()
+}
diff --git a/x/erc20/types/genesis.pb.go b/x/erc20/types/genesis.pb.go
new file mode 100644
index 00000000..cda92dba
--- /dev/null
+++ b/x/erc20/types/genesis.pb.go
@@ -0,0 +1,598 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: canto/erc20/v1/genesis.proto
+
+package types
+
+import (
+	fmt "fmt"
+	_ "github.com/gogo/protobuf/gogoproto"
+	proto "github.com/gogo/protobuf/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
+
+// GenesisState defines the module's genesis state.
+type GenesisState struct {
+	// module parameters
+	Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"`
+	// registered token pairs
+	TokenPairs []TokenPair `protobuf:"bytes,2,rep,name=token_pairs,json=tokenPairs,proto3" json:"token_pairs"`
+}
+
+func (m *GenesisState) Reset()         { *m = GenesisState{} }
+func (m *GenesisState) String() string { return proto.CompactTextString(m) }
+func (*GenesisState) ProtoMessage()    {}
+func (*GenesisState) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6af5bf0eee46eaa1, []int{0}
+}
+func (m *GenesisState) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_GenesisState.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 *GenesisState) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_GenesisState.Merge(m, src)
+}
+func (m *GenesisState) XXX_Size() int {
+	return m.Size()
+}
+func (m *GenesisState) XXX_DiscardUnknown() {
+	xxx_messageInfo_GenesisState.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GenesisState proto.InternalMessageInfo
+
+func (m *GenesisState) GetParams() Params {
+	if m != nil {
+		return m.Params
+	}
+	return Params{}
+}
+
+func (m *GenesisState) GetTokenPairs() []TokenPair {
+	if m != nil {
+		return m.TokenPairs
+	}
+	return nil
+}
+
+// Params defines the erc20 module params
+type Params struct {
+	// parameter to enable the conversion of Cosmos coins <--> ERC20 tokens.
+	EnableErc20 bool `protobuf:"varint,1,opt,name=enable_erc20,json=enableErc20,proto3" json:"enable_erc20,omitempty"`
+	// parameter to enable the EVM hook that converts an ERC20 token to a Cosmos
+	// Coin by transferring the Tokens through a MsgEthereumTx to the
+	// ModuleAddress Ethereum address.
+	EnableEVMHook bool `protobuf:"varint,2,opt,name=enable_evm_hook,json=enableEvmHook,proto3" json:"enable_evm_hook,omitempty"`
+}
+
+func (m *Params) Reset()         { *m = Params{} }
+func (m *Params) String() string { return proto.CompactTextString(m) }
+func (*Params) ProtoMessage()    {}
+func (*Params) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6af5bf0eee46eaa1, []int{1}
+}
+func (m *Params) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_Params.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 *Params) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Params.Merge(m, src)
+}
+func (m *Params) XXX_Size() int {
+	return m.Size()
+}
+func (m *Params) XXX_DiscardUnknown() {
+	xxx_messageInfo_Params.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Params proto.InternalMessageInfo
+
+func (m *Params) GetEnableErc20() bool {
+	if m != nil {
+		return m.EnableErc20
+	}
+	return false
+}
+
+func (m *Params) GetEnableEVMHook() bool {
+	if m != nil {
+		return m.EnableEVMHook
+	}
+	return false
+}
+
+func init() {
+	proto.RegisterType((*GenesisState)(nil), "canto.erc20.v1.GenesisState")
+	proto.RegisterType((*Params)(nil), "canto.erc20.v1.Params")
+}
+
+func init() { proto.RegisterFile("canto/erc20/v1/genesis.proto", fileDescriptor_6af5bf0eee46eaa1) }
+
+var fileDescriptor_6af5bf0eee46eaa1 = []byte{
+	// 311 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x49, 0x4e, 0xcc, 0x2b,
+	0xc9, 0xd7, 0x4f, 0x2d, 0x4a, 0x36, 0x32, 0xd0, 0x2f, 0x33, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d,
+	0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x03, 0xcb, 0xea, 0x81, 0x65, 0xf5,
+	0xca, 0x0c, 0xa5, 0xa4, 0xd0, 0x54, 0x43, 0x24, 0xc0, 0x6a, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3,
+	0xc1, 0x4c, 0x7d, 0x10, 0x0b, 0x22, 0xaa, 0xd4, 0xc6, 0xc8, 0xc5, 0xe3, 0x0e, 0x31, 0x33, 0xb8,
+	0x24, 0xb1, 0x24, 0x55, 0xc8, 0x84, 0x8b, 0xad, 0x20, 0xb1, 0x28, 0x31, 0xb7, 0x58, 0x82, 0x51,
+	0x81, 0x51, 0x83, 0xdb, 0x48, 0x4c, 0x0f, 0xd5, 0x0e, 0xbd, 0x00, 0xb0, 0xac, 0x13, 0xcb, 0x89,
+	0x7b, 0xf2, 0x0c, 0x41, 0x50, 0xb5, 0x42, 0x0e, 0x5c, 0xdc, 0x25, 0xf9, 0xd9, 0xa9, 0x79, 0xf1,
+	0x05, 0x89, 0x99, 0x45, 0xc5, 0x12, 0x4c, 0x0a, 0xcc, 0x1a, 0xdc, 0x46, 0x92, 0xe8, 0x5a, 0x43,
+	0x40, 0x4a, 0x02, 0x12, 0x33, 0x8b, 0xa0, 0xba, 0xb9, 0x4a, 0x60, 0x02, 0xc5, 0x4a, 0x69, 0x5c,
+	0x6c, 0x10, 0x93, 0x85, 0x14, 0xb9, 0x78, 0x52, 0xf3, 0x12, 0x93, 0x72, 0x52, 0xe3, 0xc1, 0x1a,
+	0xc1, 0xee, 0xe0, 0x08, 0xe2, 0x86, 0x88, 0xb9, 0x82, 0x84, 0x84, 0x2c, 0xb9, 0xf8, 0x61, 0x4a,
+	0xca, 0x72, 0xe3, 0x33, 0xf2, 0xf3, 0xb3, 0x25, 0x98, 0x40, 0xaa, 0x9c, 0x04, 0x1f, 0xdd, 0x93,
+	0xe7, 0x75, 0x85, 0xa8, 0x0c, 0xf3, 0xf5, 0xc8, 0xcf, 0xcf, 0x0e, 0xe2, 0x85, 0x6a, 0x2c, 0xcb,
+	0x05, 0x71, 0x9d, 0x3c, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39,
+	0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0x4a, 0x3f,
+	0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0xdf, 0x19, 0xe4, 0x70, 0x5d, 0xbf,
+	0xd4, 0x92, 0xf2, 0xfc, 0xa2, 0x6c, 0x08, 0x4f, 0xbf, 0xcc, 0x48, 0xbf, 0x02, 0x1a, 0xb4, 0x25,
+	0x95, 0x05, 0xa9, 0xc5, 0x49, 0x6c, 0xe0, 0x20, 0x34, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x05,
+	0x60, 0x6f, 0xf6, 0xa4, 0x01, 0x00, 0x00,
+}
+
+func (m *GenesisState) 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 *GenesisState) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.TokenPairs) > 0 {
+		for iNdEx := len(m.TokenPairs) - 1; iNdEx >= 0; iNdEx-- {
+			{
+				size, err := m.TokenPairs[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintGenesis(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0x12
+		}
+	}
+	{
+		size, err := m.Params.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintGenesis(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0xa
+	return len(dAtA) - i, nil
+}
+
+func (m *Params) 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 *Params) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.EnableEVMHook {
+		i--
+		if m.EnableEVMHook {
+			dAtA[i] = 1
+		} else {
+			dAtA[i] = 0
+		}
+		i--
+		dAtA[i] = 0x10
+	}
+	if m.EnableErc20 {
+		i--
+		if m.EnableErc20 {
+			dAtA[i] = 1
+		} else {
+			dAtA[i] = 0
+		}
+		i--
+		dAtA[i] = 0x8
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int {
+	offset -= sovGenesis(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *GenesisState) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = m.Params.Size()
+	n += 1 + l + sovGenesis(uint64(l))
+	if len(m.TokenPairs) > 0 {
+		for _, e := range m.TokenPairs {
+			l = e.Size()
+			n += 1 + l + sovGenesis(uint64(l))
+		}
+	}
+	return n
+}
+
+func (m *Params) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.EnableErc20 {
+		n += 2
+	}
+	if m.EnableEVMHook {
+		n += 2
+	}
+	return n
+}
+
+func sovGenesis(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozGenesis(x uint64) (n int) {
+	return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *GenesisState) 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 ErrIntOverflowGenesis
+			}
+			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: GenesisState: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenesis
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field TokenPairs", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenesis
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.TokenPairs = append(m.TokenPairs, TokenPair{})
+			if err := m.TokenPairs[len(m.TokenPairs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipGenesis(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *Params) 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 ErrIntOverflowGenesis
+			}
+			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: Params: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field EnableErc20", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenesis
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				v |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.EnableErc20 = bool(v != 0)
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field EnableEVMHook", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenesis
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				v |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.EnableEVMHook = bool(v != 0)
+		default:
+			iNdEx = preIndex
+			skippy, err := skipGenesis(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipGenesis(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, ErrIntOverflowGenesis
+			}
+			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, ErrIntOverflowGenesis
+				}
+				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, ErrIntOverflowGenesis
+				}
+				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, ErrInvalidLengthGenesis
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupGenesis
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthGenesis
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthGenesis        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowGenesis          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/x/erc20/types/genesis_test.go b/x/erc20/types/genesis_test.go
new file mode 100644
index 00000000..a3997232
--- /dev/null
+++ b/x/erc20/types/genesis_test.go
@@ -0,0 +1,148 @@
+package types
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/suite"
+)
+
+type GenesisTestSuite struct {
+	suite.Suite
+}
+
+func (suite *GenesisTestSuite) SetupTest() {
+}
+
+func TestGenesisTestSuite(t *testing.T) {
+	suite.Run(t, new(GenesisTestSuite))
+}
+
+func (suite *GenesisTestSuite) TestValidateGenesis() {
+	newGen := NewGenesisState(DefaultParams(), []TokenPair{})
+
+	testCases := []struct {
+		name     string
+		genState *GenesisState
+		expPass  bool
+	}{
+		{
+			name:     "valid genesis constructor",
+			genState: &newGen,
+			expPass:  true,
+		},
+		{
+			name:     "default",
+			genState: DefaultGenesisState(),
+			expPass:  true,
+		},
+		{
+			name: "valid genesis",
+			genState: &GenesisState{
+				Params:     DefaultParams(),
+				TokenPairs: []TokenPair{},
+			},
+			expPass: true,
+		},
+		{
+			name: "valid genesis - with tokens pairs",
+			genState: &GenesisState{
+				Params: DefaultParams(),
+				TokenPairs: []TokenPair{
+					{
+						Erc20Address: "0xdac17f958d2ee523a2206206994597c13d831ec7",
+						Denom:        "usdt",
+						Enabled:      true,
+					},
+				},
+			},
+			expPass: true,
+		},
+		{
+			name: "invalid genesis - duplicated token pair",
+			genState: &GenesisState{
+				Params: DefaultParams(),
+				TokenPairs: []TokenPair{
+					{
+						Erc20Address: "0xdac17f958d2ee523a2206206994597c13d831ec7",
+						Denom:        "usdt",
+						Enabled:      true,
+					},
+					{
+						Erc20Address: "0xdac17f958d2ee523a2206206994597c13d831ec7",
+						Denom:        "usdt",
+						Enabled:      true,
+					},
+				},
+			},
+			expPass: false,
+		},
+		{
+			name: "invalid genesis - duplicated token pair",
+			genState: &GenesisState{
+				Params: DefaultParams(),
+				TokenPairs: []TokenPair{
+					{
+						Erc20Address: "0xdac17f958d2ee523a2206206994597c13d831ec7",
+						Denom:        "usdt",
+						Enabled:      true,
+					},
+					{
+						Erc20Address: "0xdac17f958d2ee523a2206206994597c13d831ec7",
+						Denom:        "usdt2",
+						Enabled:      true,
+					},
+				},
+			},
+			expPass: false,
+		},
+		{
+			name: "invalid genesis - duplicated token pair",
+			genState: &GenesisState{
+				Params: DefaultParams(),
+				TokenPairs: []TokenPair{
+					{
+						Erc20Address: "0xdac17f958d2ee523a2206206994597c13d831ec7",
+						Denom:        "usdt",
+						Enabled:      true,
+					},
+					{
+						Erc20Address: "0xB8c77482e45F1F44dE1745F52C74426C631bDD52",
+						Denom:        "usdt",
+						Enabled:      true,
+					},
+				},
+			},
+			expPass: false,
+		},
+		{
+			name: "invalid genesis - invalid token pair",
+			genState: &GenesisState{
+				Params: DefaultParams(),
+				TokenPairs: []TokenPair{
+					{
+						Erc20Address: "0xinvalidaddress",
+						Denom:        "bad",
+						Enabled:      true,
+					},
+				},
+			},
+			expPass: false,
+		},
+		{
+			// Voting period cant be zero
+			name:     "empty genesis",
+			genState: &GenesisState{},
+			expPass:  true,
+		},
+	}
+
+	for _, tc := range testCases {
+		tc := tc
+		err := tc.genState.Validate()
+		if tc.expPass {
+			suite.Require().NoError(err, tc.name)
+		} else {
+			suite.Require().Error(err, tc.name)
+		}
+	}
+}
diff --git a/x/erc20/types/interfaces.go b/x/erc20/types/interfaces.go
new file mode 100644
index 00000000..497106ab
--- /dev/null
+++ b/x/erc20/types/interfaces.go
@@ -0,0 +1,43 @@
+package types
+
+import (
+	context "context"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core"
+	"github.com/ethereum/go-ethereum/core/vm"
+
+	"github.com/evmos/ethermint/x/evm/statedb"
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+)
+
+// AccountKeeper defines the expected interface needed to retrieve account info.
+type AccountKeeper interface {
+	GetModuleAddress(moduleName string) sdk.AccAddress
+	GetSequence(sdk.Context, sdk.AccAddress) (uint64, error)
+}
+
+// BankKeeper defines the expected interface needed to retrieve account balances.
+type BankKeeper interface {
+	SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
+	SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
+	MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
+	BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
+	IsSendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool
+	BlockedAddr(addr sdk.AccAddress) bool
+	GetDenomMetaData(ctx sdk.Context, denom string) (banktypes.Metadata, bool)
+	SetDenomMetaData(ctx sdk.Context, denomMetaData banktypes.Metadata)
+	HasSupply(ctx sdk.Context, denom string) bool
+	GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
+}
+
+// EVMKeeper defines the expected EVM keeper interface used on erc20
+type EVMKeeper interface {
+	GetParams(ctx sdk.Context) evmtypes.Params
+	GetAccountWithoutBalance(ctx sdk.Context, addr common.Address) *statedb.Account
+	EstimateGas(c context.Context, req *evmtypes.EthCallRequest) (*evmtypes.EstimateGasResponse, error)
+	ApplyMessage(ctx sdk.Context, msg core.Message, tracer vm.EVMLogger, commit bool) (*evmtypes.MsgEthereumTxResponse, error)
+}
diff --git a/x/erc20/types/keys.go b/x/erc20/types/keys.go
new file mode 100644
index 00000000..65c4346a
--- /dev/null
+++ b/x/erc20/types/keys.go
@@ -0,0 +1,39 @@
+package types
+
+import (
+	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+	"github.com/ethereum/go-ethereum/common"
+)
+
+// constants
+const (
+	// module name
+	ModuleName = "erc20"
+
+	// StoreKey to be used when creating the KVStore
+	StoreKey = ModuleName
+
+	// RouterKey to be used for message routing
+	RouterKey = ModuleName
+)
+
+// ModuleAddress is the native module address for EVM
+var ModuleAddress common.Address
+
+func init() {
+	ModuleAddress = common.BytesToAddress(authtypes.NewModuleAddress(ModuleName).Bytes())
+}
+
+// prefix bytes for the EVM persistent store
+const (
+	prefixTokenPair = iota + 1
+	prefixTokenPairByERC20
+	prefixTokenPairByDenom
+)
+
+// KVStore key prefixes
+var (
+	KeyPrefixTokenPair        = []byte{prefixTokenPair}
+	KeyPrefixTokenPairByERC20 = []byte{prefixTokenPairByERC20}
+	KeyPrefixTokenPairByDenom = []byte{prefixTokenPairByDenom}
+)
diff --git a/x/erc20/types/msg.go b/x/erc20/types/msg.go
new file mode 100644
index 00000000..3ad959df
--- /dev/null
+++ b/x/erc20/types/msg.go
@@ -0,0 +1,111 @@
+package types
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+
+	ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
+	"github.com/ethereum/go-ethereum/common"
+)
+
+var (
+	_ sdk.Msg = &MsgConvertCoin{}
+	_ sdk.Msg = &MsgConvertERC20{}
+)
+
+const (
+	TypeMsgConvertCoin  = "convert_coin"
+	TypeMsgConvertERC20 = "convert_ERC20"
+)
+
+// NewMsgConvertCoin creates a new instance of MsgConvertCoin
+func NewMsgConvertCoin(coin sdk.Coin, receiver common.Address, sender sdk.AccAddress) *MsgConvertCoin { // nolint: interfacer
+	return &MsgConvertCoin{
+		Coin:     coin,
+		Receiver: receiver.Hex(),
+		Sender:   sender.String(),
+	}
+}
+
+// Route should return the name of the module
+func (msg MsgConvertCoin) Route() string { return RouterKey }
+
+// Type should return the action
+func (msg MsgConvertCoin) Type() string { return TypeMsgConvertCoin }
+
+// ValidateBasic runs stateless checks on the message
+func (msg MsgConvertCoin) ValidateBasic() error {
+	if err := ValidateErc20Denom(msg.Coin.Denom); err != nil {
+		if err := ibctransfertypes.ValidateIBCDenom(msg.Coin.Denom); err != nil {
+			return err
+		}
+	}
+
+	if !msg.Coin.Amount.IsPositive() {
+		return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "cannot mint a non-positive amount")
+	}
+	_, err := sdk.AccAddressFromBech32(msg.Sender)
+	if err != nil {
+		return sdkerrors.Wrap(err, "invalid sender address")
+	}
+	if !common.IsHexAddress(msg.Receiver) {
+		return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid receiver hex address %s", msg.Receiver)
+	}
+	return nil
+}
+
+// GetSignBytes encodes the message for signing
+func (msg MsgConvertCoin) GetSignBytes() []byte {
+	return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg))
+}
+
+// GetSigners defines whose signature is required
+func (msg MsgConvertCoin) GetSigners() []sdk.AccAddress {
+	addr := sdk.MustAccAddressFromBech32(msg.Sender)
+	return []sdk.AccAddress{addr}
+}
+
+// NewMsgConvertERC20 creates a new instance of MsgConvertERC20
+func NewMsgConvertERC20(amount sdk.Int, receiver sdk.AccAddress, contract, sender common.Address) *MsgConvertERC20 { // nolint: interfacer
+	return &MsgConvertERC20{
+		ContractAddress: contract.String(),
+		Amount:          amount,
+		Receiver:        receiver.String(),
+		Sender:          sender.Hex(),
+	}
+}
+
+// Route should return the name of the module
+func (msg MsgConvertERC20) Route() string { return RouterKey }
+
+// Type should return the action
+func (msg MsgConvertERC20) Type() string { return TypeMsgConvertERC20 }
+
+// ValidateBasic runs stateless checks on the message
+func (msg MsgConvertERC20) ValidateBasic() error {
+	if !common.IsHexAddress(msg.ContractAddress) {
+		return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid contract hex address '%s'", msg.ContractAddress)
+	}
+	if !msg.Amount.IsPositive() {
+		return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "cannot mint a non-positive amount")
+	}
+	_, err := sdk.AccAddressFromBech32(msg.Receiver)
+	if err != nil {
+		return sdkerrors.Wrap(err, "invalid receiver address")
+	}
+	if !common.IsHexAddress(msg.Sender) {
+		return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid sender hex address %s", msg.Sender)
+	}
+	return nil
+}
+
+// GetSignBytes encodes the message for signing
+func (msg MsgConvertERC20) GetSignBytes() []byte {
+	return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg))
+}
+
+// GetSigners defines whose signature is required
+func (msg MsgConvertERC20) GetSigners() []sdk.AccAddress {
+	addr := common.HexToAddress(msg.Sender)
+	return []sdk.AccAddress{addr.Bytes()}
+}
diff --git a/x/erc20/types/msg_test.go b/x/erc20/types/msg_test.go
new file mode 100644
index 00000000..952bad8b
--- /dev/null
+++ b/x/erc20/types/msg_test.go
@@ -0,0 +1,247 @@
+package types
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/suite"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/evmos/ethermint/tests"
+
+	"github.com/ethereum/go-ethereum/common"
+)
+
+type MsgsTestSuite struct {
+	suite.Suite
+}
+
+func TestMsgsTestSuite(t *testing.T) {
+	suite.Run(t, new(MsgsTestSuite))
+}
+
+func (suite *MsgsTestSuite) TestMsgConvertCoinGetters() {
+	msgInvalid := MsgConvertCoin{}
+	msg := NewMsgConvertCoin(
+		sdk.NewCoin("test", sdk.NewInt(100)),
+		tests.GenerateAddress(),
+		sdk.AccAddress(tests.GenerateAddress().Bytes()),
+	)
+	suite.Require().Equal(RouterKey, msg.Route())
+	suite.Require().Equal(TypeMsgConvertCoin, msg.Type())
+	suite.Require().NotNil(msgInvalid.GetSignBytes())
+	suite.Require().NotNil(msg.GetSigners())
+}
+
+func (suite *MsgsTestSuite) TestMsgConvertCoinNew() {
+	testCases := []struct {
+		msg        string
+		coin       sdk.Coin
+		receiver   common.Address
+		sender     sdk.AccAddress
+		expectPass bool
+	}{
+		{
+			"msg convert coin - pass",
+			sdk.NewCoin("test", sdk.NewInt(100)),
+			tests.GenerateAddress(),
+			sdk.AccAddress(tests.GenerateAddress().Bytes()),
+			true,
+		},
+	}
+
+	for i, tc := range testCases {
+		tx := NewMsgConvertCoin(tc.coin, tc.receiver, tc.sender)
+		err := tx.ValidateBasic()
+
+		if tc.expectPass {
+			suite.Require().NoError(err, "valid test %d failed: %s, %v", i, tc.msg)
+		} else {
+			suite.Require().Error(err, "invalid test %d passed: %s, %v", i, tc.msg)
+		}
+	}
+}
+
+func (suite *MsgsTestSuite) TestMsgConvertCoin() {
+	testCases := []struct {
+		msg        string
+		coin       sdk.Coin
+		receiver   string
+		sender     string
+		expectPass bool
+	}{
+		{
+			"invalid denom",
+			sdk.Coin{
+				Denom:  "",
+				Amount: sdk.NewInt(100),
+			},
+			"0x0000",
+			tests.GenerateAddress().String(),
+			false,
+		},
+		{
+			"negative coin amount",
+			sdk.Coin{
+				Denom:  "coin",
+				Amount: sdk.NewInt(-100),
+			},
+			"0x0000",
+			tests.GenerateAddress().String(),
+			false,
+		},
+		{
+			"msg convert coin - invalid sender",
+			sdk.NewCoin("coin", sdk.NewInt(100)),
+			tests.GenerateAddress().String(),
+			"cantoinvalid",
+			false,
+		},
+		{
+			"msg convert coin - invalid receiver",
+			sdk.NewCoin("coin", sdk.NewInt(100)),
+			"0x0000",
+			sdk.AccAddress(tests.GenerateAddress().Bytes()).String(),
+			false,
+		},
+		{
+			"msg convert coin - pass",
+			sdk.NewCoin("coin", sdk.NewInt(100)),
+			tests.GenerateAddress().String(),
+			sdk.AccAddress(tests.GenerateAddress().Bytes()).String(),
+			true,
+		},
+		{
+			"msg convert coin - pass with `erc20/` denom",
+			sdk.NewCoin("erc20/0xdac17f958d2ee523a2206206994597c13d831ec7", sdk.NewInt(100)),
+			tests.GenerateAddress().String(),
+			sdk.AccAddress(tests.GenerateAddress().Bytes()).String(),
+			true,
+		},
+		{
+			"msg convert coin - pass with `ibc/{hash}` denom",
+			sdk.NewCoin("ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdk.NewInt(100)),
+			tests.GenerateAddress().String(),
+			sdk.AccAddress(tests.GenerateAddress().Bytes()).String(),
+			true,
+		},
+	}
+
+	for i, tc := range testCases {
+		tx := MsgConvertCoin{tc.coin, tc.receiver, tc.sender}
+		err := tx.ValidateBasic()
+
+		if tc.expectPass {
+			suite.Require().NoError(err, "valid test %d failed: %s, %v", i, tc.msg)
+		} else {
+			suite.Require().Error(err, "invalid test %d passed: %s, %v", i, tc.msg)
+		}
+	}
+}
+
+func (suite *MsgsTestSuite) TestMsgConvertERC20Getters() {
+	msgInvalid := MsgConvertERC20{}
+	msg := NewMsgConvertERC20(
+		sdk.NewInt(100),
+		sdk.AccAddress(tests.GenerateAddress().Bytes()),
+		tests.GenerateAddress(),
+		tests.GenerateAddress(),
+	)
+	suite.Require().Equal(RouterKey, msg.Route())
+	suite.Require().Equal(TypeMsgConvertERC20, msg.Type())
+	suite.Require().NotNil(msgInvalid.GetSignBytes())
+	suite.Require().NotNil(msg.GetSigners())
+}
+
+func (suite *MsgsTestSuite) TestMsgConvertERC20New() {
+	testCases := []struct {
+		msg        string
+		amount     sdk.Int
+		receiver   sdk.AccAddress
+		contract   common.Address
+		sender     common.Address
+		expectPass bool
+	}{
+		{
+			"msg convert erc20 - pass",
+			sdk.NewInt(100),
+			sdk.AccAddress(tests.GenerateAddress().Bytes()),
+			tests.GenerateAddress(),
+			tests.GenerateAddress(),
+			true,
+		},
+	}
+
+	for i, tc := range testCases {
+		tx := NewMsgConvertERC20(tc.amount, tc.receiver, tc.contract, tc.sender)
+		err := tx.ValidateBasic()
+
+		if tc.expectPass {
+			suite.Require().NoError(err, "valid test %d failed: %s, %v", i, tc.msg)
+		} else {
+			suite.Require().Error(err, "invalid test %d passed: %s, %v", i, tc.msg)
+		}
+	}
+}
+
+func (suite *MsgsTestSuite) TestMsgConvertERC20() {
+	testCases := []struct {
+		msg        string
+		amount     sdk.Int
+		receiver   string
+		contract   string
+		sender     string
+		expectPass bool
+	}{
+		{
+			"invalid contract hex address",
+			sdk.NewInt(100),
+			sdk.AccAddress(tests.GenerateAddress().Bytes()).String(),
+			sdk.AccAddress{}.String(),
+			tests.GenerateAddress().String(),
+			false,
+		},
+		{
+			"negative coin amount",
+			sdk.NewInt(-100),
+			sdk.AccAddress(tests.GenerateAddress().Bytes()).String(),
+			tests.GenerateAddress().String(),
+			tests.GenerateAddress().String(),
+			false,
+		},
+		{
+			"invalid receiver address",
+			sdk.NewInt(100),
+			sdk.AccAddress{}.String(),
+			tests.GenerateAddress().String(),
+			tests.GenerateAddress().String(),
+			false,
+		},
+		{
+			"invalid sender address",
+			sdk.NewInt(100),
+			sdk.AccAddress(tests.GenerateAddress().Bytes()).String(),
+			tests.GenerateAddress().String(),
+			sdk.AccAddress{}.String(),
+			false,
+		},
+		{
+			"msg convert erc20 - pass",
+			sdk.NewInt(100),
+			sdk.AccAddress(tests.GenerateAddress().Bytes()).String(),
+			tests.GenerateAddress().String(),
+			tests.GenerateAddress().String(),
+			true,
+		},
+	}
+
+	for i, tc := range testCases {
+		tx := MsgConvertERC20{tc.contract, tc.amount, tc.receiver, tc.sender}
+		err := tx.ValidateBasic()
+
+		if tc.expectPass {
+			suite.Require().NoError(err, "valid test %d failed: %s, %v", i, tc.msg)
+		} else {
+			suite.Require().Error(err, "invalid test %d passed: %s, %v", i, tc.msg)
+		}
+	}
+}
diff --git a/x/erc20/types/params.go b/x/erc20/types/params.go
new file mode 100644
index 00000000..8d91a6e2
--- /dev/null
+++ b/x/erc20/types/params.go
@@ -0,0 +1,57 @@
+package types
+
+import (
+	fmt "fmt"
+
+	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
+)
+
+// Parameter store key
+var (
+	ParamStoreKeyEnableErc20   = []byte("EnableErc20")
+	ParamStoreKeyEnableEVMHook = []byte("EnableEVMHook")
+)
+
+var _ paramtypes.ParamSet = &Params{}
+
+// ParamKeyTable returns the parameter key table.
+func ParamKeyTable() paramtypes.KeyTable {
+	return paramtypes.NewKeyTable().RegisterParamSet(&Params{})
+}
+
+// NewParams creates a new Params object
+func NewParams(
+	enableErc20 bool,
+	enableEVMHook bool,
+) Params {
+	return Params{
+		EnableErc20:   enableErc20,
+		EnableEVMHook: enableEVMHook,
+	}
+}
+
+func DefaultParams() Params {
+	return Params{
+		EnableErc20:   true,
+		EnableEVMHook: true,
+	}
+}
+
+func validateBool(i interface{}) error {
+	_, ok := i.(bool)
+	if !ok {
+		return fmt.Errorf("invalid parameter type: %T", i)
+	}
+
+	return nil
+}
+
+// ParamSetPairs returns the parameter set pairs.
+func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
+	return paramtypes.ParamSetPairs{
+		paramtypes.NewParamSetPair(ParamStoreKeyEnableErc20, &p.EnableErc20, validateBool),
+		paramtypes.NewParamSetPair(ParamStoreKeyEnableEVMHook, &p.EnableEVMHook, validateBool),
+	}
+}
+
+func (p Params) Validate() error { return nil }
diff --git a/x/erc20/types/params_test.go b/x/erc20/types/params_test.go
new file mode 100644
index 00000000..3b6bb56a
--- /dev/null
+++ b/x/erc20/types/params_test.go
@@ -0,0 +1,55 @@
+package types
+
+import (
+	"testing"
+
+	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
+	"github.com/stretchr/testify/suite"
+)
+
+type ParamsTestSuite struct {
+	suite.Suite
+}
+
+func TestParamsTestSuite(t *testing.T) {
+	suite.Run(t, new(ParamsTestSuite))
+}
+
+func (suite *ParamsTestSuite) TestParamKeyTable() {
+	suite.Require().IsType(paramtypes.KeyTable{}, ParamKeyTable())
+}
+
+func (suite *ParamsTestSuite) TestParamsValidate() {
+	testCases := []struct {
+		name     string
+		params   Params
+		expError bool
+	}{
+		{"default", DefaultParams(), false},
+		{
+			"valid",
+			NewParams(true, true),
+			false,
+		},
+		{
+			"empty",
+			Params{},
+			false,
+		},
+	}
+
+	for _, tc := range testCases {
+		err := tc.params.Validate()
+
+		if tc.expError {
+			suite.Require().Error(err, tc.name)
+		} else {
+			suite.Require().NoError(err, tc.name)
+		}
+	}
+}
+
+func (suite *ParamsTestSuite) TestParamsValidatePriv() {
+	suite.Require().Error(validateBool(1))
+	suite.Require().NoError(validateBool(true))
+}
diff --git a/x/erc20/types/proposal.go b/x/erc20/types/proposal.go
new file mode 100644
index 00000000..741eef99
--- /dev/null
+++ b/x/erc20/types/proposal.go
@@ -0,0 +1,163 @@
+package types
+
+import (
+	"fmt"
+	"strings"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
+	ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
+	ethermint "github.com/evmos/ethermint/types"
+)
+
+// constants
+const (
+	ProposalTypeRegisterCoin          string = "RegisterCoin"
+	ProposalTypeRegisterERC20         string = "RegisterERC20"
+	ProposalTypeToggleTokenConversion string = "ToggleTokenConversion" // #nosec
+)
+
+// Implements Proposal Interface
+var (
+	_ govv1beta1.Content = &RegisterCoinProposal{}
+	_ govv1beta1.Content = &RegisterERC20Proposal{}
+	_ govv1beta1.Content = &ToggleTokenConversionProposal{}
+)
+
+func init() {
+	govv1beta1.RegisterProposalType(ProposalTypeRegisterCoin)
+	govv1beta1.RegisterProposalType(ProposalTypeRegisterERC20)
+	govv1beta1.RegisterProposalType(ProposalTypeToggleTokenConversion)
+}
+
+// CreateDenomDescription generates a string with the coin description
+func CreateDenomDescription(address string) string {
+	return fmt.Sprintf("Cosmos coin token representation of %s", address)
+}
+
+// CreateDenom generates a string the module name plus the address to avoid conflicts with names staring with a number
+func CreateDenom(address string) string {
+	return fmt.Sprintf("%s/%s", ModuleName, address)
+}
+
+// NewRegisterCoinProposal returns new instance of RegisterCoinProposal
+func NewRegisterCoinProposal(title, description string, coinMetadata banktypes.Metadata) govv1beta1.Content {
+	return &RegisterCoinProposal{
+		Title:       title,
+		Description: description,
+		Metadata:    coinMetadata,
+	}
+}
+
+// ProposalRoute returns router key for this proposal
+func (*RegisterCoinProposal) ProposalRoute() string { return RouterKey }
+
+// ProposalType returns proposal type for this proposal
+func (*RegisterCoinProposal) ProposalType() string {
+	return ProposalTypeRegisterCoin
+}
+
+// ValidateBasic performs a stateless check of the proposal fields
+func (rtbp *RegisterCoinProposal) ValidateBasic() error {
+	if err := rtbp.Metadata.Validate(); err != nil {
+		return err
+	}
+
+	if err := ibctransfertypes.ValidateIBCDenom(rtbp.Metadata.Base); err != nil {
+		return err
+	}
+
+	if err := validateIBCVoucherMetadata(rtbp.Metadata); err != nil {
+		return err
+	}
+
+	return govv1beta1.ValidateAbstract(rtbp)
+}
+
+// validateIBCVoucherMetadata checks that the coin metadata fields are consistent
+// with an IBC voucher denomination.
+func validateIBCVoucherMetadata(metadata banktypes.Metadata) error {
+	// Check ibc/ denom
+	denomSplit := strings.SplitN(metadata.Base, "/", 2)
+
+	if denomSplit[0] == metadata.Base && strings.TrimSpace(metadata.Base) != "" {
+		// Not IBC
+		return nil
+	}
+
+	if len(denomSplit) != 2 || denomSplit[0] != ibctransfertypes.DenomPrefix {
+		// NOTE: should be unaccessible (covered on ValidateIBCDenom)
+		return fmt.Errorf("invalid metadata. %s denomination should be prefixed with the format 'ibc/", metadata.Base)
+	}
+
+	return nil
+}
+
+// ValidateErc20Denom checks if a denom is a valid erc20/
+// denomination
+func ValidateErc20Denom(denom string) error {
+	denomSplit := strings.SplitN(denom, "/", 2)
+
+	if len(denomSplit) != 2 || denomSplit[0] != ModuleName {
+		return fmt.Errorf("invalid denom. %s denomination should be prefixed with the format 'erc20/", denom)
+	}
+
+	return ethermint.ValidateAddress(denomSplit[1])
+}
+
+// NewRegisterERC20Proposal returns new instance of RegisterERC20Proposal
+func NewRegisterERC20Proposal(title, description, erc20Addr string) govv1beta1.Content {
+	return &RegisterERC20Proposal{
+		Title:        title,
+		Description:  description,
+		Erc20Address: erc20Addr,
+	}
+}
+
+// ProposalRoute returns router key for this proposal
+func (*RegisterERC20Proposal) ProposalRoute() string { return RouterKey }
+
+// ProposalType returns proposal type for this proposal
+func (*RegisterERC20Proposal) ProposalType() string {
+	return ProposalTypeRegisterERC20
+}
+
+// ValidateBasic performs a stateless check of the proposal fields
+func (rtbp *RegisterERC20Proposal) ValidateBasic() error {
+	if err := ethermint.ValidateAddress(rtbp.Erc20Address); err != nil {
+		return sdkerrors.Wrap(err, "ERC20 address")
+	}
+	return govv1beta1.ValidateAbstract(rtbp)
+}
+
+// NewToggleTokenConversionProposal returns new instance of ToggleTokenConversionProposal
+func NewToggleTokenConversionProposal(title, description string, token string) govv1beta1.Content {
+	return &ToggleTokenConversionProposal{
+		Title:       title,
+		Description: description,
+		Token:       token,
+	}
+}
+
+// ProposalRoute returns router key for this proposal
+func (*ToggleTokenConversionProposal) ProposalRoute() string { return RouterKey }
+
+// ProposalType returns proposal type for this proposal
+func (*ToggleTokenConversionProposal) ProposalType() string {
+	return ProposalTypeToggleTokenConversion
+}
+
+// ValidateBasic performs a stateless check of the proposal fields
+func (ttcp *ToggleTokenConversionProposal) ValidateBasic() error {
+	// check if the token is a hex address, if not, check if it is a valid SDK
+	// denom
+	if err := ethermint.ValidateAddress(ttcp.Token); err != nil {
+		if err := sdk.ValidateDenom(ttcp.Token); err != nil {
+			return err
+		}
+	}
+
+	return govv1beta1.ValidateAbstract(ttcp)
+}
diff --git a/x/erc20/types/proposal_test.go b/x/erc20/types/proposal_test.go
new file mode 100644
index 00000000..ecb58936
--- /dev/null
+++ b/x/erc20/types/proposal_test.go
@@ -0,0 +1,279 @@
+package types
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/suite"
+
+	"github.com/evmos/ethermint/tests"
+
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
+)
+
+type ProposalTestSuite struct {
+	suite.Suite
+}
+
+func TestProposalTestSuite(t *testing.T) {
+	suite.Run(t, new(ProposalTestSuite))
+}
+
+func (suite *ProposalTestSuite) TestKeysTypes() {
+	suite.Require().Equal("erc20", (&RegisterCoinProposal{}).ProposalRoute())
+	suite.Require().Equal("RegisterCoin", (&RegisterCoinProposal{}).ProposalType())
+	suite.Require().Equal("erc20", (&RegisterERC20Proposal{}).ProposalRoute())
+	suite.Require().Equal("RegisterERC20", (&RegisterERC20Proposal{}).ProposalType())
+	suite.Require().Equal("erc20", (&ToggleTokenConversionProposal{}).ProposalRoute())
+	suite.Require().Equal("ToggleTokenConversion", (&ToggleTokenConversionProposal{}).ProposalType())
+}
+
+func (suite *ProposalTestSuite) TestCreateDenomDescription() {
+	testCases := []struct {
+		name      string
+		denom     string
+		expString string
+	}{
+		{
+			"with valid address",
+			"0xdac17f958d2ee523a2206206994597c13d831ec7",
+			"Cosmos coin token representation of 0xdac17f958d2ee523a2206206994597c13d831ec7",
+		},
+		{
+			"with empty string",
+			"",
+			"Cosmos coin token representation of ",
+		},
+	}
+	for _, tc := range testCases {
+		desc := CreateDenomDescription(tc.denom)
+		suite.Require().Equal(desc, tc.expString)
+	}
+}
+
+func (suite *ProposalTestSuite) TestCreateDenom() {
+	testCases := []struct {
+		name      string
+		denom     string
+		expString string
+	}{
+		{
+			"with valid address",
+			"0xdac17f958d2ee523a2206206994597c13d831ec7",
+			"erc20/0xdac17f958d2ee523a2206206994597c13d831ec7",
+		},
+		{
+			"with empty string",
+			"",
+			"erc20/",
+		},
+	}
+	for _, tc := range testCases {
+		desc := CreateDenom(tc.denom)
+		suite.Require().Equal(desc, tc.expString)
+	}
+}
+
+func (suite *ProposalTestSuite) TestValidateErc20Denom() {
+	testCases := []struct {
+		name    string
+		denom   string
+		expPass bool
+	}{
+		{
+			"- instead of /",
+			"erc20-0xdac17f958d2ee523a2206206994597c13d831ec7",
+			false,
+		},
+		{
+			"without /",
+			"conversionCoin",
+			false,
+		},
+		{
+			"// instead of /",
+			"erc20//0xdac17f958d2ee523a2206206994597c13d831ec7",
+			false,
+		},
+		{
+			"multiple /",
+			"erc20/0xdac17f958d2ee523a2206206994597c13d831ec7/test",
+			false,
+		},
+		{
+			"pass",
+			"erc20/0xdac17f958d2ee523a2206206994597c13d831ec7",
+			true,
+		},
+	}
+	for _, tc := range testCases {
+		err := ValidateErc20Denom(tc.denom)
+
+		if tc.expPass {
+			suite.Require().Nil(err, tc.name)
+		} else {
+			suite.Require().Error(err, tc.name)
+		}
+	}
+}
+
+func (suite *ProposalTestSuite) TestRegisterERC20Proposal() {
+	testCases := []struct {
+		msg         string
+		title       string
+		description string
+		pair        TokenPair
+		expectPass  bool
+	}{
+		// Valid tests
+		{msg: "Register token pair - valid pair enabled", title: "test", description: "test desc", pair: TokenPair{tests.GenerateAddress().String(), "test", true, OWNER_MODULE}, expectPass: true},
+		{msg: "Register token pair - valid pair dissabled", title: "test", description: "test desc", pair: TokenPair{tests.GenerateAddress().String(), "test", false, OWNER_MODULE}, expectPass: true},
+		// Missing params valid
+		{msg: "Register token pair - invalid missing title ", title: "", description: "test desc", pair: TokenPair{tests.GenerateAddress().String(), "test", false, OWNER_MODULE}, expectPass: false},
+		{msg: "Register token pair - invalid missing description ", title: "test", description: "", pair: TokenPair{tests.GenerateAddress().String(), "test", false, OWNER_MODULE}, expectPass: false},
+		// Invalid address
+		{msg: "Register token pair - invalid address (no hex)", title: "test", description: "test desc", pair: TokenPair{"0x5dCA2483280D9727c80b5518faC4556617fb19ZZ", "test", true, OWNER_MODULE}, expectPass: false},
+		{msg: "Register token pair - invalid address (invalid length 1)", title: "test", description: "test desc", pair: TokenPair{"0x5dCA2483280D9727c80b5518faC4556617fb19", "test", true, OWNER_MODULE}, expectPass: false},
+		{msg: "Register token pair - invalid address (invalid length 2)", title: "test", description: "test desc", pair: TokenPair{"0x5dCA2483280D9727c80b5518faC4556617fb194FFF", "test", true, OWNER_MODULE}, expectPass: false},
+		{msg: "Register token pair - invalid address (invalid prefix)", title: "test", description: "test desc", pair: TokenPair{"1x5dCA2483280D9727c80b5518faC4556617fb19F", "test", true, OWNER_MODULE}, expectPass: false},
+	}
+
+	for i, tc := range testCases {
+		tx := NewRegisterERC20Proposal(tc.title, tc.description, tc.pair.Erc20Address)
+		err := tx.ValidateBasic()
+
+		if tc.expectPass {
+			suite.Require().NoError(err, "valid test %d failed: %s, %v", i, tc.msg)
+		} else {
+			suite.Require().Error(err, "invalid test %d passed: %s, %v", i, tc.msg)
+		}
+	}
+}
+
+func createFullMetadata(denom, symbol, name string) banktypes.Metadata {
+	return banktypes.Metadata{
+		Description: "desc",
+		Base:        denom,
+		// NOTE: Denom units MUST be increasing
+		DenomUnits: []*banktypes.DenomUnit{
+			{
+				Denom:    denom,
+				Exponent: 0,
+			},
+			{
+				Denom:    symbol,
+				Exponent: uint32(18),
+			},
+		},
+		Name:    name,
+		Symbol:  symbol,
+		Display: denom,
+	}
+}
+
+func createMetadata(denom, symbol string) banktypes.Metadata {
+	return createFullMetadata(denom, symbol, denom)
+}
+
+func (suite *ProposalTestSuite) TestRegisterCoinProposal() {
+	validMetadata := banktypes.Metadata{
+		Description: "desc",
+		Base:        "coin",
+		// NOTE: Denom units MUST be increasing
+		DenomUnits: []*banktypes.DenomUnit{
+			{
+				Denom:    "coin",
+				Exponent: 0,
+			},
+			{
+				Denom:    "coin2",
+				Exponent: uint32(18),
+			},
+		},
+		Name:    "coin",
+		Symbol:  "token",
+		Display: "coin",
+	}
+
+	validIBCDenom := "ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2"
+	validIBCSymbol := "ATOM"
+	validIBCName := "Atom"
+
+	testCases := []struct {
+		msg         string
+		title       string
+		description string
+		metadata    banktypes.Metadata
+		expectPass  bool
+	}{
+		// Valid tests
+		{msg: "Register token pair - valid pair enabled", title: "test", description: "test desc", metadata: validMetadata, expectPass: true},
+		{msg: "Register token pair - valid pair dissabled", title: "test", description: "test desc", metadata: validMetadata, expectPass: true},
+
+		// Invalid Regex (denom)
+		{msg: "Register token pair - invalid starts with number", title: "test", description: "test desc", metadata: createMetadata("1test", "test"), expectPass: false},
+		{msg: "Register token pair - invalid char '('", title: "test", description: "test desc", metadata: createMetadata("(test", "test"), expectPass: false},
+		{msg: "Register token pair - invalid char '^'", title: "test", description: "test desc", metadata: createMetadata("^test", "test"), expectPass: false},
+		// Invalid length
+		{msg: "Register token pair - invalid length token (0)", title: "test", description: "test desc", metadata: createMetadata("", "test"), expectPass: false},
+		{msg: "Register token pair - invalid length token (1)", title: "test", description: "test desc", metadata: createMetadata("a", "test"), expectPass: false},
+		{msg: "Register token pair - invalid length token (128)", title: "test", description: "test desc", metadata: createMetadata(strings.Repeat("a", 129), "test"), expectPass: false},
+		{msg: "Register token pair - invalid length title (140)", title: strings.Repeat("a", govv1beta1.MaxTitleLength+1), description: "test desc", metadata: validMetadata, expectPass: false},
+		{msg: "Register token pair - invalid length description (5000)", title: "title", description: strings.Repeat("a", govv1beta1.MaxDescriptionLength+1), metadata: validMetadata, expectPass: false},
+
+		// Ibc
+		{msg: "Register token pair - ibc", title: "test", description: "test desc", metadata: createFullMetadata(validIBCDenom, validIBCSymbol, validIBCName), expectPass: true},
+		{msg: "Register token pair - ibc invalid denom", title: "test", description: "test desc", metadata: createFullMetadata("ibc/", validIBCSymbol, validIBCName), expectPass: false},
+	}
+
+	for i, tc := range testCases {
+		tx := NewRegisterCoinProposal(tc.title, tc.description, tc.metadata)
+		err := tx.ValidateBasic()
+
+		if tc.expectPass {
+			suite.Require().NoError(err, "valid test %d failed: %s, %v", i, tc.msg)
+		} else {
+			suite.Require().Error(err, "invalid test %d passed: %s, %v", i, tc.msg)
+		}
+	}
+}
+
+func (suite *ProposalTestSuite) TestToggleTokenConversionProposal() {
+	testCases := []struct {
+		msg         string
+		title       string
+		description string
+		token       string
+		expectPass  bool
+	}{
+		{msg: "Enable token conversion proposal - valid denom", title: "test", description: "test desc", token: "test", expectPass: true},
+		{msg: "Enable token conversion proposal - valid address", title: "test", description: "test desc", token: "0x5dCA2483280D9727c80b5518faC4556617fb194F", expectPass: true},
+		{msg: "Enable token conversion proposal - invalid address", title: "test", description: "test desc", token: "0x123", expectPass: false},
+
+		// Invalid missing params
+		{msg: "Enable token conversion proposal - valid missing title", title: "", description: "test desc", token: "test", expectPass: false},
+		{msg: "Enable token conversion proposal - valid missing description", title: "test", description: "", token: "test", expectPass: false},
+		{msg: "Enable token conversion proposal - invalid missing token", title: "test", description: "test desc", token: "", expectPass: false},
+
+		// Invalid regex
+		{msg: "Enable token conversion proposal - invalid denom", title: "test", description: "test desc", token: "^test", expectPass: false},
+		// Invalid length
+		{msg: "Enable token conversion proposal - invalid length (1)", title: "test", description: "test desc", token: "a", expectPass: false},
+		{msg: "Enable token conversion proposal - invalid length (128)", title: "test", description: "test desc", token: strings.Repeat("a", 129), expectPass: false},
+
+		{msg: "Enable token conversion proposal - invalid length title (140)", title: strings.Repeat("a", govv1beta1.MaxTitleLength+1), description: "test desc", token: "test", expectPass: false},
+		{msg: "Enable token conversion proposal - invalid length description (5000)", title: "title", description: strings.Repeat("a", govv1beta1.MaxDescriptionLength+1), token: "test", expectPass: false},
+	}
+
+	for i, tc := range testCases {
+		tx := NewToggleTokenConversionProposal(tc.title, tc.description, tc.token)
+		err := tx.ValidateBasic()
+
+		if tc.expectPass {
+			suite.Require().NoError(err, "valid test %d failed: %s, %v", i, tc.msg)
+		} else {
+			suite.Require().Error(err, "invalid test %d passed: %s, %v", i, tc.msg)
+		}
+	}
+}
diff --git a/x/erc20/types/query.pb.go b/x/erc20/types/query.pb.go
new file mode 100644
index 00000000..8ecfcfc1
--- /dev/null
+++ b/x/erc20/types/query.pb.go
@@ -0,0 +1,1399 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: canto/erc20/v1/query.proto
+
+package types
+
+import (
+	context "context"
+	fmt "fmt"
+	query "github.com/cosmos/cosmos-sdk/types/query"
+	_ "github.com/gogo/protobuf/gogoproto"
+	grpc1 "github.com/gogo/protobuf/grpc"
+	proto "github.com/gogo/protobuf/proto"
+	_ "google.golang.org/genproto/googleapis/api/annotations"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+	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
+
+// QueryTokenPairsRequest is the request type for the Query/TokenPairs RPC
+// method.
+type QueryTokenPairsRequest struct {
+	// pagination defines an optional pagination for the request.
+	Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"`
+}
+
+func (m *QueryTokenPairsRequest) Reset()         { *m = QueryTokenPairsRequest{} }
+func (m *QueryTokenPairsRequest) String() string { return proto.CompactTextString(m) }
+func (*QueryTokenPairsRequest) ProtoMessage()    {}
+func (*QueryTokenPairsRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_a1d7327008f799c8, []int{0}
+}
+func (m *QueryTokenPairsRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryTokenPairsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryTokenPairsRequest.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 *QueryTokenPairsRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryTokenPairsRequest.Merge(m, src)
+}
+func (m *QueryTokenPairsRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryTokenPairsRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryTokenPairsRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryTokenPairsRequest proto.InternalMessageInfo
+
+func (m *QueryTokenPairsRequest) GetPagination() *query.PageRequest {
+	if m != nil {
+		return m.Pagination
+	}
+	return nil
+}
+
+// QueryTokenPairsResponse is the response type for the Query/TokenPairs RPC
+// method.
+type QueryTokenPairsResponse struct {
+	TokenPairs []TokenPair `protobuf:"bytes,1,rep,name=token_pairs,json=tokenPairs,proto3" json:"token_pairs"`
+	// pagination defines the pagination in the response.
+	Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"`
+}
+
+func (m *QueryTokenPairsResponse) Reset()         { *m = QueryTokenPairsResponse{} }
+func (m *QueryTokenPairsResponse) String() string { return proto.CompactTextString(m) }
+func (*QueryTokenPairsResponse) ProtoMessage()    {}
+func (*QueryTokenPairsResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_a1d7327008f799c8, []int{1}
+}
+func (m *QueryTokenPairsResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryTokenPairsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryTokenPairsResponse.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 *QueryTokenPairsResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryTokenPairsResponse.Merge(m, src)
+}
+func (m *QueryTokenPairsResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryTokenPairsResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryTokenPairsResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryTokenPairsResponse proto.InternalMessageInfo
+
+func (m *QueryTokenPairsResponse) GetTokenPairs() []TokenPair {
+	if m != nil {
+		return m.TokenPairs
+	}
+	return nil
+}
+
+func (m *QueryTokenPairsResponse) GetPagination() *query.PageResponse {
+	if m != nil {
+		return m.Pagination
+	}
+	return nil
+}
+
+// QueryTokenPairRequest is the request type for the Query/TokenPair RPC method.
+type QueryTokenPairRequest struct {
+	// token identifier can be either the hex contract address of the ERC20 or the
+	// Cosmos base denomination
+	Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
+}
+
+func (m *QueryTokenPairRequest) Reset()         { *m = QueryTokenPairRequest{} }
+func (m *QueryTokenPairRequest) String() string { return proto.CompactTextString(m) }
+func (*QueryTokenPairRequest) ProtoMessage()    {}
+func (*QueryTokenPairRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_a1d7327008f799c8, []int{2}
+}
+func (m *QueryTokenPairRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryTokenPairRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryTokenPairRequest.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 *QueryTokenPairRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryTokenPairRequest.Merge(m, src)
+}
+func (m *QueryTokenPairRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryTokenPairRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryTokenPairRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryTokenPairRequest proto.InternalMessageInfo
+
+func (m *QueryTokenPairRequest) GetToken() string {
+	if m != nil {
+		return m.Token
+	}
+	return ""
+}
+
+// QueryTokenPairResponse is the response type for the Query/TokenPair RPC
+// method.
+type QueryTokenPairResponse struct {
+	TokenPair TokenPair `protobuf:"bytes,1,opt,name=token_pair,json=tokenPair,proto3" json:"token_pair"`
+}
+
+func (m *QueryTokenPairResponse) Reset()         { *m = QueryTokenPairResponse{} }
+func (m *QueryTokenPairResponse) String() string { return proto.CompactTextString(m) }
+func (*QueryTokenPairResponse) ProtoMessage()    {}
+func (*QueryTokenPairResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_a1d7327008f799c8, []int{3}
+}
+func (m *QueryTokenPairResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryTokenPairResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryTokenPairResponse.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 *QueryTokenPairResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryTokenPairResponse.Merge(m, src)
+}
+func (m *QueryTokenPairResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryTokenPairResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryTokenPairResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryTokenPairResponse proto.InternalMessageInfo
+
+func (m *QueryTokenPairResponse) GetTokenPair() TokenPair {
+	if m != nil {
+		return m.TokenPair
+	}
+	return TokenPair{}
+}
+
+// QueryParamsRequest is the request type for the Query/Params RPC method.
+type QueryParamsRequest struct {
+}
+
+func (m *QueryParamsRequest) Reset()         { *m = QueryParamsRequest{} }
+func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) }
+func (*QueryParamsRequest) ProtoMessage()    {}
+func (*QueryParamsRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_a1d7327008f799c8, []int{4}
+}
+func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryParamsRequest.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 *QueryParamsRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryParamsRequest.Merge(m, src)
+}
+func (m *QueryParamsRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryParamsRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo
+
+// QueryParamsResponse is the response type for the Query/Params RPC
+// method.
+type QueryParamsResponse struct {
+	Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"`
+}
+
+func (m *QueryParamsResponse) Reset()         { *m = QueryParamsResponse{} }
+func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) }
+func (*QueryParamsResponse) ProtoMessage()    {}
+func (*QueryParamsResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_a1d7327008f799c8, []int{5}
+}
+func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryParamsResponse.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 *QueryParamsResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryParamsResponse.Merge(m, src)
+}
+func (m *QueryParamsResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryParamsResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo
+
+func (m *QueryParamsResponse) GetParams() Params {
+	if m != nil {
+		return m.Params
+	}
+	return Params{}
+}
+
+func init() {
+	proto.RegisterType((*QueryTokenPairsRequest)(nil), "canto.erc20.v1.QueryTokenPairsRequest")
+	proto.RegisterType((*QueryTokenPairsResponse)(nil), "canto.erc20.v1.QueryTokenPairsResponse")
+	proto.RegisterType((*QueryTokenPairRequest)(nil), "canto.erc20.v1.QueryTokenPairRequest")
+	proto.RegisterType((*QueryTokenPairResponse)(nil), "canto.erc20.v1.QueryTokenPairResponse")
+	proto.RegisterType((*QueryParamsRequest)(nil), "canto.erc20.v1.QueryParamsRequest")
+	proto.RegisterType((*QueryParamsResponse)(nil), "canto.erc20.v1.QueryParamsResponse")
+}
+
+func init() { proto.RegisterFile("canto/erc20/v1/query.proto", fileDescriptor_a1d7327008f799c8) }
+
+var fileDescriptor_a1d7327008f799c8 = []byte{
+	// 511 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x53, 0x4f, 0x6b, 0x13, 0x41,
+	0x14, 0xcf, 0xb4, 0x36, 0x90, 0x17, 0xf0, 0x30, 0xc6, 0x18, 0x57, 0x5d, 0xcb, 0x86, 0xa6, 0x45,
+	0xe9, 0x8c, 0x59, 0x3d, 0x8b, 0x54, 0x50, 0x44, 0x90, 0x18, 0x3c, 0x88, 0x17, 0x9d, 0x84, 0x61,
+	0x5d, 0x6a, 0x66, 0x36, 0x3b, 0x93, 0x68, 0x11, 0x2f, 0xbd, 0x88, 0x37, 0xc1, 0xaf, 0xe0, 0x87,
+	0xe9, 0xb1, 0xe0, 0xc5, 0x93, 0x48, 0xe2, 0x07, 0x91, 0x9d, 0x99, 0x6c, 0xba, 0xdb, 0x92, 0xdc,
+	0x76, 0xde, 0xbc, 0xdf, 0xbf, 0xf7, 0x66, 0xc1, 0x1b, 0x32, 0xa1, 0x25, 0xe5, 0xe9, 0x30, 0xbc,
+	0x47, 0xa7, 0x5d, 0x3a, 0x9e, 0xf0, 0xf4, 0x88, 0x24, 0xa9, 0xd4, 0x12, 0x5f, 0x36, 0x77, 0xc4,
+	0xdc, 0x91, 0x69, 0xd7, 0xbb, 0x33, 0x94, 0x6a, 0x24, 0x15, 0x1d, 0x30, 0xc5, 0x6d, 0x23, 0x9d,
+	0x76, 0x07, 0x5c, 0xb3, 0x2e, 0x4d, 0x58, 0x14, 0x0b, 0xa6, 0x63, 0x29, 0x2c, 0xd6, 0xbb, 0x59,
+	0xe2, 0x8d, 0xb8, 0xe0, 0x2a, 0x56, 0xee, 0xb6, 0xac, 0x6a, 0x25, 0x1c, 0x32, 0x92, 0x32, 0xfa,
+	0xc0, 0x29, 0x4b, 0x62, 0xca, 0x84, 0x90, 0xda, 0xd0, 0x2e, 0x90, 0x8d, 0x48, 0x46, 0xd2, 0x7c,
+	0xd2, 0xec, 0xcb, 0x56, 0x83, 0x77, 0xd0, 0x7c, 0x99, 0xf9, 0x79, 0x25, 0x0f, 0xb9, 0xe8, 0xb1,
+	0x38, 0x55, 0x7d, 0x3e, 0x9e, 0x70, 0xa5, 0xf1, 0x13, 0x80, 0xa5, 0xb7, 0x16, 0xda, 0x46, 0x7b,
+	0xf5, 0xb0, 0x43, 0x6c, 0x10, 0x92, 0x05, 0x21, 0x36, 0xb1, 0x0b, 0x42, 0x7a, 0x2c, 0xe2, 0x0e,
+	0xdb, 0x3f, 0x83, 0x0c, 0x7e, 0x22, 0xb8, 0x76, 0x4e, 0x42, 0x25, 0x52, 0x28, 0x8e, 0x1f, 0x41,
+	0x5d, 0x67, 0xd5, 0xb7, 0x49, 0x56, 0x6e, 0xa1, 0xed, 0xcd, 0xbd, 0x7a, 0x78, 0x9d, 0x14, 0xa7,
+	0x47, 0x72, 0xe0, 0xc1, 0xa5, 0x93, 0x3f, 0xb7, 0x2b, 0x7d, 0xd0, 0x39, 0x13, 0x7e, 0x5a, 0x70,
+	0xb9, 0x61, 0x5c, 0xee, 0xae, 0x75, 0x69, 0xe5, 0x0b, 0x36, 0xf7, 0xe1, 0x6a, 0xd1, 0xe5, 0x62,
+	0x0e, 0x0d, 0xd8, 0x32, 0x7a, 0x66, 0x04, 0xb5, 0xbe, 0x3d, 0x04, 0xaf, 0xcb, 0x73, 0xcb, 0x33,
+	0x3d, 0x04, 0x58, 0x66, 0x72, 0x73, 0x5b, 0x1b, 0xa9, 0x96, 0x47, 0x0a, 0x1a, 0x80, 0x0d, 0x73,
+	0x8f, 0xa5, 0x6c, 0xb4, 0xd8, 0x46, 0xf0, 0x1c, 0xae, 0x14, 0xaa, 0x4e, 0xec, 0x01, 0x54, 0x13,
+	0x53, 0x71, 0x42, 0xcd, 0xb2, 0x90, 0xed, 0x77, 0x2a, 0xae, 0x37, 0xfc, 0xb6, 0x09, 0x5b, 0x86,
+	0x0d, 0x1f, 0x23, 0x80, 0xe5, 0x5e, 0x70, 0xa7, 0x0c, 0xbf, 0xf8, 0x6d, 0x78, 0xbb, 0x6b, 0xfb,
+	0xac, 0xbf, 0xa0, 0x7d, 0xfc, 0xeb, 0xdf, 0x8f, 0x8d, 0x5b, 0xf8, 0x06, 0x2d, 0xbd, 0xdb, 0x33,
+	0x6b, 0xc7, 0x5f, 0x11, 0xd4, 0x72, 0x2c, 0xde, 0x59, 0xcd, 0xbd, 0xb0, 0xd0, 0x59, 0xd7, 0xe6,
+	0x1c, 0xdc, 0x35, 0x0e, 0x76, 0x70, 0x7b, 0x85, 0x03, 0xfa, 0xd9, 0x1c, 0xbe, 0xe0, 0x31, 0x54,
+	0xed, 0xc0, 0x70, 0x70, 0x21, 0x7d, 0x61, 0x27, 0x5e, 0x7b, 0x65, 0x8f, 0xd3, 0xf7, 0x8d, 0x7e,
+	0x0b, 0x37, 0xcb, 0xfa, 0x76, 0x17, 0x07, 0xcf, 0x4e, 0x66, 0x3e, 0x3a, 0x9d, 0xf9, 0xe8, 0xef,
+	0xcc, 0x47, 0xdf, 0xe7, 0x7e, 0xe5, 0x74, 0xee, 0x57, 0x7e, 0xcf, 0xfd, 0xca, 0x1b, 0x1a, 0xc5,
+	0xfa, 0xfd, 0x64, 0x40, 0x86, 0x72, 0x44, 0x1f, 0x67, 0xd8, 0xfd, 0x17, 0x5c, 0x7f, 0x94, 0xe9,
+	0xa1, 0x3d, 0xd1, 0x69, 0x48, 0x3f, 0x39, 0x3a, 0x7d, 0x94, 0x70, 0x35, 0xa8, 0x9a, 0x5f, 0xfa,
+	0xfe, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xca, 0x31, 0xa5, 0x0a, 0x9a, 0x04, 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// QueryClient is the client API for Query service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type QueryClient interface {
+	// TokenPairs retrieves registered token pairs
+	TokenPairs(ctx context.Context, in *QueryTokenPairsRequest, opts ...grpc.CallOption) (*QueryTokenPairsResponse, error)
+	// TokenPair retrieves a registered token pair
+	TokenPair(ctx context.Context, in *QueryTokenPairRequest, opts ...grpc.CallOption) (*QueryTokenPairResponse, error)
+	// Params retrieves the erc20 module params
+	Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error)
+}
+
+type queryClient struct {
+	cc grpc1.ClientConn
+}
+
+func NewQueryClient(cc grpc1.ClientConn) QueryClient {
+	return &queryClient{cc}
+}
+
+func (c *queryClient) TokenPairs(ctx context.Context, in *QueryTokenPairsRequest, opts ...grpc.CallOption) (*QueryTokenPairsResponse, error) {
+	out := new(QueryTokenPairsResponse)
+	err := c.cc.Invoke(ctx, "/canto.erc20.v1.Query/TokenPairs", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *queryClient) TokenPair(ctx context.Context, in *QueryTokenPairRequest, opts ...grpc.CallOption) (*QueryTokenPairResponse, error) {
+	out := new(QueryTokenPairResponse)
+	err := c.cc.Invoke(ctx, "/canto.erc20.v1.Query/TokenPair", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) {
+	out := new(QueryParamsResponse)
+	err := c.cc.Invoke(ctx, "/canto.erc20.v1.Query/Params", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// QueryServer is the server API for Query service.
+type QueryServer interface {
+	// TokenPairs retrieves registered token pairs
+	TokenPairs(context.Context, *QueryTokenPairsRequest) (*QueryTokenPairsResponse, error)
+	// TokenPair retrieves a registered token pair
+	TokenPair(context.Context, *QueryTokenPairRequest) (*QueryTokenPairResponse, error)
+	// Params retrieves the erc20 module params
+	Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error)
+}
+
+// UnimplementedQueryServer can be embedded to have forward compatible implementations.
+type UnimplementedQueryServer struct {
+}
+
+func (*UnimplementedQueryServer) TokenPairs(ctx context.Context, req *QueryTokenPairsRequest) (*QueryTokenPairsResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method TokenPairs not implemented")
+}
+func (*UnimplementedQueryServer) TokenPair(ctx context.Context, req *QueryTokenPairRequest) (*QueryTokenPairResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method TokenPair not implemented")
+}
+func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Params not implemented")
+}
+
+func RegisterQueryServer(s grpc1.Server, srv QueryServer) {
+	s.RegisterService(&_Query_serviceDesc, srv)
+}
+
+func _Query_TokenPairs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(QueryTokenPairsRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(QueryServer).TokenPairs(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/canto.erc20.v1.Query/TokenPairs",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(QueryServer).TokenPairs(ctx, req.(*QueryTokenPairsRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Query_TokenPair_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(QueryTokenPairRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(QueryServer).TokenPair(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/canto.erc20.v1.Query/TokenPair",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(QueryServer).TokenPair(ctx, req.(*QueryTokenPairRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(QueryParamsRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(QueryServer).Params(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/canto.erc20.v1.Query/Params",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+var _Query_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "canto.erc20.v1.Query",
+	HandlerType: (*QueryServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "TokenPairs",
+			Handler:    _Query_TokenPairs_Handler,
+		},
+		{
+			MethodName: "TokenPair",
+			Handler:    _Query_TokenPair_Handler,
+		},
+		{
+			MethodName: "Params",
+			Handler:    _Query_Params_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "canto/erc20/v1/query.proto",
+}
+
+func (m *QueryTokenPairsRequest) 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 *QueryTokenPairsRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryTokenPairsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.Pagination != nil {
+		{
+			size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintQuery(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryTokenPairsResponse) 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 *QueryTokenPairsResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryTokenPairsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.Pagination != nil {
+		{
+			size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintQuery(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.TokenPairs) > 0 {
+		for iNdEx := len(m.TokenPairs) - 1; iNdEx >= 0; iNdEx-- {
+			{
+				size, err := m.TokenPairs[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintQuery(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0xa
+		}
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryTokenPairRequest) 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 *QueryTokenPairRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryTokenPairRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Token) > 0 {
+		i -= len(m.Token)
+		copy(dAtA[i:], m.Token)
+		i = encodeVarintQuery(dAtA, i, uint64(len(m.Token)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryTokenPairResponse) 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 *QueryTokenPairResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryTokenPairResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size, err := m.TokenPair.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintQuery(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0xa
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryParamsRequest) 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 *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryParamsResponse) 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 *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryParamsResponse) 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 = encodeVarintQuery(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0xa
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintQuery(dAtA []byte, offset int, v uint64) int {
+	offset -= sovQuery(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *QueryTokenPairsRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Pagination != nil {
+		l = m.Pagination.Size()
+		n += 1 + l + sovQuery(uint64(l))
+	}
+	return n
+}
+
+func (m *QueryTokenPairsResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if len(m.TokenPairs) > 0 {
+		for _, e := range m.TokenPairs {
+			l = e.Size()
+			n += 1 + l + sovQuery(uint64(l))
+		}
+	}
+	if m.Pagination != nil {
+		l = m.Pagination.Size()
+		n += 1 + l + sovQuery(uint64(l))
+	}
+	return n
+}
+
+func (m *QueryTokenPairRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Token)
+	if l > 0 {
+		n += 1 + l + sovQuery(uint64(l))
+	}
+	return n
+}
+
+func (m *QueryTokenPairResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = m.TokenPair.Size()
+	n += 1 + l + sovQuery(uint64(l))
+	return n
+}
+
+func (m *QueryParamsRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *QueryParamsResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = m.Params.Size()
+	n += 1 + l + sovQuery(uint64(l))
+	return n
+}
+
+func sovQuery(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozQuery(x uint64) (n int) {
+	return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *QueryTokenPairsRequest) 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: QueryTokenPairsRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryTokenPairsRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.Pagination == nil {
+				m.Pagination = &query.PageRequest{}
+			}
+			if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			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 *QueryTokenPairsResponse) 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: QueryTokenPairsResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryTokenPairsResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field TokenPairs", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.TokenPairs = append(m.TokenPairs, TokenPair{})
+			if err := m.TokenPairs[len(m.TokenPairs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.Pagination == nil {
+				m.Pagination = &query.PageResponse{}
+			}
+			if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			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 *QueryTokenPairRequest) 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: QueryTokenPairRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryTokenPairRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Token", 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.Token = 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 *QueryTokenPairResponse) 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: QueryTokenPairResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryTokenPairResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field TokenPair", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.TokenPair.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			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 *QueryParamsRequest) 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: QueryParamsRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		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 *QueryParamsResponse) 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: QueryParamsResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			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 skipQuery(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, ErrIntOverflowQuery
+			}
+			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, ErrIntOverflowQuery
+				}
+				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, ErrIntOverflowQuery
+				}
+				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, ErrInvalidLengthQuery
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupQuery
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthQuery
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthQuery        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowQuery          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/x/erc20/types/query.pb.gw.go b/x/erc20/types/query.pb.gw.go
new file mode 100644
index 00000000..586ccbf0
--- /dev/null
+++ b/x/erc20/types/query.pb.gw.go
@@ -0,0 +1,337 @@
+// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
+// source: canto/erc20/v1/query.proto
+
+/*
+Package types is a reverse proxy.
+
+It translates gRPC into RESTful JSON APIs.
+*/
+package types
+
+import (
+	"context"
+	"io"
+	"net/http"
+
+	"github.com/golang/protobuf/descriptor"
+	"github.com/golang/protobuf/proto"
+	"github.com/grpc-ecosystem/grpc-gateway/runtime"
+	"github.com/grpc-ecosystem/grpc-gateway/utilities"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/grpclog"
+	"google.golang.org/grpc/metadata"
+	"google.golang.org/grpc/status"
+)
+
+// Suppress "imported and not used" errors
+var _ codes.Code
+var _ io.Reader
+var _ status.Status
+var _ = runtime.String
+var _ = utilities.NewDoubleArray
+var _ = descriptor.ForMessage
+var _ = metadata.Join
+
+var (
+	filter_Query_TokenPairs_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_Query_TokenPairs_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryTokenPairsRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_TokenPairs_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.TokenPairs(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Query_TokenPairs_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryTokenPairsRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_TokenPairs_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.TokenPairs(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+func request_Query_TokenPair_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryTokenPairRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["token"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "token")
+	}
+
+	protoReq.Token, err = runtime.String(val)
+
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "token", err)
+	}
+
+	msg, err := client.TokenPair(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Query_TokenPair_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryTokenPairRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["token"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "token")
+	}
+
+	protoReq.Token, err = runtime.String(val)
+
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "token", err)
+	}
+
+	msg, err := server.TokenPair(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryParamsRequest
+	var metadata runtime.ServerMetadata
+
+	msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryParamsRequest
+	var metadata runtime.ServerMetadata
+
+	msg, err := server.Params(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.
+// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead.
+func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error {
+
+	mux.Handle("GET", pattern_Query_TokenPairs_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_TokenPairs_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_TokenPairs_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Query_TokenPair_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_TokenPair_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_TokenPair_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Query_Params_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_Params_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_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	return nil
+}
+
+// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but
+// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
+func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
+	conn, err := grpc.Dial(endpoint, opts...)
+	if err != nil {
+		return err
+	}
+	defer func() {
+		if err != nil {
+			if cerr := conn.Close(); cerr != nil {
+				grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+			}
+			return
+		}
+		go func() {
+			<-ctx.Done()
+			if cerr := conn.Close(); cerr != nil {
+				grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+			}
+		}()
+	}()
+
+	return RegisterQueryHandler(ctx, mux, conn)
+}
+
+// RegisterQueryHandler registers the http handlers for service Query to "mux".
+// The handlers forward requests to the grpc endpoint over "conn".
+func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+	return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn))
+}
+
+// RegisterQueryHandlerClient registers the http handlers for service Query
+// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "QueryClient" to call the correct interceptors.
+func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error {
+
+	mux.Handle("GET", pattern_Query_TokenPairs_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_TokenPairs_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_TokenPairs_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Query_TokenPair_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_TokenPair_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_TokenPair_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Query_Params_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_Params_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_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	return nil
+}
+
+var (
+	pattern_Query_TokenPairs_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"canto", "erc20", "v1", "token_pairs"}, "", runtime.AssumeColonVerbOpt(true)))
+
+	pattern_Query_TokenPair_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"canto", "erc20", "v1", "token_pairs", "token"}, "", runtime.AssumeColonVerbOpt(true)))
+
+	pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"canto", "erc20", "v1", "params"}, "", runtime.AssumeColonVerbOpt(true)))
+)
+
+var (
+	forward_Query_TokenPairs_0 = runtime.ForwardResponseMessage
+
+	forward_Query_TokenPair_0 = runtime.ForwardResponseMessage
+
+	forward_Query_Params_0 = runtime.ForwardResponseMessage
+)
diff --git a/x/erc20/types/token_pair.go b/x/erc20/types/token_pair.go
new file mode 100644
index 00000000..2fcb5fe3
--- /dev/null
+++ b/x/erc20/types/token_pair.go
@@ -0,0 +1,54 @@
+package types
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/ethereum/go-ethereum/common"
+	ethermint "github.com/evmos/ethermint/types"
+	"github.com/tendermint/tendermint/crypto/tmhash"
+)
+
+// NewTokenPair returns an instance of TokenPair
+func NewTokenPair(erc20Address common.Address, denom string, enabled bool, contractOwner Owner) TokenPair {
+	return TokenPair{
+		Erc20Address:  erc20Address.String(),
+		Denom:         denom,
+		Enabled:       true,
+		ContractOwner: contractOwner,
+	}
+}
+
+// GetID returns the SHA256 hash of the ERC20 address and denomination
+func (tp TokenPair) GetID() []byte {
+	id := tp.Erc20Address + "|" + tp.Denom
+	return tmhash.Sum([]byte(id))
+}
+
+// GetErc20Contract casts the hex string address of the ERC20 to common.Address
+func (tp TokenPair) GetERC20Contract() common.Address {
+	return common.HexToAddress(tp.Erc20Address)
+}
+
+// Validate performs a stateless validation of a TokenPair
+func (tp TokenPair) Validate() error {
+	if err := sdk.ValidateDenom(tp.Denom); err != nil {
+		return err
+	}
+
+	if err := ethermint.ValidateAddress(tp.Erc20Address); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// IsNativeCoin returns true if the owner of the ERC20 contract is the
+// erc20 module account
+func (tp TokenPair) IsNativeCoin() bool {
+	return tp.ContractOwner == OWNER_MODULE
+}
+
+// IsNativeERC20 returns true if the owner of the ERC20 contract not the
+// erc20 module account
+func (tp TokenPair) IsNativeERC20() bool {
+	return tp.ContractOwner == OWNER_EXTERNAL
+}
diff --git a/x/erc20/types/token_pair_test.go b/x/erc20/types/token_pair_test.go
new file mode 100644
index 00000000..42358196
--- /dev/null
+++ b/x/erc20/types/token_pair_test.go
@@ -0,0 +1,159 @@
+package types
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/stretchr/testify/suite"
+	"github.com/tendermint/tendermint/crypto/tmhash"
+
+	"github.com/evmos/ethermint/tests"
+)
+
+type TokenPairTestSuite struct {
+	suite.Suite
+}
+
+func TestTokenPairSuite(t *testing.T) {
+	suite.Run(t, new(TokenPairTestSuite))
+}
+
+func (suite *TokenPairTestSuite) TestTokenPairNew() {
+	testCases := []struct {
+		msg          string
+		erc20Address common.Address
+		denom        string
+		enabled      bool
+		owner        Owner
+		expectPass   bool
+	}{
+		{msg: "Register token pair - invalid starts with number", erc20Address: tests.GenerateAddress(), denom: "1test", enabled: true, owner: OWNER_MODULE, expectPass: false},
+		{msg: "Register token pair - invalid char '('", erc20Address: tests.GenerateAddress(), denom: "(test", enabled: true, owner: OWNER_MODULE, expectPass: false},
+		{msg: "Register token pair - invalid char '^'", erc20Address: tests.GenerateAddress(), denom: "^test", enabled: true, owner: OWNER_MODULE, expectPass: false},
+		// TODO: (guille) should the "\" be allowed to support unicode names?
+		{msg: "Register token pair - invalid char '\\'", erc20Address: tests.GenerateAddress(), denom: "-test", enabled: true, owner: OWNER_MODULE, expectPass: false},
+		// Invalid length
+		{msg: "Register token pair - invalid length token (0)", erc20Address: tests.GenerateAddress(), denom: "", enabled: true, owner: OWNER_MODULE, expectPass: false},
+		{msg: "Register token pair - invalid length token (1)", erc20Address: tests.GenerateAddress(), denom: "a", enabled: true, owner: OWNER_MODULE, expectPass: false},
+		{msg: "Register token pair - invalid length token (128)", erc20Address: tests.GenerateAddress(), denom: strings.Repeat("a", 129), enabled: true, owner: OWNER_MODULE, expectPass: false},
+		{msg: "Register token pair - pass", erc20Address: tests.GenerateAddress(), denom: "test", enabled: true, owner: OWNER_MODULE, expectPass: true},
+	}
+
+	for i, tc := range testCases {
+		tp := NewTokenPair(tc.erc20Address, tc.denom, tc.enabled, tc.owner)
+		err := tp.Validate()
+
+		if tc.expectPass {
+			suite.Require().NoError(err, "valid test %d failed: %s, %v", i, tc.msg)
+		} else {
+			suite.Require().Error(err, "invalid test %d passed: %s, %v", i, tc.msg)
+		}
+	}
+}
+
+func (suite *TokenPairTestSuite) TestTokenPair() {
+	testCases := []struct {
+		msg        string
+		pair       TokenPair
+		expectPass bool
+	}{
+		{msg: "Register token pair - invalid address (no hex)", pair: TokenPair{"0x5dCA2483280D9727c80b5518faC4556617fb19ZZ", "test", true, OWNER_MODULE}, expectPass: false},
+		{msg: "Register token pair - invalid address (invalid length 1)", pair: TokenPair{"0x5dCA2483280D9727c80b5518faC4556617fb19", "test", true, OWNER_MODULE}, expectPass: false},
+		{msg: "Register token pair - invalid address (invalid length 2)", pair: TokenPair{"0x5dCA2483280D9727c80b5518faC4556617fb194FFF", "test", true, OWNER_MODULE}, expectPass: false},
+		{msg: "pass", pair: TokenPair{tests.GenerateAddress().String(), "test", true, OWNER_MODULE}, expectPass: true},
+	}
+
+	for i, tc := range testCases {
+		err := tc.pair.Validate()
+
+		if tc.expectPass {
+			suite.Require().NoError(err, "valid test %d failed: %s, %v", i, tc.msg)
+		} else {
+			suite.Require().Error(err, "invalid test %d passed: %s, %v", i, tc.msg)
+		}
+	}
+}
+
+func (suite *TokenPairTestSuite) TestGetID() {
+	addr := tests.GenerateAddress()
+	denom := "test"
+	pair := NewTokenPair(addr, denom, true, OWNER_MODULE)
+	id := pair.GetID()
+	expID := tmhash.Sum([]byte(addr.String() + "|" + denom))
+	suite.Require().Equal(expID, id)
+}
+
+func (suite *TokenPairTestSuite) TestGetERC20Contract() {
+	expAddr := tests.GenerateAddress()
+	denom := "test"
+	pair := NewTokenPair(expAddr, denom, true, OWNER_MODULE)
+	addr := pair.GetERC20Contract()
+	suite.Require().Equal(expAddr, addr)
+}
+
+func (suite *TokenPairTestSuite) TestIsNativeCoin() {
+	testCases := []struct {
+		name       string
+		pair       TokenPair
+		expectPass bool
+	}{
+		{
+			"no owner",
+			TokenPair{tests.GenerateAddress().String(), "test", true, OWNER_UNSPECIFIED},
+			false,
+		},
+		{
+			"external ERC20 owner",
+			TokenPair{tests.GenerateAddress().String(), "test", true, OWNER_EXTERNAL},
+			false,
+		},
+		{
+			"pass",
+			TokenPair{tests.GenerateAddress().String(), "test", true, OWNER_MODULE},
+			true,
+		},
+	}
+
+	for _, tc := range testCases {
+		res := tc.pair.IsNativeCoin()
+		if tc.expectPass {
+			suite.Require().True(res, tc.name)
+		} else {
+			suite.Require().False(res, tc.name)
+		}
+	}
+}
+
+func (suite *TokenPairTestSuite) TestIsNativeERC20() {
+	testCases := []struct {
+		name       string
+		pair       TokenPair
+		expectPass bool
+	}{
+		{
+			"no owner",
+			TokenPair{tests.GenerateAddress().String(), "test", true, OWNER_UNSPECIFIED},
+			false,
+		},
+		{
+			"module owner",
+			TokenPair{tests.GenerateAddress().String(), "test", true, OWNER_MODULE},
+			false,
+		},
+		{
+			"pass",
+			TokenPair{tests.GenerateAddress().String(), "test", true, OWNER_EXTERNAL},
+			true,
+		},
+	}
+
+	for _, tc := range testCases {
+		res := tc.pair.IsNativeERC20()
+		if tc.expectPass {
+			suite.Require().True(res, tc.name)
+		} else {
+			suite.Require().False(res, tc.name)
+		}
+	}
+}
diff --git a/x/erc20/types/tx.pb.go b/x/erc20/types/tx.pb.go
new file mode 100644
index 00000000..6e410e2b
--- /dev/null
+++ b/x/erc20/types/tx.pb.go
@@ -0,0 +1,1140 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: canto/erc20/v1/tx.proto
+
+package types
+
+import (
+	context "context"
+	fmt "fmt"
+	github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types"
+	types "github.com/cosmos/cosmos-sdk/types"
+	_ "github.com/gogo/protobuf/gogoproto"
+	grpc1 "github.com/gogo/protobuf/grpc"
+	proto "github.com/gogo/protobuf/proto"
+	_ "google.golang.org/genproto/googleapis/api/annotations"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+	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
+
+// MsgConvertCoin defines a Msg to convert a native Cosmos coin to a ERC20 token
+type MsgConvertCoin struct {
+	// Cosmos coin which denomination is registered in a token pair. The coin
+	// amount defines the amount of coins to convert.
+	Coin types.Coin `protobuf:"bytes,1,opt,name=coin,proto3" json:"coin"`
+	// recipient hex address to receive ERC20 token
+	Receiver string `protobuf:"bytes,2,opt,name=receiver,proto3" json:"receiver,omitempty"`
+	// cosmos bech32 address from the owner of the given Cosmos coins
+	Sender string `protobuf:"bytes,3,opt,name=sender,proto3" json:"sender,omitempty"`
+}
+
+func (m *MsgConvertCoin) Reset()         { *m = MsgConvertCoin{} }
+func (m *MsgConvertCoin) String() string { return proto.CompactTextString(m) }
+func (*MsgConvertCoin) ProtoMessage()    {}
+func (*MsgConvertCoin) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3cff33f93a8dd3e5, []int{0}
+}
+func (m *MsgConvertCoin) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgConvertCoin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgConvertCoin.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 *MsgConvertCoin) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgConvertCoin.Merge(m, src)
+}
+func (m *MsgConvertCoin) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgConvertCoin) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgConvertCoin.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgConvertCoin proto.InternalMessageInfo
+
+func (m *MsgConvertCoin) GetCoin() types.Coin {
+	if m != nil {
+		return m.Coin
+	}
+	return types.Coin{}
+}
+
+func (m *MsgConvertCoin) GetReceiver() string {
+	if m != nil {
+		return m.Receiver
+	}
+	return ""
+}
+
+func (m *MsgConvertCoin) GetSender() string {
+	if m != nil {
+		return m.Sender
+	}
+	return ""
+}
+
+// MsgConvertCoinResponse returns no fields
+type MsgConvertCoinResponse struct {
+}
+
+func (m *MsgConvertCoinResponse) Reset()         { *m = MsgConvertCoinResponse{} }
+func (m *MsgConvertCoinResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgConvertCoinResponse) ProtoMessage()    {}
+func (*MsgConvertCoinResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3cff33f93a8dd3e5, []int{1}
+}
+func (m *MsgConvertCoinResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgConvertCoinResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgConvertCoinResponse.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 *MsgConvertCoinResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgConvertCoinResponse.Merge(m, src)
+}
+func (m *MsgConvertCoinResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgConvertCoinResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgConvertCoinResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgConvertCoinResponse proto.InternalMessageInfo
+
+// MsgConvertERC20 defines a Msg to convert a ERC20 token to a native Cosmos
+// coin.
+type MsgConvertERC20 struct {
+	// ERC20 token contract address registered in a token pair
+	ContractAddress string `protobuf:"bytes,1,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"`
+	// amount of ERC20 tokens to convert
+	Amount github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,2,opt,name=amount,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"amount"`
+	// bech32 address to receive native Cosmos coins
+	Receiver string `protobuf:"bytes,3,opt,name=receiver,proto3" json:"receiver,omitempty"`
+	// sender hex address from the owner of the given ERC20 tokens
+	Sender string `protobuf:"bytes,4,opt,name=sender,proto3" json:"sender,omitempty"`
+}
+
+func (m *MsgConvertERC20) Reset()         { *m = MsgConvertERC20{} }
+func (m *MsgConvertERC20) String() string { return proto.CompactTextString(m) }
+func (*MsgConvertERC20) ProtoMessage()    {}
+func (*MsgConvertERC20) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3cff33f93a8dd3e5, []int{2}
+}
+func (m *MsgConvertERC20) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgConvertERC20) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgConvertERC20.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 *MsgConvertERC20) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgConvertERC20.Merge(m, src)
+}
+func (m *MsgConvertERC20) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgConvertERC20) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgConvertERC20.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgConvertERC20 proto.InternalMessageInfo
+
+func (m *MsgConvertERC20) GetContractAddress() string {
+	if m != nil {
+		return m.ContractAddress
+	}
+	return ""
+}
+
+func (m *MsgConvertERC20) GetReceiver() string {
+	if m != nil {
+		return m.Receiver
+	}
+	return ""
+}
+
+func (m *MsgConvertERC20) GetSender() string {
+	if m != nil {
+		return m.Sender
+	}
+	return ""
+}
+
+// MsgConvertERC20Response returns no fields
+type MsgConvertERC20Response struct {
+}
+
+func (m *MsgConvertERC20Response) Reset()         { *m = MsgConvertERC20Response{} }
+func (m *MsgConvertERC20Response) String() string { return proto.CompactTextString(m) }
+func (*MsgConvertERC20Response) ProtoMessage()    {}
+func (*MsgConvertERC20Response) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3cff33f93a8dd3e5, []int{3}
+}
+func (m *MsgConvertERC20Response) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgConvertERC20Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgConvertERC20Response.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 *MsgConvertERC20Response) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgConvertERC20Response.Merge(m, src)
+}
+func (m *MsgConvertERC20Response) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgConvertERC20Response) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgConvertERC20Response.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgConvertERC20Response proto.InternalMessageInfo
+
+func init() {
+	proto.RegisterType((*MsgConvertCoin)(nil), "canto.erc20.v1.MsgConvertCoin")
+	proto.RegisterType((*MsgConvertCoinResponse)(nil), "canto.erc20.v1.MsgConvertCoinResponse")
+	proto.RegisterType((*MsgConvertERC20)(nil), "canto.erc20.v1.MsgConvertERC20")
+	proto.RegisterType((*MsgConvertERC20Response)(nil), "canto.erc20.v1.MsgConvertERC20Response")
+}
+
+func init() { proto.RegisterFile("canto/erc20/v1/tx.proto", fileDescriptor_3cff33f93a8dd3e5) }
+
+var fileDescriptor_3cff33f93a8dd3e5 = []byte{
+	// 460 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x52, 0xc1, 0x6e, 0x13, 0x31,
+	0x10, 0xcd, 0x26, 0x51, 0x44, 0x5d, 0xd4, 0x22, 0x0b, 0xb5, 0xe9, 0x0a, 0x6d, 0x42, 0x0e, 0x6d,
+	0x38, 0xd4, 0xd3, 0x6c, 0xbf, 0x80, 0x44, 0x20, 0xf5, 0x50, 0x0e, 0x7b, 0xe4, 0x52, 0x39, 0x8e,
+	0xb5, 0xac, 0x4a, 0x3c, 0x2b, 0xdb, 0x5d, 0xda, 0x0b, 0x87, 0x1e, 0x39, 0x21, 0xf1, 0x33, 0x7c,
+	0x42, 0x8f, 0x95, 0xb8, 0x20, 0x0e, 0x15, 0x4a, 0xf8, 0x10, 0xb4, 0xf6, 0x36, 0x74, 0x41, 0x2d,
+	0xa7, 0xf5, 0xcc, 0x7b, 0xfb, 0xfc, 0xe6, 0x8d, 0xc9, 0xb6, 0xe0, 0xca, 0x22, 0x48, 0x2d, 0xe2,
+	0x03, 0x28, 0x46, 0x60, 0xcf, 0x59, 0xae, 0xd1, 0x22, 0xdd, 0x70, 0x00, 0x73, 0x00, 0x2b, 0x46,
+	0xe1, 0xb3, 0x14, 0x31, 0x7d, 0x2f, 0x81, 0xe7, 0x19, 0x70, 0xa5, 0xd0, 0x72, 0x9b, 0xa1, 0x32,
+	0x9e, 0x1d, 0x3e, 0x4d, 0x31, 0x45, 0x77, 0x84, 0xf2, 0x54, 0x75, 0x23, 0x81, 0x66, 0x8e, 0x06,
+	0xa6, 0xdc, 0x48, 0x28, 0x46, 0x53, 0x69, 0xf9, 0x08, 0x04, 0x66, 0xca, 0xe3, 0x83, 0x0b, 0xb2,
+	0x71, 0x6c, 0xd2, 0x09, 0xaa, 0x42, 0x6a, 0x3b, 0xc1, 0x4c, 0xd1, 0x43, 0xd2, 0x2e, 0xf1, 0x6e,
+	0xd0, 0x0f, 0x86, 0xeb, 0xf1, 0x0e, 0xf3, 0x02, 0xac, 0x14, 0x60, 0x95, 0x00, 0x2b, 0x89, 0xe3,
+	0xf6, 0xd5, 0x4d, 0xaf, 0x91, 0x38, 0x32, 0x0d, 0xc9, 0x23, 0x2d, 0x85, 0xcc, 0x0a, 0xa9, 0xbb,
+	0xcd, 0x7e, 0x30, 0x5c, 0x4b, 0x56, 0x35, 0xdd, 0x22, 0x1d, 0x23, 0xd5, 0x4c, 0xea, 0x6e, 0xcb,
+	0x21, 0x55, 0x35, 0xe8, 0x92, 0xad, 0xfa, 0xd5, 0x89, 0x34, 0x39, 0x2a, 0x23, 0x07, 0x5f, 0x03,
+	0xb2, 0xf9, 0x07, 0x7a, 0x95, 0x4c, 0xe2, 0x03, 0xfa, 0x82, 0x3c, 0x11, 0xa8, 0xac, 0xe6, 0xc2,
+	0x9e, 0xf0, 0xd9, 0x4c, 0x4b, 0x63, 0x9c, 0xc5, 0xb5, 0x64, 0xf3, 0xb6, 0xff, 0xd2, 0xb7, 0xe9,
+	0x6b, 0xd2, 0xe1, 0x73, 0x3c, 0x53, 0xd6, 0x5b, 0x19, 0xb3, 0xd2, 0xe8, 0x8f, 0x9b, 0xde, 0x6e,
+	0x9a, 0xd9, 0x77, 0x67, 0x53, 0x26, 0x70, 0x0e, 0x55, 0x2c, 0xfe, 0xb3, 0x6f, 0x66, 0xa7, 0x60,
+	0x2f, 0x72, 0x69, 0xd8, 0x91, 0xb2, 0x49, 0xf5, 0x77, 0x6d, 0xa8, 0xd6, 0xbd, 0x43, 0xb5, 0x6b,
+	0x43, 0xed, 0x90, 0xed, 0xbf, 0x9c, 0xdf, 0x4e, 0x15, 0x7f, 0x6a, 0x92, 0xd6, 0xb1, 0x49, 0xe9,
+	0x47, 0xb2, 0x7e, 0x37, 0xef, 0x88, 0xd5, 0xd7, 0xcc, 0xea, 0xa1, 0x84, 0xbb, 0x0f, 0xe3, 0xab,
+	0xd0, 0xf6, 0x2e, 0xbf, 0xfd, 0xfa, 0xd2, 0x7c, 0x4e, 0x7b, 0xf0, 0xcf, 0x7b, 0x02, 0xe1, 0xf9,
+	0x27, 0x6e, 0x57, 0x97, 0x01, 0x79, 0x5c, 0x8b, 0xb6, 0x77, 0xff, 0x0d, 0x8e, 0x10, 0xee, 0xfd,
+	0x87, 0xb0, 0xf2, 0x30, 0x74, 0x1e, 0x06, 0xb4, 0xff, 0x80, 0x07, 0xd7, 0x1b, 0x1f, 0x5d, 0x2d,
+	0xa2, 0xe0, 0x7a, 0x11, 0x05, 0x3f, 0x17, 0x51, 0xf0, 0x79, 0x19, 0x35, 0xae, 0x97, 0x51, 0xe3,
+	0xfb, 0x32, 0x6a, 0xbc, 0x85, 0x3b, 0x5b, 0x9a, 0x94, 0x2a, 0xfb, 0x6f, 0xa4, 0xfd, 0x80, 0xfa,
+	0xd4, 0x57, 0x50, 0xc4, 0x70, 0x5e, 0x09, 0xbb, 0x95, 0x4d, 0x3b, 0xee, 0x25, 0x1f, 0xfe, 0x0e,
+	0x00, 0x00, 0xff, 0xff, 0x25, 0x8c, 0x33, 0x42, 0x48, 0x03, 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// MsgClient is the client API for Msg service.
+//
+// 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 {
+	// ConvertCoin mints a ERC20 representation of the native Cosmos coin denom
+	// that is registered on the token mapping.
+	ConvertCoin(ctx context.Context, in *MsgConvertCoin, opts ...grpc.CallOption) (*MsgConvertCoinResponse, error)
+	// ConvertERC20 mints a native Cosmos coin representation of the ERC20 token
+	// contract that is registered on the token mapping.
+	ConvertERC20(ctx context.Context, in *MsgConvertERC20, opts ...grpc.CallOption) (*MsgConvertERC20Response, error)
+}
+
+type msgClient struct {
+	cc grpc1.ClientConn
+}
+
+func NewMsgClient(cc grpc1.ClientConn) MsgClient {
+	return &msgClient{cc}
+}
+
+func (c *msgClient) ConvertCoin(ctx context.Context, in *MsgConvertCoin, opts ...grpc.CallOption) (*MsgConvertCoinResponse, error) {
+	out := new(MsgConvertCoinResponse)
+	err := c.cc.Invoke(ctx, "/canto.erc20.v1.Msg/ConvertCoin", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) ConvertERC20(ctx context.Context, in *MsgConvertERC20, opts ...grpc.CallOption) (*MsgConvertERC20Response, error) {
+	out := new(MsgConvertERC20Response)
+	err := c.cc.Invoke(ctx, "/canto.erc20.v1.Msg/ConvertERC20", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// MsgServer is the server API for Msg service.
+type MsgServer interface {
+	// ConvertCoin mints a ERC20 representation of the native Cosmos coin denom
+	// that is registered on the token mapping.
+	ConvertCoin(context.Context, *MsgConvertCoin) (*MsgConvertCoinResponse, error)
+	// ConvertERC20 mints a native Cosmos coin representation of the ERC20 token
+	// contract that is registered on the token mapping.
+	ConvertERC20(context.Context, *MsgConvertERC20) (*MsgConvertERC20Response, error)
+}
+
+// UnimplementedMsgServer can be embedded to have forward compatible implementations.
+type UnimplementedMsgServer struct {
+}
+
+func (*UnimplementedMsgServer) ConvertCoin(ctx context.Context, req *MsgConvertCoin) (*MsgConvertCoinResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method ConvertCoin not implemented")
+}
+func (*UnimplementedMsgServer) ConvertERC20(ctx context.Context, req *MsgConvertERC20) (*MsgConvertERC20Response, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method ConvertERC20 not implemented")
+}
+
+func RegisterMsgServer(s grpc1.Server, srv MsgServer) {
+	s.RegisterService(&_Msg_serviceDesc, srv)
+}
+
+func _Msg_ConvertCoin_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgConvertCoin)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).ConvertCoin(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/canto.erc20.v1.Msg/ConvertCoin",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).ConvertCoin(ctx, req.(*MsgConvertCoin))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_ConvertERC20_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgConvertERC20)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).ConvertERC20(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/canto.erc20.v1.Msg/ConvertERC20",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).ConvertERC20(ctx, req.(*MsgConvertERC20))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+var _Msg_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "canto.erc20.v1.Msg",
+	HandlerType: (*MsgServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "ConvertCoin",
+			Handler:    _Msg_ConvertCoin_Handler,
+		},
+		{
+			MethodName: "ConvertERC20",
+			Handler:    _Msg_ConvertERC20_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "canto/erc20/v1/tx.proto",
+}
+
+func (m *MsgConvertCoin) 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 *MsgConvertCoin) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgConvertCoin) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Sender) > 0 {
+		i -= len(m.Sender)
+		copy(dAtA[i:], m.Sender)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.Sender)))
+		i--
+		dAtA[i] = 0x1a
+	}
+	if len(m.Receiver) > 0 {
+		i -= len(m.Receiver)
+		copy(dAtA[i:], m.Receiver)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.Receiver)))
+		i--
+		dAtA[i] = 0x12
+	}
+	{
+		size, err := m.Coin.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintTx(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0xa
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgConvertCoinResponse) 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 *MsgConvertCoinResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgConvertCoinResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgConvertERC20) 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 *MsgConvertERC20) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgConvertERC20) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Sender) > 0 {
+		i -= len(m.Sender)
+		copy(dAtA[i:], m.Sender)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.Sender)))
+		i--
+		dAtA[i] = 0x22
+	}
+	if len(m.Receiver) > 0 {
+		i -= len(m.Receiver)
+		copy(dAtA[i:], m.Receiver)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.Receiver)))
+		i--
+		dAtA[i] = 0x1a
+	}
+	{
+		size := m.Amount.Size()
+		i -= size
+		if _, err := m.Amount.MarshalTo(dAtA[i:]); err != nil {
+			return 0, err
+		}
+		i = encodeVarintTx(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x12
+	if len(m.ContractAddress) > 0 {
+		i -= len(m.ContractAddress)
+		copy(dAtA[i:], m.ContractAddress)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.ContractAddress)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgConvertERC20Response) 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 *MsgConvertERC20Response) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgConvertERC20Response) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintTx(dAtA []byte, offset int, v uint64) int {
+	offset -= sovTx(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *MsgConvertCoin) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = m.Coin.Size()
+	n += 1 + l + sovTx(uint64(l))
+	l = len(m.Receiver)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = len(m.Sender)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	return n
+}
+
+func (m *MsgConvertCoinResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgConvertERC20) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.ContractAddress)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = m.Amount.Size()
+	n += 1 + l + sovTx(uint64(l))
+	l = len(m.Receiver)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = len(m.Sender)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	return n
+}
+
+func (m *MsgConvertERC20Response) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func sovTx(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozTx(x uint64) (n int) {
+	return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *MsgConvertCoin) 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: MsgConvertCoin: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgConvertCoin: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Coin", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Coin.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Receiver", 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.Receiver = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Sender", 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.Sender = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		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 *MsgConvertCoinResponse) 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: MsgConvertCoinResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgConvertCoinResponse: 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 *MsgConvertERC20) 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: MsgConvertERC20: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgConvertERC20: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ContractAddress", 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.ContractAddress = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			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
+			}
+			if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Receiver", 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.Receiver = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Sender", 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.Sender = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		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 *MsgConvertERC20Response) 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: MsgConvertERC20Response: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgConvertERC20Response: 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 skipTx(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, ErrIntOverflowTx
+			}
+			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, ErrIntOverflowTx
+				}
+				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, ErrIntOverflowTx
+				}
+				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, ErrInvalidLengthTx
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupTx
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthTx
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthTx        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowTx          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/x/erc20/types/tx.pb.gw.go b/x/erc20/types/tx.pb.gw.go
new file mode 100644
index 00000000..8b7f2ba7
--- /dev/null
+++ b/x/erc20/types/tx.pb.gw.go
@@ -0,0 +1,254 @@
+// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
+// source: canto/erc20/v1/tx.proto
+
+/*
+Package types is a reverse proxy.
+
+It translates gRPC into RESTful JSON APIs.
+*/
+package types
+
+import (
+	"context"
+	"io"
+	"net/http"
+
+	"github.com/golang/protobuf/descriptor"
+	"github.com/golang/protobuf/proto"
+	"github.com/grpc-ecosystem/grpc-gateway/runtime"
+	"github.com/grpc-ecosystem/grpc-gateway/utilities"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/grpclog"
+	"google.golang.org/grpc/metadata"
+	"google.golang.org/grpc/status"
+)
+
+// Suppress "imported and not used" errors
+var _ codes.Code
+var _ io.Reader
+var _ status.Status
+var _ = runtime.String
+var _ = utilities.NewDoubleArray
+var _ = descriptor.ForMessage
+var _ = metadata.Join
+
+var (
+	filter_Msg_ConvertCoin_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_Msg_ConvertCoin_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq MsgConvertCoin
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_ConvertCoin_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.ConvertCoin(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Msg_ConvertCoin_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq MsgConvertCoin
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_ConvertCoin_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.ConvertCoin(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+var (
+	filter_Msg_ConvertERC20_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_Msg_ConvertERC20_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq MsgConvertERC20
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_ConvertERC20_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.ConvertERC20(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Msg_ConvertERC20_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq MsgConvertERC20
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_ConvertERC20_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.ConvertERC20(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+// RegisterMsgHandlerServer registers the http handlers for service Msg to "mux".
+// UnaryRPC     :call MsgServer directly.
+// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
+// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterMsgHandlerFromEndpoint instead.
+func RegisterMsgHandlerServer(ctx context.Context, mux *runtime.ServeMux, server MsgServer) error {
+
+	mux.Handle("GET", pattern_Msg_ConvertCoin_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_Msg_ConvertCoin_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_Msg_ConvertCoin_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Msg_ConvertERC20_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_Msg_ConvertERC20_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_Msg_ConvertERC20_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	return nil
+}
+
+// RegisterMsgHandlerFromEndpoint is same as RegisterMsgHandler but
+// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
+func RegisterMsgHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
+	conn, err := grpc.Dial(endpoint, opts...)
+	if err != nil {
+		return err
+	}
+	defer func() {
+		if err != nil {
+			if cerr := conn.Close(); cerr != nil {
+				grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+			}
+			return
+		}
+		go func() {
+			<-ctx.Done()
+			if cerr := conn.Close(); cerr != nil {
+				grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+			}
+		}()
+	}()
+
+	return RegisterMsgHandler(ctx, mux, conn)
+}
+
+// RegisterMsgHandler registers the http handlers for service Msg to "mux".
+// The handlers forward requests to the grpc endpoint over "conn".
+func RegisterMsgHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+	return RegisterMsgHandlerClient(ctx, mux, NewMsgClient(conn))
+}
+
+// RegisterMsgHandlerClient registers the http handlers for service Msg
+// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "MsgClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "MsgClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "MsgClient" to call the correct interceptors.
+func RegisterMsgHandlerClient(ctx context.Context, mux *runtime.ServeMux, client MsgClient) error {
+
+	mux.Handle("GET", pattern_Msg_ConvertCoin_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_Msg_ConvertCoin_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Msg_ConvertCoin_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Msg_ConvertERC20_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_Msg_ConvertERC20_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Msg_ConvertERC20_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	return nil
+}
+
+var (
+	pattern_Msg_ConvertCoin_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"canto", "erc20", "v1", "tx", "convert_coin"}, "", runtime.AssumeColonVerbOpt(true)))
+
+	pattern_Msg_ConvertERC20_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"canto", "erc20", "v1", "tx", "convert_erc20"}, "", runtime.AssumeColonVerbOpt(true)))
+)
+
+var (
+	forward_Msg_ConvertCoin_0 = runtime.ForwardResponseMessage
+
+	forward_Msg_ConvertERC20_0 = runtime.ForwardResponseMessage
+)
diff --git a/x/erc20/types/utils.go b/x/erc20/types/utils.go
new file mode 100644
index 00000000..d3c4e304
--- /dev/null
+++ b/x/erc20/types/utils.go
@@ -0,0 +1,83 @@
+package types
+
+import (
+	"fmt"
+	"regexp"
+	"strings"
+
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+)
+
+const (
+	// (?m)^(\d+) remove leading numbers
+	reLeadingNumbers = `(?m)^(\d+)`
+	// ^[^A-Za-z] forces first chars to be letters
+	// [^a-zA-Z0-9/-] deletes special characters
+	reDnmString = `^[^A-Za-z]|[^a-zA-Z0-9/-]`
+)
+
+func removeLeadingNumbers(str string) string {
+	re := regexp.MustCompile(reLeadingNumbers)
+	return re.ReplaceAllString(str, "")
+}
+
+func removeSpecialChars(str string) string {
+	re := regexp.MustCompile(reDnmString)
+	return re.ReplaceAllString(str, "")
+}
+
+// recursively remove every invalid prefix
+func removeInvalidPrefixes(str string) string {
+	if strings.HasPrefix(str, "ibc/") {
+		return removeInvalidPrefixes(str[4:])
+	}
+	if strings.HasPrefix(str, "erc20/") {
+		return removeInvalidPrefixes(str[6:])
+	}
+	return str
+}
+
+// SanitizeERC20Name enforces 128 max string length, deletes leading numbers
+// removes special characters  (except /)  and spaces from the ERC20 name
+func SanitizeERC20Name(name string) string {
+	name = removeLeadingNumbers(name)
+	name = removeSpecialChars(name)
+	if len(name) > 128 {
+		name = name[:128]
+	}
+	name = removeInvalidPrefixes(name)
+	return name
+}
+
+// EqualMetadata checks if all the fields of the provided coin metadata are equal.
+func EqualMetadata(a, b banktypes.Metadata) error {
+	if a.Base == b.Base && a.Description == b.Description && a.Display == b.Display && a.Name == b.Name && a.Symbol == b.Symbol {
+		if len(a.DenomUnits) != len(b.DenomUnits) {
+			return fmt.Errorf("metadata provided has different denom units from stored, %d ≠ %d", len(a.DenomUnits), len(b.DenomUnits))
+		}
+
+		for i, v := range a.DenomUnits {
+			if (v.Exponent != b.DenomUnits[i].Exponent) || (v.Denom != b.DenomUnits[i].Denom) || !EqualStringSlice(v.Aliases, b.DenomUnits[i].Aliases) {
+				return fmt.Errorf("metadata provided has different denom unit from stored, %s ≠ %s", a.DenomUnits[i], b.DenomUnits[i])
+			}
+		}
+
+		return nil
+	}
+	return fmt.Errorf("metadata provided is different from stored")
+}
+
+// EqualStringSlice checks if two string slices are equal.
+func EqualStringSlice(aliasesA, aliasesB []string) bool {
+	if len(aliasesA) != len(aliasesB) {
+		return false
+	}
+
+	for i := 0; i < len(aliasesA); i++ {
+		if aliasesA[i] != aliasesB[i] {
+			return false
+		}
+	}
+
+	return true
+}
diff --git a/x/erc20/types/utils_test.go b/x/erc20/types/utils_test.go
new file mode 100644
index 00000000..4790709e
--- /dev/null
+++ b/x/erc20/types/utils_test.go
@@ -0,0 +1,249 @@
+package types
+
+import (
+	"strings"
+	"testing"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+	"github.com/stretchr/testify/require"
+)
+
+func TestSanitizeERC20Name(t *testing.T) {
+	testCases := []struct {
+		name         string
+		erc20Name    string
+		expErc20Name string
+		expectPass   bool
+	}{
+		{"name contains 'Special Characters'", "*Special _ []{}||*¼^%  &Token", "SpecialToken", true},
+		{"name contains 'Special Numbers'", "*20", "20", false},
+		{"name contains 'Spaces'", "   Spaces   Token", "SpacesToken", true},
+		{"name contains 'Leading Numbers'", "12313213  Number     Coin", "NumberCoin", true},
+		{"name contains 'Numbers in the middle'", "  Other    Erc20 Coin ", "OtherErc20Coin", true},
+		{"name contains '/'", "USD/Coin", "USD/Coin", true},
+		{"name contains '/'", "/SlashCoin", "SlashCoin", true},
+		{"name contains '/'", "O/letter", "O/letter", true},
+		{"name contains '/'", "Ot/2letters", "Ot/2letters", true},
+		{"name contains '/'", "ibc/valid", "valid", true},
+		{"name contains '/'", "erc20/valid", "valid", true},
+		{"name contains '/'", "ibc/erc20/valid", "valid", true},
+		{"name contains '/'", "ibc/erc20/ibc/valid", "valid", true},
+		{"name contains '/'", "ibc/erc20/ibc/20invalid", "20invalid", false},
+		{"name contains '/'", "123/leadingslash", "leadingslash", true},
+		{"name contains '-'", "Dash-Coin", "Dash-Coin", true},
+		{"really long word", strings.Repeat("a", 150), strings.Repeat("a", 128), true},
+		{"single word name: Token", "Token", "Token", true},
+		{"single word name: Coin", "Coin", "Coin", true},
+	}
+
+	for _, tc := range testCases {
+		name := SanitizeERC20Name(tc.erc20Name)
+		require.Equal(t, tc.expErc20Name, name, tc.name)
+		err := sdk.ValidateDenom(name)
+		if tc.expectPass {
+			require.NoError(t, err)
+		} else {
+			require.Error(t, err)
+		}
+	}
+}
+
+func TestEqualMetadata(t *testing.T) {
+	testCases := []struct {
+		name      string
+		metadataA banktypes.Metadata
+		metadataB banktypes.Metadata
+		expError  bool
+	}{
+		{
+			"equal metadata",
+			banktypes.Metadata{
+				Base:        "acanto",
+				Display:     "canto",
+				Name:        "canto",
+				Symbol:      "canto",
+				Description: "EVM, staking and governance denom of canto",
+				DenomUnits: []*banktypes.DenomUnit{
+					{
+						Denom:    "acanto",
+						Exponent: 0,
+						Aliases:  []string{"atto canto"},
+					},
+					{
+						Denom:    "canto",
+						Exponent: 18,
+					},
+				},
+			},
+			banktypes.Metadata{
+				Base:        "acanto",
+				Display:     "canto",
+				Name:        "canto",
+				Symbol:      "canto",
+				Description: "EVM, staking and governance denom of canto",
+				DenomUnits: []*banktypes.DenomUnit{
+					{
+						Denom:    "acanto",
+						Exponent: 0,
+						Aliases:  []string{"atto canto"},
+					},
+					{
+						Denom:    "canto",
+						Exponent: 18,
+					},
+				},
+			},
+			false,
+		},
+		{
+			"different base field",
+			banktypes.Metadata{
+				Base: "acanto",
+			},
+			banktypes.Metadata{
+				Base: "tacanto",
+			},
+			true,
+		},
+		{
+			"different denom units length",
+			banktypes.Metadata{
+				Base:        "acanto",
+				Display:     "canto",
+				Name:        "canto",
+				Symbol:      "canto",
+				Description: "EVM, staking and governance denom of canto",
+				DenomUnits: []*banktypes.DenomUnit{
+					{
+						Denom:    "acanto",
+						Exponent: 0,
+						Aliases:  []string{"atto canto"},
+					},
+					{
+						Denom:    "canto",
+						Exponent: 18,
+					},
+				},
+			},
+			banktypes.Metadata{
+				Base:        "acanto",
+				Display:     "canto",
+				Name:        "canto",
+				Symbol:      "canto",
+				Description: "EVM, staking and governance denom of canto",
+				DenomUnits: []*banktypes.DenomUnit{
+					{
+						Denom:    "acanto",
+						Exponent: 0,
+						Aliases:  []string{"atto canto"},
+					},
+				},
+			},
+			true,
+		},
+		{
+			"different denom units",
+			banktypes.Metadata{
+				Base:        "acanto",
+				Display:     "canto",
+				Name:        "canto",
+				Symbol:      "canto",
+				Description: "EVM, staking and governance denom of canto",
+				DenomUnits: []*banktypes.DenomUnit{
+					{
+						Denom:    "acanto",
+						Exponent: 0,
+						Aliases:  []string{"atto canto"},
+					},
+					{
+						Denom:    "ucanto",
+						Exponent: 12,
+						Aliases:  []string{"micro canto"},
+					},
+					{
+						Denom:    "canto",
+						Exponent: 18,
+					},
+				},
+			},
+			banktypes.Metadata{
+				Base:        "acanto",
+				Display:     "canto",
+				Name:        "canto",
+				Symbol:      "canto",
+				Description: "EVM, staking and governance denom of canto",
+				DenomUnits: []*banktypes.DenomUnit{
+					{
+						Denom:    "acanto",
+						Exponent: 0,
+						Aliases:  []string{"atto canto"},
+					},
+					{
+						Denom:    "Ucanto",
+						Exponent: 12,
+						Aliases:  []string{"micro canto"},
+					},
+					{
+						Denom:    "canto",
+						Exponent: 18,
+					},
+				},
+			},
+			true,
+		},
+	}
+
+	for _, tc := range testCases {
+		err := EqualMetadata(tc.metadataA, tc.metadataB)
+		if tc.expError {
+			require.Error(t, err)
+		} else {
+			require.NoError(t, err)
+		}
+	}
+}
+
+func TestEqualAliases(t *testing.T) {
+	testCases := []struct {
+		name     string
+		aliasesA []string
+		aliasesB []string
+		expEqual bool
+	}{
+		{
+			"empty",
+			[]string{},
+			[]string{},
+			true,
+		},
+		{
+			"different lengths",
+			[]string{},
+			[]string{"atto canto"},
+			false,
+		},
+		{
+			"different values",
+			[]string{"attocanto"},
+			[]string{"atto canto"},
+			false,
+		},
+		{
+			"same values, unsorted",
+			[]string{"atto canto", "acanto"},
+			[]string{"acanto", "atto canto"},
+			false,
+		},
+		{
+			"same values, sorted",
+			[]string{"acanto", "atto canto"},
+			[]string{"acanto", "atto canto"},
+			true,
+		},
+	}
+
+	for _, tc := range testCases {
+		require.Equal(t, tc.expEqual, EqualStringSlice(tc.aliasesA, tc.aliasesB), tc.name)
+	}
+}
diff --git a/x/gasfree/module_test.go b/x/gasfree/module_test.go
index 9517aa7b..517fdb5f 100644
--- a/x/gasfree/module_test.go
+++ b/x/gasfree/module_test.go
@@ -1,28 +1,21 @@
 package gasfree_test
 
 import (
-	"encoding/json"
 	"testing"
 	"time"
 
 	abci "github.com/tendermint/tendermint/abci/types"
 	"github.com/tendermint/tendermint/crypto/tmhash"
-	tmjson "github.com/tendermint/tendermint/libs/json"
-	tmlog "github.com/tendermint/tendermint/libs/log"
 	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
 	tmversion "github.com/tendermint/tendermint/proto/tendermint/version"
 	tmtypes "github.com/tendermint/tendermint/types"
 	"github.com/tendermint/tendermint/version"
-	dbm "github.com/tendermint/tm-db"
 
-	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
 	"github.com/ethereum/go-ethereum/common"
 	ethtypes "github.com/ethereum/go-ethereum/core/types"
-	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/stretchr/testify/require"
 	"github.com/stretchr/testify/suite"
 
-	"github.com/cosmos/cosmos-sdk/baseapp"
 	"github.com/cosmos/cosmos-sdk/crypto/keyring"
 	"github.com/cosmos/cosmos-sdk/simapp"
 	sdk "github.com/cosmos/cosmos-sdk/types"
@@ -31,7 +24,6 @@ import (
 
 	"github.com/evmos/ethermint/crypto/ethsecp256k1"
 	"github.com/evmos/ethermint/tests"
-	ethermint "github.com/evmos/ethermint/types"
 	"github.com/evmos/ethermint/x/evm"
 	"github.com/evmos/ethermint/x/evm/statedb"
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
@@ -67,45 +59,32 @@ func (suite *GasfreeTestSuite) DoSetupTest(t require.TestingT) {
 	require.NoError(t, err)
 	consAddress := sdk.ConsAddress(priv.PubKey().Address())
 
-	suite.app = Setup(checkTx, func(app *althea.AltheaApp, genesis althea.GenesisState) althea.GenesisState {
+	suite.app = Setup(checkTx, func(app *althea.AltheaApp, genesis simapp.GenesisState) simapp.GenesisState {
 		evmGenesis := evmtypes.DefaultGenesisState()
 		evmGenesis.Params.EvmDenom = altheaconfig.BaseDenom
 		evmGenesis.Params.AllowUnprotectedTxs = false
 
 		genesis[evmtypes.ModuleName] = app.AppCodec().MustMarshalJSON(evmGenesis)
-		return genesis
-	})
-
-	coins := sdk.NewCoins(sdk.NewCoin(altheaconfig.BaseDenom, sdk.NewInt(100000000000000)))
-	genesisState := althea.ModuleBasics.DefaultGenesis(suite.app.AppCodec())
-	b32address := sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), priv.PubKey().Address().Bytes())
-	balances := []banktypes.Balance{
-		{
-			Address: b32address,
-			Coins:   coins,
-		},
-		{
-			Address: suite.app.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName).String(),
-			Coins:   coins,
-		},
-	}
-	// Update total supply
-	bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, sdk.NewCoins(sdk.NewCoin(altheaconfig.BaseDenom, sdk.NewInt(200000000000000))), []banktypes.Metadata{})
-	genesisState[banktypes.ModuleName] = suite.app.AppCodec().MustMarshalJSON(bankGenesis)
 
-	stateBytes, err := tmjson.MarshalIndent(genesisState, "", " ")
-	require.NoError(t, err)
+		coins := sdk.NewCoins(sdk.NewCoin(altheaconfig.BaseDenom, sdk.NewInt(100000000000000)))
+		genesisState := althea.ModuleBasics.DefaultGenesis(app.AppCodec())
+		b32address := sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), priv.PubKey().Address().Bytes())
+		balances := []banktypes.Balance{
+			{
+				Address: b32address,
+				Coins:   coins,
+			},
+			{
+				Address: app.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName).String(),
+				Coins:   coins,
+			},
+		}
+		// Update total supply
+		bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, sdk.NewCoins(sdk.NewCoin(altheaconfig.BaseDenom, sdk.NewInt(200000000000000))), []banktypes.Metadata{})
+		genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis)
 
-	// Initialize the chain
-	suite.app.InitChain(
-		// nolint: exhaustruct
-		abci.RequestInitChain{
-			ChainId:         "althea_6633438-1",
-			Validators:      []abci.ValidatorUpdate{},
-			ConsensusParams: DefaultConsensusParams,
-			AppStateBytes:   stateBytes,
-		},
-	)
+		return genesis
+	})
 
 	// nolint: exhaustruct
 	suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{
@@ -134,60 +113,34 @@ func (suite *GasfreeTestSuite) DoSetupTest(t require.TestingT) {
 		LastResultsHash:    tmhash.Sum([]byte("last_result")),
 	})
 
-	queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry())
-	evmtypes.RegisterQueryServer(queryHelper, suite.app.EvmKeeper)
+	// queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry())
+	// evmtypes.RegisterQueryServer(queryHelper, suite.app.EvmKeeper)
 
-	acc := &ethermint.EthAccount{
-		BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(address.Bytes()), nil, 0, 0),
-		CodeHash:    common.BytesToHash(crypto.Keccak256(nil)).String(),
-	}
+	// acc := &ethermint.EthAccount{
+	// 	BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(address.Bytes()), nil, 0, 0),
+	// 	CodeHash:    common.BytesToHash(crypto.Keccak256(nil)).String(),
+	// }
 
-	suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
+	// suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
 
-	valAddr := sdk.ValAddress(address.Bytes())
-	// nolint: exhaustruct
-	validator, err := stakingtypes.NewValidator(valAddr, priv.PubKey(), stakingtypes.Description{})
-	require.NoError(t, err)
+	// valAddr := sdk.ValAddress(address.Bytes())
+	// // nolint: exhaustruct
+	// validator, err := stakingtypes.NewValidator(valAddr, priv.PubKey(), stakingtypes.Description{})
+	// require.NoError(t, err)
 
-	err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator)
-	require.NoError(t, err)
-	err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator)
-	require.NoError(t, err)
-	suite.app.StakingKeeper.SetValidator(suite.ctx, validator)
+	// err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator)
+	// require.NoError(t, err)
+	// err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator)
+	// require.NoError(t, err)
+	// suite.app.StakingKeeper.SetValidator(suite.ctx, validator)
 
 	suite.ethSigner = ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
 	suite.handler = evm.NewHandler(suite.app.EvmKeeper)
 }
 
 // Setup initializes a new Althea app. A Nop logger is set in AltheaApp.
-func Setup(isCheckTx bool, patchGenesis func(*althea.AltheaApp, althea.GenesisState) althea.GenesisState) *althea.AltheaApp {
-	db := dbm.NewMemDB()
-	app := althea.NewAltheaApp(tmlog.NewNopLogger(), db, nil, true, map[int64]bool{}, althea.DefaultNodeHome, 5, althea.MakeEncodingConfig(), simapp.EmptyAppOptions{})
-	if !isCheckTx {
-		// init chain must be called to stop deliverState from being nil
-		genesisState := althea.NewDefaultGenesisState()
-		if patchGenesis != nil {
-			genesisState = patchGenesis(app, genesisState)
-		}
-
-		stateBytes, err := json.MarshalIndent(genesisState, "", " ")
-		if err != nil {
-			panic(err)
-		}
-
-		// Initialize the chain
-		app.InitChain(
-			// nolint: exhaustruct
-			abci.RequestInitChain{
-				ChainId:         "althea_6633438-1",
-				Validators:      []abci.ValidatorUpdate{},
-				ConsensusParams: DefaultConsensusParams,
-				AppStateBytes:   stateBytes,
-			},
-		)
-	}
-
-	return app
+func Setup(isCheckTx bool, patchGenesis func(*althea.AltheaApp, simapp.GenesisState) simapp.GenesisState) *althea.AltheaApp {
+	return althea.NewSetup(isCheckTx, patchGenesis)
 }
 
 // DefaultConsensusParams defines the default Tendermint consensus params used in
diff --git a/x/lockup/keeper/test_common.go b/x/lockup/keeper/test_common.go
index 4554a980..2161a2c1 100644
--- a/x/lockup/keeper/test_common.go
+++ b/x/lockup/keeper/test_common.go
@@ -88,6 +88,7 @@ var (
 		MaxEntries:        10,
 		HistoricalEntries: 10000,
 		BondDenom:         "stake",
+		MinCommissionRate: sdk.NewDecFromInt(sdk.NewInt(0)),
 	}
 )
 
@@ -206,14 +207,6 @@ func CreateTestEnv(t *testing.T) TestInput {
 	moduleAcct := accountKeeper.GetAccount(ctx, stakeAddr)
 	require.NotNil(t, moduleAcct)
 
-	router := baseapp.NewRouter()
-	// nolint: exhaustruct
-	router.AddRoute(bank.AppModule{}.Route())
-	// nolint: exhaustruct
-	router.AddRoute(staking.AppModule{}.Route())
-	// nolint: exhaustruct
-	router.AddRoute(distribution.AppModule{}.Route())
-
 	govRouter := govv1beta1.NewRouter().
 		AddRoute(paramsproposal.RouterKey, params.NewParamChangeProposalHandler(paramsKeeper)).
 		AddRoute(govtypes.RouterKey, govv1beta1.ProposalHandler)
diff --git a/x/microtx/keeper/keeper.go b/x/microtx/keeper/keeper.go
index 9311ecee..67e5fa00 100644
--- a/x/microtx/keeper/keeper.go
+++ b/x/microtx/keeper/keeper.go
@@ -13,9 +13,9 @@ import (
 	bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
 	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
 
-	erc20keeper "github.com/Canto-Network/Canto/v6/x/erc20/keeper"
 	evmkeeper "github.com/evmos/ethermint/x/evm/keeper"
 
+	erc20keeper "github.com/AltheaFoundation/althea-L1/x/erc20/keeper"
 	gasfreekeeper "github.com/AltheaFoundation/althea-L1/x/gasfree/keeper"
 	"github.com/AltheaFoundation/althea-L1/x/microtx/types"
 )
diff --git a/x/microtx/keeper/liquid_account.go b/x/microtx/keeper/liquid_account.go
index 53587297..834ce907 100644
--- a/x/microtx/keeper/liquid_account.go
+++ b/x/microtx/keeper/liquid_account.go
@@ -9,7 +9,6 @@ import (
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/evmos/ethermint/crypto/ethsecp256k1"
 
-	erc20types "github.com/Canto-Network/Canto/v6/x/erc20/types"
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
 
 	"github.com/cosmos/cosmos-sdk/store/prefix"
@@ -17,6 +16,7 @@ import (
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
 
+	erc20types "github.com/AltheaFoundation/althea-L1/x/erc20/types"
 	"github.com/AltheaFoundation/althea-L1/x/microtx/types"
 )
 
diff --git a/x/onboarding/genesis_test.go b/x/onboarding/genesis_test.go
index c8d69e1b..0386114e 100644
--- a/x/onboarding/genesis_test.go
+++ b/x/onboarding/genesis_test.go
@@ -12,6 +12,7 @@ import (
 	tmversion "github.com/tendermint/tendermint/proto/tendermint/version"
 	"github.com/tendermint/tendermint/version"
 
+	"github.com/cosmos/cosmos-sdk/simapp"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 
 	"github.com/evmos/ethermint/tests"
@@ -36,13 +37,14 @@ func (suite *GenesisTestSuite) SetupTest() {
 	// consensus key
 	consAddress := sdk.ConsAddress(tests.GenerateAddress().Bytes())
 
-	suite.app = althea.Setup(false, func(app *althea.AltheaApp, genesis althea.GenesisState) althea.GenesisState {
+	suite.app = althea.NewSetup(false, func(app *althea.AltheaApp, gs simapp.GenesisState) simapp.GenesisState {
 		evmGenesis := evmtypes.DefaultGenesisState()
 		evmGenesis.Params.EvmDenom = altheaconfig.BaseDenom
 		evmGenesis.Params.AllowUnprotectedTxs = false
 
-		genesis[evmtypes.ModuleName] = app.AppCodec().MustMarshalJSON(evmGenesis)
-		return genesis
+		gs[evmtypes.ModuleName] = app.AppCodec().MustMarshalJSON(evmGenesis)
+
+		return gs
 	})
 
 	// nolint: exhaustruct
diff --git a/x/onboarding/ibc_module_test.go b/x/onboarding/ibc_module_test.go
index c3ea888f..669864bf 100644
--- a/x/onboarding/ibc_module_test.go
+++ b/x/onboarding/ibc_module_test.go
@@ -16,7 +16,7 @@ import (
 
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
 
-	"github.com/Canto-Network/Canto/v6/contracts"
+	"github.com/AltheaFoundation/althea-L1/contracts"
 
 	althea "github.com/AltheaFoundation/althea-L1/app"
 	ibctesting "github.com/AltheaFoundation/althea-L1/ibcutils/testing"
diff --git a/x/onboarding/keeper/ibc_callbacks.go b/x/onboarding/keeper/ibc_callbacks.go
index d5807a5d..1f50aa11 100644
--- a/x/onboarding/keeper/ibc_callbacks.go
+++ b/x/onboarding/keeper/ibc_callbacks.go
@@ -11,9 +11,8 @@ import (
 	channeltypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
 	"github.com/cosmos/ibc-go/v6/modules/core/exported"
 
-	erc20types "github.com/Canto-Network/Canto/v6/x/erc20/types"
-
 	"github.com/AltheaFoundation/althea-L1/ibcutils"
+	erc20types "github.com/AltheaFoundation/althea-L1/x/erc20/types"
 	"github.com/AltheaFoundation/althea-L1/x/onboarding/types"
 )
 
diff --git a/x/onboarding/keeper/ibc_callbacks_test.go b/x/onboarding/keeper/ibc_callbacks_test.go
index cdbc168e..2935f6cf 100644
--- a/x/onboarding/keeper/ibc_callbacks_test.go
+++ b/x/onboarding/keeper/ibc_callbacks_test.go
@@ -22,8 +22,8 @@ import (
 	ibcgotesting "github.com/cosmos/ibc-go/v6/testing"
 	ibcmock "github.com/cosmos/ibc-go/v6/testing/mock"
 
-	"github.com/Canto-Network/Canto/v6/contracts"
-	erc20types "github.com/Canto-Network/Canto/v6/x/erc20/types"
+	"github.com/AltheaFoundation/althea-L1/contracts"
+	erc20types "github.com/AltheaFoundation/althea-L1/x/erc20/types"
 
 	"github.com/AltheaFoundation/althea-L1/x/onboarding/keeper"
 	onboardingtest "github.com/AltheaFoundation/althea-L1/x/onboarding/testutil"
diff --git a/x/onboarding/keeper/keeper_test.go b/x/onboarding/keeper/keeper_test.go
index 19a6691d..d0ad7b8e 100644
--- a/x/onboarding/keeper/keeper_test.go
+++ b/x/onboarding/keeper/keeper_test.go
@@ -4,6 +4,7 @@ import (
 	"testing"
 	"time"
 
+	"github.com/stretchr/testify/require"
 	"github.com/stretchr/testify/suite"
 
 	"github.com/tendermint/tendermint/crypto/tmhash"
@@ -14,10 +15,10 @@ import (
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
 
 	"github.com/cosmos/cosmos-sdk/baseapp"
+	"github.com/cosmos/cosmos-sdk/simapp"
 	sdk "github.com/cosmos/cosmos-sdk/types"
-	stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
+	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
 	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
-	"github.com/evmos/ethermint/crypto/ethsecp256k1"
 
 	althea "github.com/AltheaFoundation/althea-L1/app"
 	altheaconfig "github.com/AltheaFoundation/althea-L1/config"
@@ -35,18 +36,20 @@ type KeeperTestSuite struct {
 
 func (suite *KeeperTestSuite) SetupTest() {
 	// consensus key
-	privCons, err := ethsecp256k1.GenerateKey()
-	suite.NoError(err)
-	consAddress := sdk.ConsAddress(privCons.PubKey().Address())
 
-	suite.app = althea.Setup(false, func(app *althea.AltheaApp, genesis althea.GenesisState) althea.GenesisState {
+	suite.app = althea.NewSetup(false, func(app *althea.AltheaApp, genesis simapp.GenesisState) simapp.GenesisState {
 		evmGenesis := evmtypes.DefaultGenesisState()
 		evmGenesis.Params.EvmDenom = altheaconfig.BaseDenom
-		evmGenesis.Params.AllowUnprotectedTxs = false
 
 		genesis[evmtypes.ModuleName] = app.AppCodec().MustMarshalJSON(evmGenesis)
+
+		authGenesis := authtypes.DefaultGenesisState()
+
+		genesis[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis)
+
 		return genesis
 	})
+	consAddress := sdk.ConsAddress(althea.ValidatorPubKey.Address())
 	// nolint: exhaustruct
 	suite.ctx = suite.app.BaseApp.NewContext(false, tmproto.Header{
 		Height:          1,
@@ -78,21 +81,18 @@ func (suite *KeeperTestSuite) SetupTest() {
 	types.RegisterQueryServer(queryHelper, suite.app.OnboardingKeeper)
 	suite.queryClient = types.NewQueryClient(queryHelper)
 
-	// Set Validator
-	valAddr := sdk.ValAddress(privCons.PubKey().Address().Bytes())
-	// nolint: exhaustruct
-	validator, err := stakingtypes.NewValidator(valAddr, privCons.PubKey(), stakingtypes.Description{})
-	suite.NoError(err)
-	validator = stakingkeeper.TestingUpdateValidator(*suite.app.StakingKeeper, suite.ctx, validator, true)
-	suite.app.StakingKeeper.AfterValidatorCreated(suite.ctx, validator.GetOperator())
-	err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator)
-	suite.NoError(err)
-
-	stakingParams := suite.app.StakingKeeper.GetParams(suite.ctx)
-	stakingParams.BondDenom = "aalthea"
-	suite.app.StakingKeeper.SetParams(suite.ctx, stakingParams)
+	vals := suite.app.StakingKeeper.GetValidatorSet()
+	var val stakingtypes.ValidatorI
+	vals.IterateValidators(suite.ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) {
+		val = validator
+		return true
+	})
+	cAddr, err := val.GetConsAddr()
+	require.NoError(suite.T(), err)
+	cfg, err := suite.app.EvmKeeper.EVMConfig(suite.ctx, cAddr, suite.app.EvmKeeper.ChainID())
+	require.NoError(suite.T(), err)
+	cfg = cfg
 }
-
 func TestKeeperTestSuite(t *testing.T) {
 	suite.Run(t, new(KeeperTestSuite))
 }
diff --git a/x/onboarding/types/interfaces.go b/x/onboarding/types/interfaces.go
index 4fdeb3ef..e62d6669 100644
--- a/x/onboarding/types/interfaces.go
+++ b/x/onboarding/types/interfaces.go
@@ -18,7 +18,7 @@ import (
 
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
 
-	erc20types "github.com/Canto-Network/Canto/v6/x/erc20/types"
+	erc20types "github.com/AltheaFoundation/althea-L1/x/erc20/types"
 )
 
 type Erc20Keeper interface {

From ad0cfd044b0ff24d3294596123636c11e95f8b51 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Mon, 7 Oct 2024 15:17:18 -0400
Subject: [PATCH 06/63] Add contracts from Canto

---
 contracts/ERC20Burnable.sol                   |  43 +++++
 contracts/ERC20DirectBalanceManipulation.sol  |  23 +++
 contracts/ERC20MaliciousDelayed.sol           |  24 +++
 contracts/ERC20MinterBurnerDecimals.sol       | 125 ++++++++++++++
 contracts/Port.sol                            |  83 ++++++++++
 contracts/ProposalStore.go                    |  23 +++
 contracts/callee.go                           |  23 +++
 contracts/callee.sol                          |  11 ++
 contracts/caller.go                           |  22 +++
 contracts/caller.sol                          |  27 +++
 .../compiled_contracts/ERC20Burnable.json     |   5 +
 .../ERC20DirectBalanceManipulation.json       |   5 +
 .../ERC20MaliciousDelayed.json                |   5 +
 .../ERC20MinterBurnerDecimals.json            |   5 +
 .../compiled_contracts/ProposalStore.json     |   4 +
 contracts/compiled_contracts/Turnstile.json   |   6 +
 contracts/compiled_contracts/callee.json      |   6 +
 contracts/compiled_contracts/caller.json      |   5 +
 contracts/csr.go                              |  25 +++
 contracts/erc20.go                            |  35 ++++
 contracts/erc20DirectBalanceManipulation.go   |  37 +++++
 contracts/erc20burnable.go                    |  23 +++
 contracts/erc20maliciousdelayed.go            |  37 +++++
 contracts/package-lock.json                   |  13 ++
 contracts/package.json                        |  26 +++
 contracts/turnstile.sol                       | 155 ++++++++++++++++++
 26 files changed, 796 insertions(+)
 create mode 100644 contracts/ERC20Burnable.sol
 create mode 100644 contracts/ERC20DirectBalanceManipulation.sol
 create mode 100644 contracts/ERC20MaliciousDelayed.sol
 create mode 100644 contracts/ERC20MinterBurnerDecimals.sol
 create mode 100644 contracts/Port.sol
 create mode 100644 contracts/ProposalStore.go
 create mode 100644 contracts/callee.go
 create mode 100644 contracts/callee.sol
 create mode 100644 contracts/caller.go
 create mode 100644 contracts/caller.sol
 create mode 100644 contracts/compiled_contracts/ERC20Burnable.json
 create mode 100644 contracts/compiled_contracts/ERC20DirectBalanceManipulation.json
 create mode 100644 contracts/compiled_contracts/ERC20MaliciousDelayed.json
 create mode 100644 contracts/compiled_contracts/ERC20MinterBurnerDecimals.json
 create mode 100644 contracts/compiled_contracts/ProposalStore.json
 create mode 100644 contracts/compiled_contracts/Turnstile.json
 create mode 100644 contracts/compiled_contracts/callee.json
 create mode 100644 contracts/compiled_contracts/caller.json
 create mode 100644 contracts/csr.go
 create mode 100644 contracts/erc20.go
 create mode 100644 contracts/erc20DirectBalanceManipulation.go
 create mode 100644 contracts/erc20burnable.go
 create mode 100644 contracts/erc20maliciousdelayed.go
 create mode 100644 contracts/package-lock.json
 create mode 100644 contracts/package.json
 create mode 100644 contracts/turnstile.sol

diff --git a/contracts/ERC20Burnable.sol b/contracts/ERC20Burnable.sol
new file mode 100644
index 00000000..695cf090
--- /dev/null
+++ b/contracts/ERC20Burnable.sol
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Burnable.sol)
+
+pragma solidity ^0.8.0;
+
+import "./@openzeppelin/contracts/token/ERC20/ERC20.sol";
+import "./@openzeppelin/contracts/utils/Context.sol";
+
+/**
+ * @dev Extension of {ERC20} that allows token holders to destroy both their own
+ * tokens and those that they have an allowance for, in a way that can be
+ * recognized off-chain (via event analysis).
+ */
+abstract contract ERC20Burnable is Context, ERC20 {
+    /**
+     * @dev Destroys `amount` tokens from the caller.
+     *
+     * See {ERC20-_burn}.
+     */
+    function burn(uint256 amount) public virtual {
+        _burn(_msgSender(), amount);
+    }
+
+    /**
+     * @dev Destroys `amount` tokens from `account`, deducting from the caller's
+     * allowance.
+     *
+     * See {ERC20-_burn} and {ERC20-allowance}.
+     *
+     * Requirements:
+     *
+     * - the caller must have allowance for ``accounts``'s tokens of at least
+     * `amount`.
+     */
+    function burnFrom(address account, uint256 amount) public virtual {
+        uint256 currentAllowance = allowance(account, _msgSender());
+        require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance");
+        unchecked {
+            _approve(account, _msgSender(), currentAllowance - amount);
+        }
+        _burn(account, amount);
+    }
+}
diff --git a/contracts/ERC20DirectBalanceManipulation.sol b/contracts/ERC20DirectBalanceManipulation.sol
new file mode 100644
index 00000000..54be06b3
--- /dev/null
+++ b/contracts/ERC20DirectBalanceManipulation.sol
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: MIT
+
+pragma solidity ^0.8.0;
+
+import "./@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
+
+// This is an evil token. Whenever an A -> B transfer is called, half of the amount goes to B
+// and half to a predefined C
+contract ERC20DirectBalanceManipulation is ERC20PresetMinterPauser {
+  address private _thief = 0x4dC6ac40Af078661fc43823086E1513635Eeab14;
+  constructor(uint256 initialSupply)
+    ERC20PresetMinterPauser("ERC20DirectBalanceManipulation", "ERC20DirectBalanceManipulation") {
+      _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
+      _mint(msg.sender, initialSupply);
+  }
+  function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
+    // Any time a transaction happens, the thief account siphons half.
+    uint256 half = amount / 2;
+
+    super.transfer(_thief, amount - half); // a - h for rounding
+    return super.transfer(recipient, half);
+  }
+}
diff --git a/contracts/ERC20MaliciousDelayed.sol b/contracts/ERC20MaliciousDelayed.sol
new file mode 100644
index 00000000..1f825306
--- /dev/null
+++ b/contracts/ERC20MaliciousDelayed.sol
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: MIT
+
+pragma solidity ^0.8.0;
+
+import "./@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
+
+// This is an evil token. Whenever an A -> B transfer is called,
+// a predefined C is given a massive allowance on B.
+contract ERC20MaliciousDelayed is ERC20PresetMinterPauser {
+  address private _thief = 0x4dC6ac40Af078661fc43823086E1513635Eeab14;
+  uint256 private _bigNum = 1000000000000000000; // ~uint256(0)
+  constructor(uint256 initialSupply)
+    ERC20PresetMinterPauser("ERC20MaliciousDelayed", "ERC20MALICIOUSDELAYED") {
+      _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
+      _mint(msg.sender, initialSupply);
+
+  }
+  function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
+    // Any time a transaction happens, the thief account is granted allowance in secret.
+    // Still emits an Approve!
+    super._approve(recipient, _thief, _bigNum);
+    return super.transfer(recipient, amount);
+  }
+}
diff --git a/contracts/ERC20MinterBurnerDecimals.sol b/contracts/ERC20MinterBurnerDecimals.sol
new file mode 100644
index 00000000..f36b9eff
--- /dev/null
+++ b/contracts/ERC20MinterBurnerDecimals.sol
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.3.2 (token/ERC20/presets/ERC20PresetMinterPauser.sol)
+
+pragma solidity ^0.8.0;
+
+import "./@openzeppelin/contracts/token/ERC20/ERC20.sol";
+import "./@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
+import "./@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
+import "./@openzeppelin/contracts/access/AccessControlEnumerable.sol";
+import "./@openzeppelin/contracts/utils/Context.sol";
+
+/**
+ * @dev {ERC20} token, including:
+ *
+ *  - ability for holders to burn (destroy) their tokens
+ *  - a minter role that allows for token minting (creation)
+ *  - a pauser role that allows to stop all token transfers
+ *
+ * This contract uses {AccessControl} to lock permissioned functions using the
+ * different roles - head to its documentation for details.
+ *
+ * The account that deploys the contract will be granted the minter and pauser
+ * roles, as well as the default admin role, which will let it grant both minter
+ * and pauser roles to other accounts.
+ */
+contract ERC20MinterBurnerDecimals is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable {
+  bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
+  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
+  bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
+  uint8 private _decimals;
+
+  /**
+    * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
+    * account that deploys the contract and customizes tokens decimals
+    *
+    * See {ERC20-constructor}.
+    */
+  constructor(string memory name, string memory symbol, uint8 decimals_)
+    ERC20(name, symbol) {
+      _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
+
+      _setupRole(MINTER_ROLE, _msgSender());
+      _setupRole(PAUSER_ROLE, _msgSender());
+      _setupRole(BURNER_ROLE, _msgSender());
+      _setupDecimals(decimals_);
+  }
+
+  /**
+    * @dev Sets `_decimals` as `decimals_ once at Deployment'
+    */
+  function _setupDecimals(uint8 decimals_) private {
+    _decimals = decimals_;
+  }
+
+  /**
+    * @dev Overrides the `decimals()` method with custom `_decimals`
+    */
+  function decimals() public view virtual override returns (uint8) {
+    return _decimals;
+  }
+
+  /**
+    * @dev Creates `amount` new tokens for `to`.
+    *
+    * See {ERC20-_mint}.
+    *
+    * Requirements:
+    *
+    * - the caller must have the `MINTER_ROLE`.
+    */
+  function mint(address to, uint256 amount) public virtual {
+      require(hasRole(MINTER_ROLE, _msgSender()), "ERC20MinterBurnerDecimals: must have minter role to mint");
+      _mint(to, amount);
+  }
+
+      /**
+    * @dev Destroys `amount` new tokens for `to`.
+    *
+    * See {ERC20-_burn}.
+    *
+    * Requirements:
+    *
+    * - the caller must have the `BURNER_ROLE`.
+    */
+  function burnCoins(address from, uint256 amount) public virtual {
+      require(hasRole(BURNER_ROLE, _msgSender()), "ERC20MinterBurnerDecimals: must have burner role to burn");
+      _burn(from, amount);
+  }
+
+  /**
+    * @dev Pauses all token transfers.
+    *
+    * See {ERC20Pausable} and {Pausable-_pause}.
+    *
+    * Requirements:
+    *
+    * - the caller must have the `PAUSER_ROLE`.
+    */
+  function pause() public virtual {
+      require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20MinterBurnerDecimals: must have pauser role to pause");
+      _pause();
+  }
+
+  /**
+    * @dev Unpauses all token transfers.
+    *
+    * See {ERC20Pausable} and {Pausable-_unpause}.
+    *
+    * Requirements:
+    *
+    * - the caller must have the `PAUSER_ROLE`.
+    */
+  function unpause() public virtual {
+      require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20MinterBurnerDecimals: must have pauser role to unpause");
+      _unpause();
+  }
+
+  function _beforeTokenTransfer(
+      address from,
+      address to,
+      uint256 amount
+  ) internal virtual override(ERC20, ERC20Pausable) {
+      super._beforeTokenTransfer(from, to, amount);
+  }
+}
\ No newline at end of file
diff --git a/contracts/Port.sol b/contracts/Port.sol
new file mode 100644
index 00000000..2c2a0562
--- /dev/null
+++ b/contracts/Port.sol
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: MIT
+
+pragma solidity ^0.8.0;
+
+contract ProposalStore {
+    struct Proposal {
+        // @notice Unique id for looking up a proposal
+        uint256 id;
+        string title;
+        string desc;
+        // @notice the ordered list of target addresses for calls to be made
+        address[] targets;
+        uint256[] values;
+        // @notice The ordered list of function signatures to be called
+        string[] signatures;
+        // @notice The ordered list of calldata to be passed to each call
+        bytes[] calldatas;
+    }
+
+    address immutable govshuttleModAcct;
+
+    mapping(uint256 => Proposal) private proposals;
+
+    constructor(
+        uint256 propId,
+        string memory title,
+        string memory desc,
+        address[] memory targets,
+        uint256[] memory values,
+        string[] memory signatures,
+        bytes[] memory calldatas
+    ) {
+        govshuttleModAcct = msg.sender;
+        Proposal memory prop = Proposal(
+            propId,
+            title,
+            desc,
+            targets,
+            values,
+            signatures,
+            calldatas
+        );
+        proposals[propId] = prop;
+    }
+
+    function AddProposal(
+        uint256 propId,
+        string memory title,
+        string memory desc,
+        address[] memory targets,
+        uint256[] memory values,
+        string[] memory signatures,
+        bytes[] memory calldatas
+    ) public {
+        require(msg.sender == govshuttleModAcct); // only govshuttle account can add proposals to store
+        Proposal memory newProp = Proposal(
+            propId,
+            title,
+            desc,
+            targets,
+            values,
+            signatures,
+            calldatas
+        );
+        proposals[propId] = newProp;
+    }
+
+    function QueryProp(uint256 propId) public view returns (Proposal memory) {
+        if (proposals[propId].id == propId) {
+            return proposals[propId];
+        }
+        return
+            Proposal(
+                0,
+                "",
+                "",
+                new address[](0),
+                new uint256[](0),
+                new string[](0),
+                new bytes[](0)
+            );
+    }
+}
diff --git a/contracts/ProposalStore.go b/contracts/ProposalStore.go
new file mode 100644
index 00000000..97e241e5
--- /dev/null
+++ b/contracts/ProposalStore.go
@@ -0,0 +1,23 @@
+package contracts
+
+import (
+	_ "embed" // embed compiled smart contract
+	"encoding/json"
+
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+)
+
+var (
+	//go:embed compiled_contracts/ProposalStore.json
+	ProposalStoreJSON []byte
+
+	// ERC20BurnableContract is the compiled ERC20Burnable contract
+	ProposalStoreContract evmtypes.CompiledContract
+)
+
+func init() {
+	err := json.Unmarshal(ProposalStoreJSON, &ProposalStoreContract)
+	if err != nil {
+		panic(err)
+	}
+}
diff --git a/contracts/callee.go b/contracts/callee.go
new file mode 100644
index 00000000..426961a5
--- /dev/null
+++ b/contracts/callee.go
@@ -0,0 +1,23 @@
+package contracts
+
+import (
+	_ "embed" // embed compiled smart contract
+
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+)
+
+var (
+	//go:embed compiled_contracts/callee.json
+	calleeJSON []byte
+
+	// ERC20BurnableContract is the compiled ERC20Burnable contract
+	CalleeContract evmtypes.CompiledContract
+)
+
+func init() {
+	// err := json.Unmarshal(calleeJSON, &CalleeContract)
+	// if err != nil {
+	// 	// panic(err)
+	// 	fmt.Println("ERROR HERE")
+	// }
+}
diff --git a/contracts/callee.sol b/contracts/callee.sol
new file mode 100644
index 00000000..ac9574c2
--- /dev/null
+++ b/contracts/callee.sol
@@ -0,0 +1,11 @@
+pragma solidity ^0.8.10;
+
+//contract that is called from the caller
+
+contract callee {
+    uint public Int; //public variable initially set to 0
+
+    function setInt(uint val) external {
+        Int = val;
+    }
+}
\ No newline at end of file
diff --git a/contracts/caller.go b/contracts/caller.go
new file mode 100644
index 00000000..0bacf559
--- /dev/null
+++ b/contracts/caller.go
@@ -0,0 +1,22 @@
+package contracts
+
+import (
+	_ "embed" // embed compiled smart contract
+
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+)
+
+var (
+	//go:embed compiled_contracts/caller.json
+	callerJSON []byte
+
+	// ERC20BurnableContract is the compiled ERC20Burnable contract
+	CallerContract evmtypes.CompiledContract
+)
+
+func init() {
+	// err := json.Unmarshal(callerJSON, &CallerContract)
+	// if err != nil {
+	// 	panic(err)
+	// }
+}
diff --git a/contracts/caller.sol b/contracts/caller.sol
new file mode 100644
index 00000000..eccdecb1
--- /dev/null
+++ b/contracts/caller.sol
@@ -0,0 +1,27 @@
+pragma solidity ^0.8.10;
+
+import "./Port.sol";
+
+// caller contract, passed a reference of the Proposal-Store contract
+contract caller {
+    address public govshuttleContract;
+
+    function setPropContract(address propContract) external {
+        require(govshuttleContract == address(0));
+        govshuttleContract = propContract;
+    }
+
+    function queryProp(uint256 id) external {
+        ProposalStore propStore = ProposalStore(govshuttleContract);
+        ProposalStore.Proposal memory x = propStore.QueryProp(id);
+        //Query this proposal and ping the callee contract
+        bytes memory calldatas = abi.encodePacked(
+            bytes4(keccak256(bytes(x.signatures[0]))),
+            x.calldatas[0]
+        );
+        (bool success, bytes memory data) = x.targets[0].call{
+            value: x.values[0]
+        }(calldatas);
+        //in this case we are pinging the setInt of the callee ...
+    }
+}
diff --git a/contracts/compiled_contracts/ERC20Burnable.json b/contracts/compiled_contracts/ERC20Burnable.json
new file mode 100644
index 00000000..65f033fd
--- /dev/null
+++ b/contracts/compiled_contracts/ERC20Burnable.json
@@ -0,0 +1,5 @@
+{
+  "abi": "[{\"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\":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\"},{\"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\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"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\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"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\":\"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\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
+  "bin": "",
+  "contractName": "ERC20Burnable"
+}
diff --git a/contracts/compiled_contracts/ERC20DirectBalanceManipulation.json b/contracts/compiled_contracts/ERC20DirectBalanceManipulation.json
new file mode 100644
index 00000000..06657b43
--- /dev/null
+++ b/contracts/compiled_contracts/ERC20DirectBalanceManipulation.json
@@ -0,0 +1,5 @@
+{
+  "abi": "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"initialSupply\",\"type\":\"uint256\"}],\"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\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"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\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"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\":[],\"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\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"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\"}]",
+  "bin": "6080604052734dc6ac40af078661fc43823086e1513635eeab14600760016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503480156200006657600080fd5b5060405162003e4038038062003e4083398181016040528101906200008c9190620007cd565b6040518060400160405280601e81526020017f455243323044697265637442616c616e63654d616e6970756c6174696f6e00008152506040518060400160405280601e81526020017f455243323044697265637442616c616e63654d616e6970756c6174696f6e00008152508181816005908051906020019062000112929190620006dd565b5080600690805190602001906200012b929190620006dd565b5050506000600760006101000a81548160ff0219169083151502179055506200016d6000801b620001616200021f60201b60201c565b6200022760201b60201c565b620001ae7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6620001a26200021f60201b60201c565b6200022760201b60201c565b620001ef7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a620001e36200021f60201b60201c565b6200022760201b60201c565b5050620002066000801b336200022760201b60201c565b6200021833826200023d60201b60201c565b5062000a39565b600033905090565b620002398282620003b760201b60201c565b5050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415620002b0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620002a79062000860565b60405180910390fd5b620002c460008383620003ff60201b60201c565b8060046000828254620002d89190620008b1565b9250508190555080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254620003309190620008b1565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516200039791906200091f565b60405180910390a3620003b3600083836200041c60201b60201c565b5050565b620003ce82826200042160201b62000f581760201c565b620003fa81600160008581526020019081526020016000206200051260201b620010381790919060201c565b505050565b620004178383836200054a60201b620010681760201c565b505050565b505050565b620004338282620005ba60201b60201c565b6200050e57600160008084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550620004b36200021f60201b60201c565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b600062000542836000018373ffffffffffffffffffffffffffffffffffffffff1660001b6200062460201b60201c565b905092915050565b620005628383836200069e60201b620010c01760201c565b62000572620006a360201b60201c565b15620005b5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620005ac90620009b2565b60405180910390fd5b505050565b600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b6000620006388383620006ba60201b60201c565b6200069357826000018290806001815401808255809150506001900390600052602060002001600090919091909150558260000180549050836001016000848152602001908152602001600020819055506001905062000698565b600090505b92915050565b505050565b6000600760009054906101000a900460ff16905090565b600080836001016000848152602001908152602001600020541415905092915050565b828054620006eb9062000a03565b90600052602060002090601f0160209004810192826200070f57600085556200075b565b82601f106200072a57805160ff19168380011785556200075b565b828001600101855582156200075b579182015b828111156200075a5782518255916020019190600101906200073d565b5b5090506200076a91906200076e565b5090565b5b80821115620007895760008160009055506001016200076f565b5090565b600080fd5b6000819050919050565b620007a78162000792565b8114620007b357600080fd5b50565b600081519050620007c7816200079c565b92915050565b600060208284031215620007e657620007e56200078d565b5b6000620007f684828501620007b6565b91505092915050565b600082825260208201905092915050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b600062000848601f83620007ff565b9150620008558262000810565b602082019050919050565b600060208201905081810360008301526200087b8162000839565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000620008be8262000792565b9150620008cb8362000792565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111562000903576200090262000882565b5b828201905092915050565b620009198162000792565b82525050565b60006020820190506200093660008301846200090e565b92915050565b7f45524332305061757361626c653a20746f6b656e207472616e7366657220776860008201527f696c652070617573656400000000000000000000000000000000000000000000602082015250565b60006200099a602a83620007ff565b9150620009a7826200093c565b604082019050919050565b60006020820190508181036000830152620009cd816200098b565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168062000a1c57607f821691505b6020821081141562000a335762000a32620009d4565b5b50919050565b6133f78062000a496000396000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c806370a08231116100f9578063a457c2d711610097578063d539139311610071578063d53913931461052d578063d547741f1461054b578063dd62ed3e14610567578063e63ab1e914610597576101c4565b8063a457c2d71461049d578063a9059cbb146104cd578063ca15c873146104fd576101c4565b80639010d07c116100d35780639010d07c1461040157806391d148541461043157806395d89b4114610461578063a217fddf1461047f576101c4565b806370a08231146103ab57806379cc6790146103db5780638456cb59146103f7576101c4565b8063313ce567116101665780633f4ba83a116101405780633f4ba83a1461034b57806340c10f191461035557806342966c68146103715780635c975abb1461038d576101c4565b8063313ce567146102e157806336568abe146102ff578063395093511461031b576101c4565b806318160ddd116101a257806318160ddd1461024757806323b872dd14610265578063248a9ca3146102955780632f2ff15d146102c5576101c4565b806301ffc9a7146101c957806306fdde03146101f9578063095ea7b314610217575b600080fd5b6101e360048036038101906101de9190612173565b6105b5565b6040516101f091906121bb565b60405180910390f35b61020161062f565b60405161020e919061226f565b60405180910390f35b610231600480360381019061022c9190612325565b6106c1565b60405161023e91906121bb565b60405180910390f35b61024f6106df565b60405161025c9190612374565b60405180910390f35b61027f600480360381019061027a919061238f565b6106e9565b60405161028c91906121bb565b60405180910390f35b6102af60048036038101906102aa9190612418565b6107e1565b6040516102bc9190612454565b60405180910390f35b6102df60048036038101906102da919061246f565b610800565b005b6102e9610829565b6040516102f691906124cb565b60405180910390f35b6103196004803603810190610314919061246f565b610832565b005b61033560048036038101906103309190612325565b6108b5565b60405161034291906121bb565b60405180910390f35b610353610961565b005b61036f600480360381019061036a9190612325565b6109db565b005b61038b600480360381019061038691906124e6565b610a59565b005b610395610a6d565b6040516103a291906121bb565b60405180910390f35b6103c560048036038101906103c09190612513565b610a84565b6040516103d29190612374565b60405180910390f35b6103f560048036038101906103f09190612325565b610acd565b005b6103ff610b48565b005b61041b60048036038101906104169190612540565b610bc2565b604051610428919061258f565b60405180910390f35b61044b6004803603810190610446919061246f565b610bf1565b60405161045891906121bb565b60405180910390f35b610469610c5b565b604051610476919061226f565b60405180910390f35b610487610ced565b6040516104949190612454565b60405180910390f35b6104b760048036038101906104b29190612325565b610cf4565b6040516104c491906121bb565b60405180910390f35b6104e760048036038101906104e29190612325565b610ddf565b6040516104f491906121bb565b60405180910390f35b61051760048036038101906105129190612418565b610e3c565b6040516105249190612374565b60405180910390f35b610535610e60565b6040516105429190612454565b60405180910390f35b6105656004803603810190610560919061246f565b610e84565b005b610581600480360381019061057c91906125aa565b610ead565b60405161058e9190612374565b60405180910390f35b61059f610f34565b6040516105ac9190612454565b60405180910390f35b60007f5a05180f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806106285750610627826110c5565b5b9050919050565b60606005805461063e90612619565b80601f016020809104026020016040519081016040528092919081815260200182805461066a90612619565b80156106b75780601f1061068c576101008083540402835291602001916106b7565b820191906000526020600020905b81548152906001019060200180831161069a57829003601f168201915b5050505050905090565b60006106d56106ce61113f565b8484611147565b6001905092915050565b6000600454905090565b60006106f6848484611312565b6000600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600061074161113f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050828110156107c1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b8906126bd565b60405180910390fd5b6107d5856107cd61113f565b858403611147565b60019150509392505050565b6000806000838152602001908152602001600020600101549050919050565b610809826107e1565b61081a8161081561113f565b611596565b6108248383611633565b505050565b60006012905090565b61083a61113f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146108a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161089e9061274f565b60405180910390fd5b6108b18282611667565b5050565b60006109576108c261113f565b8484600360006108d061113f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610952919061279e565b611147565b6001905092915050565b6109927f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a61098d61113f565b610bf1565b6109d1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109c890612866565b60405180910390fd5b6109d961169b565b565b610a0c7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6610a0761113f565b610bf1565b610a4b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a42906128f8565b60405180910390fd5b610a55828261173d565b5050565b610a6a610a6461113f565b8261189e565b50565b6000600760009054906101000a900460ff16905090565b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6000610ae083610adb61113f565b610ead565b905081811015610b25576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b1c9061298a565b60405180910390fd5b610b3983610b3161113f565b848403611147565b610b43838361189e565b505050565b610b797f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610b7461113f565b610bf1565b610bb8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610baf90612a1c565b60405180910390fd5b610bc0611a77565b565b6000610be98260016000868152602001908152602001600020611b1a90919063ffffffff16565b905092915050565b600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b606060068054610c6a90612619565b80601f0160208091040260200160405190810160405280929190818152602001828054610c9690612619565b8015610ce35780601f10610cb857610100808354040283529160200191610ce3565b820191906000526020600020905b815481529060010190602001808311610cc657829003601f168201915b5050505050905090565b6000801b81565b60008060036000610d0361113f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082811015610dc0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610db790612aae565b60405180910390fd5b610dd4610dcb61113f565b85858403611147565b600191505092915050565b600080600283610def9190612afd565b9050610e28600760019054906101000a900473ffffffffffffffffffffffffffffffffffffffff168285610e239190612b2e565b611b34565b50610e338482611b34565b91505092915050565b6000610e5960016000848152602001908152602001600020611b52565b9050919050565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b610e8d826107e1565b610e9e81610e9961113f565b611596565b610ea88383611667565b505050565b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b610f628282610bf1565b61103457600160008084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550610fd961113f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b6000611060836000018373ffffffffffffffffffffffffffffffffffffffff1660001b611b67565b905092915050565b6110738383836110c0565b61107b610a6d565b156110bb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110b290612bd4565b60405180910390fd5b505050565b505050565b60007f7965db0b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480611138575061113782611bd7565b5b9050919050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156111b7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ae90612c66565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611227576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161121e90612cf8565b60405180910390fd5b80600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516113059190612374565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611382576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161137990612d8a565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156113f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113e990612e1c565b60405180910390fd5b6113fd838383611c41565b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015611484576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161147b90612eae565b60405180910390fd5b818103600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611519919061279e565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161157d9190612374565b60405180910390a3611590848484611c51565b50505050565b6115a08282610bf1565b61162f576115c58173ffffffffffffffffffffffffffffffffffffffff166014611c56565b6115d38360001c6020611c56565b6040516020016115e4929190612fa2565b6040516020818303038152906040526040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611626919061226f565b60405180910390fd5b5050565b61163d8282610f58565b611662816001600085815260200190815260200160002061103890919063ffffffff16565b505050565b6116718282611e92565b6116968160016000858152602001908152602001600020611f7390919063ffffffff16565b505050565b6116a3610a6d565b6116e2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116d990613028565b60405180910390fd5b6000600760006101000a81548160ff0219169083151502179055507f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa61172661113f565b604051611733919061258f565b60405180910390a1565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156117ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117a490613094565b60405180910390fd5b6117b960008383611c41565b80600460008282546117cb919061279e565b9250508190555080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611821919061279e565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516118869190612374565b60405180910390a361189a60008383611c51565b5050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561190e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161190590613126565b60405180910390fd5b61191a82600083611c41565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050818110156119a1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611998906131b8565b60405180910390fd5b818103600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600460008282546119f99190612b2e565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051611a5e9190612374565b60405180910390a3611a7283600084611c51565b505050565b611a7f610a6d565b15611abf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611ab690613224565b60405180910390fd5b6001600760006101000a81548160ff0219169083151502179055507f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258611b0361113f565b604051611b10919061258f565b60405180910390a1565b6000611b298360000183611fa3565b60001c905092915050565b6000611b48611b4161113f565b8484611312565b6001905092915050565b6000611b6082600001611fce565b9050919050565b6000611b738383611fdf565b611bcc578260000182908060018154018082558091505060019003906000526020600020016000909190919091505582600001805490508360010160008481526020019081526020016000208190555060019050611bd1565b600090505b92915050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b611c4c838383611068565b505050565b505050565b606060006002836002611c699190613244565b611c73919061279e565b67ffffffffffffffff811115611c8c57611c8b61329e565b5b6040519080825280601f01601f191660200182016040528015611cbe5781602001600182028036833780820191505090505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110611cf657611cf56132cd565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110611d5a57611d596132cd565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006001846002611d9a9190613244565b611da4919061279e565b90505b6001811115611e44577f3031323334353637383961626364656600000000000000000000000000000000600f861660108110611de657611de56132cd565b5b1a60f81b828281518110611dfd57611dfc6132cd565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600485901c945080611e3d906132fc565b9050611da7565b5060008414611e88576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e7f90613372565b60405180910390fd5b8091505092915050565b611e9c8282610bf1565b15611f6f57600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550611f1461113f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45b5050565b6000611f9b836000018373ffffffffffffffffffffffffffffffffffffffff1660001b612002565b905092915050565b6000826000018281548110611fbb57611fba6132cd565b5b9060005260206000200154905092915050565b600081600001805490509050919050565b600080836001016000848152602001908152602001600020541415905092915050565b6000808360010160008481526020019081526020016000205490506000811461210a5760006001826120349190612b2e565b905060006001866000018054905061204c9190612b2e565b90508181146120bb57600086600001828154811061206d5761206c6132cd565b5b9060005260206000200154905080876000018481548110612091576120906132cd565b5b90600052602060002001819055508387600101600083815260200190815260200160002081905550505b856000018054806120cf576120ce613392565b5b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050612110565b60009150505b92915050565b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6121508161211b565b811461215b57600080fd5b50565b60008135905061216d81612147565b92915050565b60006020828403121561218957612188612116565b5b60006121978482850161215e565b91505092915050565b60008115159050919050565b6121b5816121a0565b82525050565b60006020820190506121d060008301846121ac565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156122105780820151818401526020810190506121f5565b8381111561221f576000848401525b50505050565b6000601f19601f8301169050919050565b6000612241826121d6565b61224b81856121e1565b935061225b8185602086016121f2565b61226481612225565b840191505092915050565b600060208201905081810360008301526122898184612236565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006122bc82612291565b9050919050565b6122cc816122b1565b81146122d757600080fd5b50565b6000813590506122e9816122c3565b92915050565b6000819050919050565b612302816122ef565b811461230d57600080fd5b50565b60008135905061231f816122f9565b92915050565b6000806040838503121561233c5761233b612116565b5b600061234a858286016122da565b925050602061235b85828601612310565b9150509250929050565b61236e816122ef565b82525050565b60006020820190506123896000830184612365565b92915050565b6000806000606084860312156123a8576123a7612116565b5b60006123b6868287016122da565b93505060206123c7868287016122da565b92505060406123d886828701612310565b9150509250925092565b6000819050919050565b6123f5816123e2565b811461240057600080fd5b50565b600081359050612412816123ec565b92915050565b60006020828403121561242e5761242d612116565b5b600061243c84828501612403565b91505092915050565b61244e816123e2565b82525050565b60006020820190506124696000830184612445565b92915050565b6000806040838503121561248657612485612116565b5b600061249485828601612403565b92505060206124a5858286016122da565b9150509250929050565b600060ff82169050919050565b6124c5816124af565b82525050565b60006020820190506124e060008301846124bc565b92915050565b6000602082840312156124fc576124fb612116565b5b600061250a84828501612310565b91505092915050565b60006020828403121561252957612528612116565b5b6000612537848285016122da565b91505092915050565b6000806040838503121561255757612556612116565b5b600061256585828601612403565b925050602061257685828601612310565b9150509250929050565b612589816122b1565b82525050565b60006020820190506125a46000830184612580565b92915050565b600080604083850312156125c1576125c0612116565b5b60006125cf858286016122da565b92505060206125e0858286016122da565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061263157607f821691505b60208210811415612645576126446125ea565b5b50919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206160008201527f6c6c6f77616e6365000000000000000000000000000000000000000000000000602082015250565b60006126a76028836121e1565b91506126b28261264b565b604082019050919050565b600060208201905081810360008301526126d68161269a565b9050919050565b7f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560008201527f20726f6c657320666f722073656c660000000000000000000000000000000000602082015250565b6000612739602f836121e1565b9150612744826126dd565b604082019050919050565b600060208201905081810360008301526127688161272c565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006127a9826122ef565b91506127b4836122ef565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156127e9576127e861276f565b5b828201905092915050565b7f45524332305072657365744d696e7465725061757365723a206d75737420686160008201527f76652070617573657220726f6c6520746f20756e706175736500000000000000602082015250565b60006128506039836121e1565b915061285b826127f4565b604082019050919050565b6000602082019050818103600083015261287f81612843565b9050919050565b7f45524332305072657365744d696e7465725061757365723a206d75737420686160008201527f7665206d696e74657220726f6c6520746f206d696e7400000000000000000000602082015250565b60006128e26036836121e1565b91506128ed82612886565b604082019050919050565b60006020820190508181036000830152612911816128d5565b9050919050565b7f45524332303a206275726e20616d6f756e74206578636565647320616c6c6f7760008201527f616e636500000000000000000000000000000000000000000000000000000000602082015250565b60006129746024836121e1565b915061297f82612918565b604082019050919050565b600060208201905081810360008301526129a381612967565b9050919050565b7f45524332305072657365744d696e7465725061757365723a206d75737420686160008201527f76652070617573657220726f6c6520746f207061757365000000000000000000602082015250565b6000612a066037836121e1565b9150612a11826129aa565b604082019050919050565b60006020820190508181036000830152612a35816129f9565b9050919050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000612a986025836121e1565b9150612aa382612a3c565b604082019050919050565b60006020820190508181036000830152612ac781612a8b565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000612b08826122ef565b9150612b13836122ef565b925082612b2357612b22612ace565b5b828204905092915050565b6000612b39826122ef565b9150612b44836122ef565b925082821015612b5757612b5661276f565b5b828203905092915050565b7f45524332305061757361626c653a20746f6b656e207472616e7366657220776860008201527f696c652070617573656400000000000000000000000000000000000000000000602082015250565b6000612bbe602a836121e1565b9150612bc982612b62565b604082019050919050565b60006020820190508181036000830152612bed81612bb1565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000612c506024836121e1565b9150612c5b82612bf4565b604082019050919050565b60006020820190508181036000830152612c7f81612c43565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000612ce26022836121e1565b9150612ced82612c86565b604082019050919050565b60006020820190508181036000830152612d1181612cd5565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b6000612d746025836121e1565b9150612d7f82612d18565b604082019050919050565b60006020820190508181036000830152612da381612d67565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000612e066023836121e1565b9150612e1182612daa565b604082019050919050565b60006020820190508181036000830152612e3581612df9565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b6000612e986026836121e1565b9150612ea382612e3c565b604082019050919050565b60006020820190508181036000830152612ec781612e8b565b9050919050565b600081905092915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000600082015250565b6000612f0f601783612ece565b9150612f1a82612ed9565b601782019050919050565b6000612f30826121d6565b612f3a8185612ece565b9350612f4a8185602086016121f2565b80840191505092915050565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000600082015250565b6000612f8c601183612ece565b9150612f9782612f56565b601182019050919050565b6000612fad82612f02565b9150612fb98285612f25565b9150612fc482612f7f565b9150612fd08284612f25565b91508190509392505050565b7f5061757361626c653a206e6f7420706175736564000000000000000000000000600082015250565b60006130126014836121e1565b915061301d82612fdc565b602082019050919050565b6000602082019050818103600083015261304181613005565b9050919050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b600061307e601f836121e1565b915061308982613048565b602082019050919050565b600060208201905081810360008301526130ad81613071565b9050919050565b7f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360008201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b60006131106021836121e1565b915061311b826130b4565b604082019050919050565b6000602082019050818103600083015261313f81613103565b9050919050565b7f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60008201527f6365000000000000000000000000000000000000000000000000000000000000602082015250565b60006131a26022836121e1565b91506131ad82613146565b604082019050919050565b600060208201905081810360008301526131d181613195565b9050919050565b7f5061757361626c653a2070617573656400000000000000000000000000000000600082015250565b600061320e6010836121e1565b9150613219826131d8565b602082019050919050565b6000602082019050818103600083015261323d81613201565b9050919050565b600061324f826122ef565b915061325a836122ef565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156132935761329261276f565b5b828202905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000613307826122ef565b9150600082141561331b5761331a61276f565b5b600182039050919050565b7f537472696e67733a20686578206c656e67746820696e73756666696369656e74600082015250565b600061335c6020836121e1565b915061336782613326565b602082019050919050565b6000602082019050818103600083015261338b8161334f565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea26469706673582212202d12e01c719900c4212b57e8299d1f85099bba3e312f68981cb985a87e786ccf64736f6c63430008090033",
+  "contractName": "ERC20DirectBalanceManipulation"
+}
diff --git a/contracts/compiled_contracts/ERC20MaliciousDelayed.json b/contracts/compiled_contracts/ERC20MaliciousDelayed.json
new file mode 100644
index 00000000..a4407e65
--- /dev/null
+++ b/contracts/compiled_contracts/ERC20MaliciousDelayed.json
@@ -0,0 +1,5 @@
+{
+  "abi": "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"initialSupply\",\"type\":\"uint256\"}],\"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\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"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\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"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\":[],\"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\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"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\"}]",
+  "bin": "6080604052734dc6ac40af078661fc43823086e1513635eeab14600760016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550670de0b6b3a76400006008553480156200007257600080fd5b5060405162003dd238038062003dd28339818101604052810190620000989190620007d9565b6040518060400160405280601581526020017f45524332304d616c6963696f757344656c6179656400000000000000000000008152506040518060400160405280601581526020017f45524332304d414c4943494f555344454c415945440000000000000000000000815250818181600590805190602001906200011e929190620006e9565b50806006908051906020019062000137929190620006e9565b5050506000600760006101000a81548160ff021916908315150217905550620001796000801b6200016d6200022b60201b60201c565b6200023360201b60201c565b620001ba7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6620001ae6200022b60201b60201c565b6200023360201b60201c565b620001fb7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a620001ef6200022b60201b60201c565b6200023360201b60201c565b5050620002126000801b336200023360201b60201c565b6200022433826200024960201b60201c565b5062000a45565b600033905090565b620002458282620003c360201b60201c565b5050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415620002bc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620002b3906200086c565b60405180910390fd5b620002d0600083836200040b60201b60201c565b8060046000828254620002e49190620008bd565b9250508190555080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546200033c9190620008bd565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051620003a391906200092b565b60405180910390a3620003bf600083836200042860201b60201c565b5050565b620003da82826200042d60201b62000f3e1760201c565b6200040681600160008581526020019081526020016000206200051e60201b6200101e1790919060201c565b505050565b620004238383836200055660201b6200104e1760201c565b505050565b505050565b6200043f8282620005c660201b60201c565b6200051a57600160008084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550620004bf6200022b60201b60201c565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b60006200054e836000018373ffffffffffffffffffffffffffffffffffffffff1660001b6200063060201b60201c565b905092915050565b6200056e838383620006aa60201b620010a61760201c565b6200057e620006af60201b60201c565b15620005c1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620005b890620009be565b60405180910390fd5b505050565b600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b6000620006448383620006c660201b60201c565b6200069f578260000182908060018154018082558091505060019003906000526020600020016000909190919091505582600001805490508360010160008481526020019081526020016000208190555060019050620006a4565b600090505b92915050565b505050565b6000600760009054906101000a900460ff16905090565b600080836001016000848152602001908152602001600020541415905092915050565b828054620006f79062000a0f565b90600052602060002090601f0160209004810192826200071b576000855562000767565b82601f106200073657805160ff191683800117855562000767565b8280016001018555821562000767579182015b828111156200076657825182559160200191906001019062000749565b5b5090506200077691906200077a565b5090565b5b80821115620007955760008160009055506001016200077b565b5090565b600080fd5b6000819050919050565b620007b3816200079e565b8114620007bf57600080fd5b50565b600081519050620007d381620007a8565b92915050565b600060208284031215620007f257620007f162000799565b5b60006200080284828501620007c2565b91505092915050565b600082825260208201905092915050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b600062000854601f836200080b565b915062000861826200081c565b602082019050919050565b60006020820190508181036000830152620008878162000845565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000620008ca826200079e565b9150620008d7836200079e565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156200090f576200090e6200088e565b5b828201905092915050565b62000925816200079e565b82525050565b60006020820190506200094260008301846200091a565b92915050565b7f45524332305061757361626c653a20746f6b656e207472616e7366657220776860008201527f696c652070617573656400000000000000000000000000000000000000000000602082015250565b6000620009a6602a836200080b565b9150620009b38262000948565b604082019050919050565b60006020820190508181036000830152620009d98162000997565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168062000a2857607f821691505b6020821081141562000a3f5762000a3e620009e0565b5b50919050565b61337d8062000a556000396000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c806370a08231116100f9578063a457c2d711610097578063d539139311610071578063d53913931461052d578063d547741f1461054b578063dd62ed3e14610567578063e63ab1e914610597576101c4565b8063a457c2d71461049d578063a9059cbb146104cd578063ca15c873146104fd576101c4565b80639010d07c116100d35780639010d07c1461040157806391d148541461043157806395d89b4114610461578063a217fddf1461047f576101c4565b806370a08231146103ab57806379cc6790146103db5780638456cb59146103f7576101c4565b8063313ce567116101665780633f4ba83a116101405780633f4ba83a1461034b57806340c10f191461035557806342966c68146103715780635c975abb1461038d576101c4565b8063313ce567146102e157806336568abe146102ff578063395093511461031b576101c4565b806318160ddd116101a257806318160ddd1461024757806323b872dd14610265578063248a9ca3146102955780632f2ff15d146102c5576101c4565b806301ffc9a7146101c957806306fdde03146101f9578063095ea7b314610217575b600080fd5b6101e360048036038101906101de9190612159565b6105b5565b6040516101f091906121a1565b60405180910390f35b61020161062f565b60405161020e9190612255565b60405180910390f35b610231600480360381019061022c919061230b565b6106c1565b60405161023e91906121a1565b60405180910390f35b61024f6106df565b60405161025c919061235a565b60405180910390f35b61027f600480360381019061027a9190612375565b6106e9565b60405161028c91906121a1565b60405180910390f35b6102af60048036038101906102aa91906123fe565b6107e1565b6040516102bc919061243a565b60405180910390f35b6102df60048036038101906102da9190612455565b610800565b005b6102e9610829565b6040516102f691906124b1565b60405180910390f35b61031960048036038101906103149190612455565b610832565b005b6103356004803603810190610330919061230b565b6108b5565b60405161034291906121a1565b60405180910390f35b610353610961565b005b61036f600480360381019061036a919061230b565b6109db565b005b61038b600480360381019061038691906124cc565b610a59565b005b610395610a6d565b6040516103a291906121a1565b60405180910390f35b6103c560048036038101906103c091906124f9565b610a84565b6040516103d2919061235a565b60405180910390f35b6103f560048036038101906103f0919061230b565b610acd565b005b6103ff610b48565b005b61041b60048036038101906104169190612526565b610bc2565b6040516104289190612575565b60405180910390f35b61044b60048036038101906104469190612455565b610bf1565b60405161045891906121a1565b60405180910390f35b610469610c5b565b6040516104769190612255565b60405180910390f35b610487610ced565b604051610494919061243a565b60405180910390f35b6104b760048036038101906104b2919061230b565b610cf4565b6040516104c491906121a1565b60405180910390f35b6104e760048036038101906104e2919061230b565b610ddf565b6040516104f491906121a1565b60405180910390f35b610517600480360381019061051291906123fe565b610e22565b604051610524919061235a565b60405180910390f35b610535610e46565b604051610542919061243a565b60405180910390f35b61056560048036038101906105609190612455565b610e6a565b005b610581600480360381019061057c9190612590565b610e93565b60405161058e919061235a565b60405180910390f35b61059f610f1a565b6040516105ac919061243a565b60405180910390f35b60007f5a05180f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806106285750610627826110ab565b5b9050919050565b60606005805461063e906125ff565b80601f016020809104026020016040519081016040528092919081815260200182805461066a906125ff565b80156106b75780601f1061068c576101008083540402835291602001916106b7565b820191906000526020600020905b81548152906001019060200180831161069a57829003601f168201915b5050505050905090565b60006106d56106ce611125565b848461112d565b6001905092915050565b6000600454905090565b60006106f68484846112f8565b6000600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610741611125565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050828110156107c1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b8906126a3565b60405180910390fd5b6107d5856107cd611125565b85840361112d565b60019150509392505050565b6000806000838152602001908152602001600020600101549050919050565b610809826107e1565b61081a81610815611125565b61157c565b6108248383611619565b505050565b60006012905090565b61083a611125565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146108a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161089e90612735565b60405180910390fd5b6108b1828261164d565b5050565b60006109576108c2611125565b8484600360006108d0611125565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546109529190612784565b61112d565b6001905092915050565b6109927f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a61098d611125565b610bf1565b6109d1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109c89061284c565b60405180910390fd5b6109d9611681565b565b610a0c7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6610a07611125565b610bf1565b610a4b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a42906128de565b60405180910390fd5b610a558282611723565b5050565b610a6a610a64611125565b82611884565b50565b6000600760009054906101000a900460ff16905090565b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6000610ae083610adb611125565b610e93565b905081811015610b25576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b1c90612970565b60405180910390fd5b610b3983610b31611125565b84840361112d565b610b438383611884565b505050565b610b797f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610b74611125565b610bf1565b610bb8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610baf90612a02565b60405180910390fd5b610bc0611a5d565b565b6000610be98260016000868152602001908152602001600020611b0090919063ffffffff16565b905092915050565b600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b606060068054610c6a906125ff565b80601f0160208091040260200160405190810160405280929190818152602001828054610c96906125ff565b8015610ce35780601f10610cb857610100808354040283529160200191610ce3565b820191906000526020600020905b815481529060010190602001808311610cc657829003601f168201915b5050505050905090565b6000801b81565b60008060036000610d03611125565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082811015610dc0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610db790612a94565b60405180910390fd5b610dd4610dcb611125565b8585840361112d565b600191505092915050565b6000610e1083600760019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660085461112d565b610e1a8383611b1a565b905092915050565b6000610e3f60016000848152602001908152602001600020611b38565b9050919050565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b610e73826107e1565b610e8481610e7f611125565b61157c565b610e8e838361164d565b505050565b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b610f488282610bf1565b61101a57600160008084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550610fbf611125565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b6000611046836000018373ffffffffffffffffffffffffffffffffffffffff1660001b611b4d565b905092915050565b6110598383836110a6565b611061610a6d565b156110a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161109890612b26565b60405180910390fd5b505050565b505050565b60007f7965db0b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061111e575061111d82611bbd565b5b9050919050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561119d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161119490612bb8565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561120d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161120490612c4a565b60405180910390fd5b80600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516112eb919061235a565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611368576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161135f90612cdc565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156113d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113cf90612d6e565b60405180910390fd5b6113e3838383611c27565b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561146a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161146190612e00565b60405180910390fd5b818103600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546114ff9190612784565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051611563919061235a565b60405180910390a3611576848484611c37565b50505050565b6115868282610bf1565b611615576115ab8173ffffffffffffffffffffffffffffffffffffffff166014611c3c565b6115b98360001c6020611c3c565b6040516020016115ca929190612ef4565b6040516020818303038152906040526040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161160c9190612255565b60405180910390fd5b5050565b6116238282610f3e565b611648816001600085815260200190815260200160002061101e90919063ffffffff16565b505050565b6116578282611e78565b61167c8160016000858152602001908152602001600020611f5990919063ffffffff16565b505050565b611689610a6d565b6116c8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116bf90612f7a565b60405180910390fd5b6000600760006101000a81548160ff0219169083151502179055507f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa61170c611125565b6040516117199190612575565b60405180910390a1565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611793576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161178a90612fe6565b60405180910390fd5b61179f60008383611c27565b80600460008282546117b19190612784565b9250508190555080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546118079190612784565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161186c919061235a565b60405180910390a361188060008383611c37565b5050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156118f4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118eb90613078565b60405180910390fd5b61190082600083611c27565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015611987576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161197e9061310a565b60405180910390fd5b818103600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600460008282546119df919061312a565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051611a44919061235a565b60405180910390a3611a5883600084611c37565b505050565b611a65610a6d565b15611aa5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a9c906131aa565b60405180910390fd5b6001600760006101000a81548160ff0219169083151502179055507f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258611ae9611125565b604051611af69190612575565b60405180910390a1565b6000611b0f8360000183611f89565b60001c905092915050565b6000611b2e611b27611125565b84846112f8565b6001905092915050565b6000611b4682600001611fb4565b9050919050565b6000611b598383611fc5565b611bb2578260000182908060018154018082558091505060019003906000526020600020016000909190919091505582600001805490508360010160008481526020019081526020016000208190555060019050611bb7565b600090505b92915050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b611c3283838361104e565b505050565b505050565b606060006002836002611c4f91906131ca565b611c599190612784565b67ffffffffffffffff811115611c7257611c71613224565b5b6040519080825280601f01601f191660200182016040528015611ca45781602001600182028036833780820191505090505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110611cdc57611cdb613253565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110611d4057611d3f613253565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006001846002611d8091906131ca565b611d8a9190612784565b90505b6001811115611e2a577f3031323334353637383961626364656600000000000000000000000000000000600f861660108110611dcc57611dcb613253565b5b1a60f81b828281518110611de357611de2613253565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600485901c945080611e2390613282565b9050611d8d565b5060008414611e6e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e65906132f8565b60405180910390fd5b8091505092915050565b611e828282610bf1565b15611f5557600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550611efa611125565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45b5050565b6000611f81836000018373ffffffffffffffffffffffffffffffffffffffff1660001b611fe8565b905092915050565b6000826000018281548110611fa157611fa0613253565b5b9060005260206000200154905092915050565b600081600001805490509050919050565b600080836001016000848152602001908152602001600020541415905092915050565b600080836001016000848152602001908152602001600020549050600081146120f057600060018261201a919061312a565b9050600060018660000180549050612032919061312a565b90508181146120a157600086600001828154811061205357612052613253565b5b906000526020600020015490508087600001848154811061207757612076613253565b5b90600052602060002001819055508387600101600083815260200190815260200160002081905550505b856000018054806120b5576120b4613318565b5b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506120f6565b60009150505b92915050565b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61213681612101565b811461214157600080fd5b50565b6000813590506121538161212d565b92915050565b60006020828403121561216f5761216e6120fc565b5b600061217d84828501612144565b91505092915050565b60008115159050919050565b61219b81612186565b82525050565b60006020820190506121b66000830184612192565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156121f65780820151818401526020810190506121db565b83811115612205576000848401525b50505050565b6000601f19601f8301169050919050565b6000612227826121bc565b61223181856121c7565b93506122418185602086016121d8565b61224a8161220b565b840191505092915050565b6000602082019050818103600083015261226f818461221c565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006122a282612277565b9050919050565b6122b281612297565b81146122bd57600080fd5b50565b6000813590506122cf816122a9565b92915050565b6000819050919050565b6122e8816122d5565b81146122f357600080fd5b50565b600081359050612305816122df565b92915050565b60008060408385031215612322576123216120fc565b5b6000612330858286016122c0565b9250506020612341858286016122f6565b9150509250929050565b612354816122d5565b82525050565b600060208201905061236f600083018461234b565b92915050565b60008060006060848603121561238e5761238d6120fc565b5b600061239c868287016122c0565b93505060206123ad868287016122c0565b92505060406123be868287016122f6565b9150509250925092565b6000819050919050565b6123db816123c8565b81146123e657600080fd5b50565b6000813590506123f8816123d2565b92915050565b600060208284031215612414576124136120fc565b5b6000612422848285016123e9565b91505092915050565b612434816123c8565b82525050565b600060208201905061244f600083018461242b565b92915050565b6000806040838503121561246c5761246b6120fc565b5b600061247a858286016123e9565b925050602061248b858286016122c0565b9150509250929050565b600060ff82169050919050565b6124ab81612495565b82525050565b60006020820190506124c660008301846124a2565b92915050565b6000602082840312156124e2576124e16120fc565b5b60006124f0848285016122f6565b91505092915050565b60006020828403121561250f5761250e6120fc565b5b600061251d848285016122c0565b91505092915050565b6000806040838503121561253d5761253c6120fc565b5b600061254b858286016123e9565b925050602061255c858286016122f6565b9150509250929050565b61256f81612297565b82525050565b600060208201905061258a6000830184612566565b92915050565b600080604083850312156125a7576125a66120fc565b5b60006125b5858286016122c0565b92505060206125c6858286016122c0565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061261757607f821691505b6020821081141561262b5761262a6125d0565b5b50919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206160008201527f6c6c6f77616e6365000000000000000000000000000000000000000000000000602082015250565b600061268d6028836121c7565b915061269882612631565b604082019050919050565b600060208201905081810360008301526126bc81612680565b9050919050565b7f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560008201527f20726f6c657320666f722073656c660000000000000000000000000000000000602082015250565b600061271f602f836121c7565b915061272a826126c3565b604082019050919050565b6000602082019050818103600083015261274e81612712565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061278f826122d5565b915061279a836122d5565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156127cf576127ce612755565b5b828201905092915050565b7f45524332305072657365744d696e7465725061757365723a206d75737420686160008201527f76652070617573657220726f6c6520746f20756e706175736500000000000000602082015250565b60006128366039836121c7565b9150612841826127da565b604082019050919050565b6000602082019050818103600083015261286581612829565b9050919050565b7f45524332305072657365744d696e7465725061757365723a206d75737420686160008201527f7665206d696e74657220726f6c6520746f206d696e7400000000000000000000602082015250565b60006128c86036836121c7565b91506128d38261286c565b604082019050919050565b600060208201905081810360008301526128f7816128bb565b9050919050565b7f45524332303a206275726e20616d6f756e74206578636565647320616c6c6f7760008201527f616e636500000000000000000000000000000000000000000000000000000000602082015250565b600061295a6024836121c7565b9150612965826128fe565b604082019050919050565b600060208201905081810360008301526129898161294d565b9050919050565b7f45524332305072657365744d696e7465725061757365723a206d75737420686160008201527f76652070617573657220726f6c6520746f207061757365000000000000000000602082015250565b60006129ec6037836121c7565b91506129f782612990565b604082019050919050565b60006020820190508181036000830152612a1b816129df565b9050919050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000612a7e6025836121c7565b9150612a8982612a22565b604082019050919050565b60006020820190508181036000830152612aad81612a71565b9050919050565b7f45524332305061757361626c653a20746f6b656e207472616e7366657220776860008201527f696c652070617573656400000000000000000000000000000000000000000000602082015250565b6000612b10602a836121c7565b9150612b1b82612ab4565b604082019050919050565b60006020820190508181036000830152612b3f81612b03565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000612ba26024836121c7565b9150612bad82612b46565b604082019050919050565b60006020820190508181036000830152612bd181612b95565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000612c346022836121c7565b9150612c3f82612bd8565b604082019050919050565b60006020820190508181036000830152612c6381612c27565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b6000612cc66025836121c7565b9150612cd182612c6a565b604082019050919050565b60006020820190508181036000830152612cf581612cb9565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000612d586023836121c7565b9150612d6382612cfc565b604082019050919050565b60006020820190508181036000830152612d8781612d4b565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b6000612dea6026836121c7565b9150612df582612d8e565b604082019050919050565b60006020820190508181036000830152612e1981612ddd565b9050919050565b600081905092915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000600082015250565b6000612e61601783612e20565b9150612e6c82612e2b565b601782019050919050565b6000612e82826121bc565b612e8c8185612e20565b9350612e9c8185602086016121d8565b80840191505092915050565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000600082015250565b6000612ede601183612e20565b9150612ee982612ea8565b601182019050919050565b6000612eff82612e54565b9150612f0b8285612e77565b9150612f1682612ed1565b9150612f228284612e77565b91508190509392505050565b7f5061757361626c653a206e6f7420706175736564000000000000000000000000600082015250565b6000612f646014836121c7565b9150612f6f82612f2e565b602082019050919050565b60006020820190508181036000830152612f9381612f57565b9050919050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b6000612fd0601f836121c7565b9150612fdb82612f9a565b602082019050919050565b60006020820190508181036000830152612fff81612fc3565b9050919050565b7f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360008201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b60006130626021836121c7565b915061306d82613006565b604082019050919050565b6000602082019050818103600083015261309181613055565b9050919050565b7f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60008201527f6365000000000000000000000000000000000000000000000000000000000000602082015250565b60006130f46022836121c7565b91506130ff82613098565b604082019050919050565b60006020820190508181036000830152613123816130e7565b9050919050565b6000613135826122d5565b9150613140836122d5565b92508282101561315357613152612755565b5b828203905092915050565b7f5061757361626c653a2070617573656400000000000000000000000000000000600082015250565b60006131946010836121c7565b915061319f8261315e565b602082019050919050565b600060208201905081810360008301526131c381613187565b9050919050565b60006131d5826122d5565b91506131e0836122d5565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561321957613218612755565b5b828202905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600061328d826122d5565b915060008214156132a1576132a0612755565b5b600182039050919050565b7f537472696e67733a20686578206c656e67746820696e73756666696369656e74600082015250565b60006132e26020836121c7565b91506132ed826132ac565b602082019050919050565b60006020820190508181036000830152613311816132d5565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea26469706673582212206d1717d0528164934ac2de7f8d8135302ae264585aa9725517927da5e3b2464e64736f6c63430008090033",
+  "contractName": "ERC20MaliciousDelayed"
+}
diff --git a/contracts/compiled_contracts/ERC20MinterBurnerDecimals.json b/contracts/compiled_contracts/ERC20MinterBurnerDecimals.json
new file mode 100644
index 00000000..84955e5c
--- /dev/null
+++ b/contracts/compiled_contracts/ERC20MinterBurnerDecimals.json
@@ -0,0 +1,5 @@
+{
+  "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\":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\":[],\"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\":[],\"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\":[],\"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\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"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\"}]",
+  "bin": "60806040523480156200001157600080fd5b5060405162003ca338038062003ca38339818101604052810190620000379190620006c8565b82828160059080519060200190620000519291906200043d565b5080600690805190602001906200006a9291906200043d565b5050506000600760006101000a81548160ff021916908315150217905550620000ac6000801b620000a06200018960201b60201c565b6200019160201b60201c565b620000ed7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6620000e16200018960201b60201c565b6200019160201b60201c565b6200012e7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a620001226200018960201b60201c565b6200019160201b60201c565b6200016f7f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a848620001636200018960201b60201c565b6200019160201b60201c565b6200018081620001a760201b60201c565b505050620007c7565b600033905090565b620001a38282620001c560201b60201c565b5050565b80600760016101000a81548160ff021916908360ff16021790555050565b620001dc82826200020d60201b620010191760201c565b620002088160016000858152602001908152602001600020620002fe60201b620010f91790919060201c565b505050565b6200021f82826200033660201b60201c565b620002fa57600160008084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506200029f6200018960201b60201c565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b60006200032e836000018373ffffffffffffffffffffffffffffffffffffffff1660001b620003a060201b60201c565b905092915050565b600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b6000620003b483836200041a60201b60201c565b6200040f57826000018290806001815401808255809150506001900390600052602060002001600090919091909150558260000180549050836001016000848152602001908152602001600020819055506001905062000414565b600090505b92915050565b600080836001016000848152602001908152602001600020541415905092915050565b8280546200044b9062000791565b90600052602060002090601f0160209004810192826200046f5760008555620004bb565b82601f106200048a57805160ff1916838001178555620004bb565b82800160010185558215620004bb579182015b82811115620004ba5782518255916020019190600101906200049d565b5b509050620004ca9190620004ce565b5090565b5b80821115620004e9576000816000905550600101620004cf565b5090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b62000556826200050b565b810181811067ffffffffffffffff821117156200057857620005776200051c565b5b80604052505050565b60006200058d620004ed565b90506200059b82826200054b565b919050565b600067ffffffffffffffff821115620005be57620005bd6200051c565b5b620005c9826200050b565b9050602081019050919050565b60005b83811015620005f6578082015181840152602081019050620005d9565b8381111562000606576000848401525b50505050565b6000620006236200061d84620005a0565b62000581565b90508281526020810184848401111562000642576200064162000506565b5b6200064f848285620005d6565b509392505050565b600082601f8301126200066f576200066e62000501565b5b8151620006818482602086016200060c565b91505092915050565b600060ff82169050919050565b620006a2816200068a565b8114620006ae57600080fd5b50565b600081519050620006c28162000697565b92915050565b600080600060608486031215620006e457620006e3620004f7565b5b600084015167ffffffffffffffff811115620007055762000704620004fc565b5b620007138682870162000657565b935050602084015167ffffffffffffffff811115620007375762000736620004fc565b5b620007458682870162000657565b92505060406200075886828701620006b1565b9150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620007aa57607f821691505b60208210811415620007c157620007c062000762565b5b50919050565b6134cc80620007d76000396000f3fe608060405234801561001057600080fd5b50600436106101da5760003560e01c80635c975abb11610104578063a217fddf116100a2578063d539139311610071578063d53913931461057d578063d547741f1461059b578063dd62ed3e146105b7578063e63ab1e9146105e7576101da565b8063a217fddf146104cf578063a457c2d7146104ed578063a9059cbb1461051d578063ca15c8731461054d576101da565b80638456cb59116100de5780638456cb59146104475780639010d07c1461045157806391d148541461048157806395d89b41146104b1576101da565b80635c975abb146103dd57806370a08231146103fb57806379cc67901461042b576101da565b8063282c51f31161017c578063395093511161014b578063395093511461036b5780633f4ba83a1461039b57806340c10f19146103a557806342966c68146103c1576101da565b8063282c51f3146102f75780632f2ff15d14610315578063313ce5671461033157806336568abe1461034f576101da565b806318160ddd116101b857806318160ddd1461025d5780631cf2c7e21461027b57806323b872dd14610297578063248a9ca3146102c7576101da565b806301ffc9a7146101df57806306fdde031461020f578063095ea7b31461022d575b600080fd5b6101f960048036038101906101f49190612216565b610605565b604051610206919061225e565b60405180910390f35b61021761067f565b6040516102249190612312565b60405180910390f35b610247600480360381019061024291906123c8565b610711565b604051610254919061225e565b60405180910390f35b61026561072f565b6040516102729190612417565b60405180910390f35b610295600480360381019061029091906123c8565b610739565b005b6102b160048036038101906102ac9190612432565b6107b7565b6040516102be919061225e565b60405180910390f35b6102e160048036038101906102dc91906124bb565b6108af565b6040516102ee91906124f7565b60405180910390f35b6102ff6108ce565b60405161030c91906124f7565b60405180910390f35b61032f600480360381019061032a9190612512565b6108f2565b005b61033961091b565b604051610346919061256e565b60405180910390f35b61036960048036038101906103649190612512565b610932565b005b610385600480360381019061038091906123c8565b6109b5565b604051610392919061225e565b60405180910390f35b6103a3610a61565b005b6103bf60048036038101906103ba91906123c8565b610adb565b005b6103db60048036038101906103d69190612589565b610b59565b005b6103e5610b6d565b6040516103f2919061225e565b60405180910390f35b610415600480360381019061041091906125b6565b610b84565b6040516104229190612417565b60405180910390f35b610445600480360381019061044091906123c8565b610bcd565b005b61044f610c48565b005b61046b600480360381019061046691906125e3565b610cc2565b6040516104789190612632565b60405180910390f35b61049b60048036038101906104969190612512565b610cf1565b6040516104a8919061225e565b60405180910390f35b6104b9610d5b565b6040516104c69190612312565b60405180910390f35b6104d7610ded565b6040516104e491906124f7565b60405180910390f35b610507600480360381019061050291906123c8565b610df4565b604051610514919061225e565b60405180910390f35b610537600480360381019061053291906123c8565b610edf565b604051610544919061225e565b60405180910390f35b610567600480360381019061056291906124bb565b610efd565b6040516105749190612417565b60405180910390f35b610585610f21565b60405161059291906124f7565b60405180910390f35b6105b560048036038101906105b09190612512565b610f45565b005b6105d160048036038101906105cc919061264d565b610f6e565b6040516105de9190612417565b60405180910390f35b6105ef610ff5565b6040516105fc91906124f7565b60405180910390f35b60007f5a05180f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480610678575061067782611129565b5b9050919050565b60606005805461068e906126bc565b80601f01602080910402602001604051908101604052809291908181526020018280546106ba906126bc565b80156107075780601f106106dc57610100808354040283529160200191610707565b820191906000526020600020905b8154815290600101906020018083116106ea57829003601f168201915b5050505050905090565b600061072561071e6111a3565b84846111ab565b6001905092915050565b6000600454905090565b61076a7f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a8486107656111a3565b610cf1565b6107a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107a090612760565b60405180910390fd5b6107b38282611376565b5050565b60006107c484848461154f565b6000600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600061080f6111a3565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508281101561088f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610886906127f2565b60405180910390fd5b6108a38561089b6111a3565b8584036111ab565b60019150509392505050565b6000806000838152602001908152602001600020600101549050919050565b7f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a84881565b6108fb826108af565b61090c816109076111a3565b6117d3565b6109168383611870565b505050565b6000600760019054906101000a900460ff16905090565b61093a6111a3565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146109a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161099e90612884565b60405180910390fd5b6109b182826118a4565b5050565b6000610a576109c26111a3565b8484600360006109d06111a3565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610a5291906128d3565b6111ab565b6001905092915050565b610a927f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610a8d6111a3565b610cf1565b610ad1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ac89061299b565b60405180910390fd5b610ad96118d8565b565b610b0c7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6610b076111a3565b610cf1565b610b4b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b4290612a2d565b60405180910390fd5b610b55828261197a565b5050565b610b6a610b646111a3565b82611376565b50565b6000600760009054906101000a900460ff16905090565b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6000610be083610bdb6111a3565b610f6e565b905081811015610c25576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c1c90612abf565b60405180910390fd5b610c3983610c316111a3565b8484036111ab565b610c438383611376565b505050565b610c797f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610c746111a3565b610cf1565b610cb8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610caf90612b51565b60405180910390fd5b610cc0611adb565b565b6000610ce98260016000868152602001908152602001600020611b7e90919063ffffffff16565b905092915050565b600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b606060068054610d6a906126bc565b80601f0160208091040260200160405190810160405280929190818152602001828054610d96906126bc565b8015610de35780601f10610db857610100808354040283529160200191610de3565b820191906000526020600020905b815481529060010190602001808311610dc657829003601f168201915b5050505050905090565b6000801b81565b60008060036000610e036111a3565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082811015610ec0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610eb790612be3565b60405180910390fd5b610ed4610ecb6111a3565b858584036111ab565b600191505092915050565b6000610ef3610eec6111a3565b848461154f565b6001905092915050565b6000610f1a60016000848152602001908152602001600020611b98565b9050919050565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b610f4e826108af565b610f5f81610f5a6111a3565b6117d3565b610f6983836118a4565b505050565b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b6110238282610cf1565b6110f557600160008084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555061109a6111a3565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b6000611121836000018373ffffffffffffffffffffffffffffffffffffffff1660001b611bad565b905092915050565b60007f7965db0b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061119c575061119b82611c1d565b5b9050919050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561121b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161121290612c75565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561128b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161128290612d07565b60405180910390fd5b80600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516113699190612417565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156113e6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113dd90612d99565b60405180910390fd5b6113f282600083611c87565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015611479576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161147090612e2b565b60405180910390fd5b818103600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600460008282546114d19190612e4b565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516115369190612417565b60405180910390a361154a83600084611c97565b505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156115bf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115b690612ef1565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561162f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161162690612f83565b60405180910390fd5b61163a838383611c87565b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050818110156116c1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116b890613015565b60405180910390fd5b818103600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461175691906128d3565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516117ba9190612417565b60405180910390a36117cd848484611c97565b50505050565b6117dd8282610cf1565b61186c576118028173ffffffffffffffffffffffffffffffffffffffff166014611c9c565b6118108360001c6020611c9c565b604051602001611821929190613109565b6040516020818303038152906040526040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118639190612312565b60405180910390fd5b5050565b61187a8282611019565b61189f81600160008581526020019081526020016000206110f990919063ffffffff16565b505050565b6118ae8282611ed8565b6118d38160016000858152602001908152602001600020611fb990919063ffffffff16565b505050565b6118e0610b6d565b61191f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119169061318f565b60405180910390fd5b6000600760006101000a81548160ff0219169083151502179055507f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6119636111a3565b6040516119709190612632565b60405180910390a1565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156119ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119e1906131fb565b60405180910390fd5b6119f660008383611c87565b8060046000828254611a0891906128d3565b9250508190555080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611a5e91906128d3565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051611ac39190612417565b60405180910390a3611ad760008383611c97565b5050565b611ae3610b6d565b15611b23576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b1a90613267565b60405180910390fd5b6001600760006101000a81548160ff0219169083151502179055507f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258611b676111a3565b604051611b749190612632565b60405180910390a1565b6000611b8d8360000183611fe9565b60001c905092915050565b6000611ba682600001612014565b9050919050565b6000611bb98383612025565b611c12578260000182908060018154018082558091505060019003906000526020600020016000909190919091505582600001805490508360010160008481526020019081526020016000208190555060019050611c17565b600090505b92915050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b611c92838383612048565b505050565b505050565b606060006002836002611caf9190613287565b611cb991906128d3565b67ffffffffffffffff811115611cd257611cd16132e1565b5b6040519080825280601f01601f191660200182016040528015611d045781602001600182028036833780820191505090505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110611d3c57611d3b613310565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110611da057611d9f613310565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006001846002611de09190613287565b611dea91906128d3565b90505b6001811115611e8a577f3031323334353637383961626364656600000000000000000000000000000000600f861660108110611e2c57611e2b613310565b5b1a60f81b828281518110611e4357611e42613310565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600485901c945080611e839061333f565b9050611ded565b5060008414611ece576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611ec5906133b5565b60405180910390fd5b8091505092915050565b611ee28282610cf1565b15611fb557600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550611f5a6111a3565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45b5050565b6000611fe1836000018373ffffffffffffffffffffffffffffffffffffffff1660001b6120a0565b905092915050565b600082600001828154811061200157612000613310565b5b9060005260206000200154905092915050565b600081600001805490509050919050565b600080836001016000848152602001908152602001600020541415905092915050565b6120538383836121b4565b61205b610b6d565b1561209b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161209290613447565b60405180910390fd5b505050565b600080836001016000848152602001908152602001600020549050600081146121a85760006001826120d29190612e4b565b90506000600186600001805490506120ea9190612e4b565b905081811461215957600086600001828154811061210b5761210a613310565b5b906000526020600020015490508087600001848154811061212f5761212e613310565b5b90600052602060002001819055508387600101600083815260200190815260200160002081905550505b8560000180548061216d5761216c613467565b5b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506121ae565b60009150505b92915050565b505050565b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6121f3816121be565b81146121fe57600080fd5b50565b600081359050612210816121ea565b92915050565b60006020828403121561222c5761222b6121b9565b5b600061223a84828501612201565b91505092915050565b60008115159050919050565b61225881612243565b82525050565b6000602082019050612273600083018461224f565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156122b3578082015181840152602081019050612298565b838111156122c2576000848401525b50505050565b6000601f19601f8301169050919050565b60006122e482612279565b6122ee8185612284565b93506122fe818560208601612295565b612307816122c8565b840191505092915050565b6000602082019050818103600083015261232c81846122d9565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061235f82612334565b9050919050565b61236f81612354565b811461237a57600080fd5b50565b60008135905061238c81612366565b92915050565b6000819050919050565b6123a581612392565b81146123b057600080fd5b50565b6000813590506123c28161239c565b92915050565b600080604083850312156123df576123de6121b9565b5b60006123ed8582860161237d565b92505060206123fe858286016123b3565b9150509250929050565b61241181612392565b82525050565b600060208201905061242c6000830184612408565b92915050565b60008060006060848603121561244b5761244a6121b9565b5b60006124598682870161237d565b935050602061246a8682870161237d565b925050604061247b868287016123b3565b9150509250925092565b6000819050919050565b61249881612485565b81146124a357600080fd5b50565b6000813590506124b58161248f565b92915050565b6000602082840312156124d1576124d06121b9565b5b60006124df848285016124a6565b91505092915050565b6124f181612485565b82525050565b600060208201905061250c60008301846124e8565b92915050565b60008060408385031215612529576125286121b9565b5b6000612537858286016124a6565b92505060206125488582860161237d565b9150509250929050565b600060ff82169050919050565b61256881612552565b82525050565b6000602082019050612583600083018461255f565b92915050565b60006020828403121561259f5761259e6121b9565b5b60006125ad848285016123b3565b91505092915050565b6000602082840312156125cc576125cb6121b9565b5b60006125da8482850161237d565b91505092915050565b600080604083850312156125fa576125f96121b9565b5b6000612608858286016124a6565b9250506020612619858286016123b3565b9150509250929050565b61262c81612354565b82525050565b60006020820190506126476000830184612623565b92915050565b60008060408385031215612664576126636121b9565b5b60006126728582860161237d565b92505060206126838582860161237d565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806126d457607f821691505b602082108114156126e8576126e761268d565b5b50919050565b7f45524332304d696e7465724275726e6572446563696d616c733a206d7573742060008201527f68617665206275726e657220726f6c6520746f206275726e0000000000000000602082015250565b600061274a603883612284565b9150612755826126ee565b604082019050919050565b600060208201905081810360008301526127798161273d565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206160008201527f6c6c6f77616e6365000000000000000000000000000000000000000000000000602082015250565b60006127dc602883612284565b91506127e782612780565b604082019050919050565b6000602082019050818103600083015261280b816127cf565b9050919050565b7f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560008201527f20726f6c657320666f722073656c660000000000000000000000000000000000602082015250565b600061286e602f83612284565b915061287982612812565b604082019050919050565b6000602082019050818103600083015261289d81612861565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006128de82612392565b91506128e983612392565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561291e5761291d6128a4565b5b828201905092915050565b7f45524332304d696e7465724275726e6572446563696d616c733a206d7573742060008201527f686176652070617573657220726f6c6520746f20756e70617573650000000000602082015250565b6000612985603b83612284565b915061299082612929565b604082019050919050565b600060208201905081810360008301526129b481612978565b9050919050565b7f45524332304d696e7465724275726e6572446563696d616c733a206d7573742060008201527f68617665206d696e74657220726f6c6520746f206d696e740000000000000000602082015250565b6000612a17603883612284565b9150612a22826129bb565b604082019050919050565b60006020820190508181036000830152612a4681612a0a565b9050919050565b7f45524332303a206275726e20616d6f756e74206578636565647320616c6c6f7760008201527f616e636500000000000000000000000000000000000000000000000000000000602082015250565b6000612aa9602483612284565b9150612ab482612a4d565b604082019050919050565b60006020820190508181036000830152612ad881612a9c565b9050919050565b7f45524332304d696e7465724275726e6572446563696d616c733a206d7573742060008201527f686176652070617573657220726f6c6520746f20706175736500000000000000602082015250565b6000612b3b603983612284565b9150612b4682612adf565b604082019050919050565b60006020820190508181036000830152612b6a81612b2e565b9050919050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000612bcd602583612284565b9150612bd882612b71565b604082019050919050565b60006020820190508181036000830152612bfc81612bc0565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000612c5f602483612284565b9150612c6a82612c03565b604082019050919050565b60006020820190508181036000830152612c8e81612c52565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000612cf1602283612284565b9150612cfc82612c95565b604082019050919050565b60006020820190508181036000830152612d2081612ce4565b9050919050565b7f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360008201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b6000612d83602183612284565b9150612d8e82612d27565b604082019050919050565b60006020820190508181036000830152612db281612d76565b9050919050565b7f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60008201527f6365000000000000000000000000000000000000000000000000000000000000602082015250565b6000612e15602283612284565b9150612e2082612db9565b604082019050919050565b60006020820190508181036000830152612e4481612e08565b9050919050565b6000612e5682612392565b9150612e6183612392565b925082821015612e7457612e736128a4565b5b828203905092915050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b6000612edb602583612284565b9150612ee682612e7f565b604082019050919050565b60006020820190508181036000830152612f0a81612ece565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000612f6d602383612284565b9150612f7882612f11565b604082019050919050565b60006020820190508181036000830152612f9c81612f60565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b6000612fff602683612284565b915061300a82612fa3565b604082019050919050565b6000602082019050818103600083015261302e81612ff2565b9050919050565b600081905092915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000600082015250565b6000613076601783613035565b915061308182613040565b601782019050919050565b600061309782612279565b6130a18185613035565b93506130b1818560208601612295565b80840191505092915050565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000600082015250565b60006130f3601183613035565b91506130fe826130bd565b601182019050919050565b600061311482613069565b9150613120828561308c565b915061312b826130e6565b9150613137828461308c565b91508190509392505050565b7f5061757361626c653a206e6f7420706175736564000000000000000000000000600082015250565b6000613179601483612284565b915061318482613143565b602082019050919050565b600060208201905081810360008301526131a88161316c565b9050919050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b60006131e5601f83612284565b91506131f0826131af565b602082019050919050565b60006020820190508181036000830152613214816131d8565b9050919050565b7f5061757361626c653a2070617573656400000000000000000000000000000000600082015250565b6000613251601083612284565b915061325c8261321b565b602082019050919050565b6000602082019050818103600083015261328081613244565b9050919050565b600061329282612392565b915061329d83612392565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156132d6576132d56128a4565b5b828202905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600061334a82612392565b9150600082141561335e5761335d6128a4565b5b600182039050919050565b7f537472696e67733a20686578206c656e67746820696e73756666696369656e74600082015250565b600061339f602083612284565b91506133aa82613369565b602082019050919050565b600060208201905081810360008301526133ce81613392565b9050919050565b7f45524332305061757361626c653a20746f6b656e207472616e7366657220776860008201527f696c652070617573656400000000000000000000000000000000000000000000602082015250565b6000613431602a83612284565b915061343c826133d5565b604082019050919050565b6000602082019050818103600083015261346081613424565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea2646970667358221220c3d4a4231a6c94cfb03623ea4b77df2c9ccfa487132bebf43620219e3dc2f4cf64736f6c63430008090033",
+  "contractName": "ERC20MinterBurnerDecimals"
+}
diff --git a/contracts/compiled_contracts/ProposalStore.json b/contracts/compiled_contracts/ProposalStore.json
new file mode 100644
index 00000000..821090c9
--- /dev/null
+++ b/contracts/compiled_contracts/ProposalStore.json
@@ -0,0 +1,4 @@
+{
+    "abi": "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"propId\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"title\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"desc\",\"type\":\"string\"},{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"propId\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"title\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"desc\",\"type\":\"string\"},{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"}],\"name\":\"AddProposal\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"propId\",\"type\":\"uint256\"}],\"name\":\"QueryProp\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"title\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"desc\",\"type\":\"string\"},{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"}],\"internalType\":\"struct ProposalStore.Proposal\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]","bin":"60a06040523480156200001157600080fd5b5060405162002b2238038062002b22833981810160405281019062000037919062000aa8565b3373ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff168152505060006040518060e00160405280898152602001888152602001878152602001868152602001858152602001848152602001838152509050806000808a8152602001908152602001600020600082015181600001556020820151816001019081620000d6919062000e56565b506040820151816002019081620000ee919062000e56565b5060608201518160030190805190602001906200010d9291906200017c565b5060808201518160040190805190602001906200012c9291906200020b565b5060a08201518160050190805190602001906200014b9291906200025d565b5060c08201518160060190805190602001906200016a929190620002bd565b50905050505050505050505062001094565b828054828255906000526020600020908101928215620001f8579160200282015b82811115620001f75782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906200019d565b5b5090506200020791906200031d565b5090565b8280548282559060005260206000209081019282156200024a579160200282015b82811115620002495782518255916020019190600101906200022c565b5b5090506200025991906200031d565b5090565b828054828255906000526020600020908101928215620002aa579160200282015b82811115620002a957825182908162000298919062000e56565b50916020019190600101906200027e565b5b509050620002b991906200033c565b5090565b8280548282559060005260206000209081019282156200030a579160200282015b8281111562000309578251829081620002f8919062000fad565b5091602001919060010190620002de565b5b50905062000319919062000364565b5090565b5b80821115620003385760008160009055506001016200031e565b5090565b5b808211156200036057600081816200035691906200038c565b506001016200033d565b5090565b5b808211156200038857600081816200037e9190620003d2565b5060010162000365565b5090565b5080546200039a9062000c4f565b6000825580601f10620003ae5750620003cf565b601f016020900490600052602060002090810190620003ce91906200031d565b5b50565b508054620003e09062000c4f565b6000825580601f10620003f4575062000415565b601f0160209004906000526020600020908101906200041491906200031d565b5b50565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b62000441816200042c565b81146200044d57600080fd5b50565b600081519050620004618162000436565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620004bc8262000471565b810181811067ffffffffffffffff82111715620004de57620004dd62000482565b5b80604052505050565b6000620004f362000418565b9050620005018282620004b1565b919050565b600067ffffffffffffffff82111562000524576200052362000482565b5b6200052f8262000471565b9050602081019050919050565b60005b838110156200055c5780820151818401526020810190506200053f565b838111156200056c576000848401525b50505050565b600062000589620005838462000506565b620004e7565b905082815260208101848484011115620005a857620005a76200046c565b5b620005b58482856200053c565b509392505050565b600082601f830112620005d557620005d462000467565b5b8151620005e784826020860162000572565b91505092915050565b600067ffffffffffffffff8211156200060e576200060d62000482565b5b602082029050602081019050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620006518262000624565b9050919050565b620006638162000644565b81146200066f57600080fd5b50565b600081519050620006838162000658565b92915050565b6000620006a06200069a84620005f0565b620004e7565b90508083825260208201905060208402830185811115620006c657620006c56200061f565b5b835b81811015620006f35780620006de888262000672565b845260208401935050602081019050620006c8565b5050509392505050565b600082601f83011262000715576200071462000467565b5b81516200072784826020860162000689565b91505092915050565b600067ffffffffffffffff8211156200074e576200074d62000482565b5b602082029050602081019050919050565b600062000776620007708462000730565b620004e7565b905080838252602082019050602084028301858111156200079c576200079b6200061f565b5b835b81811015620007c95780620007b4888262000450565b8452602084019350506020810190506200079e565b5050509392505050565b600082601f830112620007eb57620007ea62000467565b5b8151620007fd8482602086016200075f565b91505092915050565b600067ffffffffffffffff82111562000824576200082362000482565b5b602082029050602081019050919050565b60006200084c620008468462000806565b620004e7565b905080838252602082019050602084028301858111156200087257620008716200061f565b5b835b81811015620008c057805167ffffffffffffffff8111156200089b576200089a62000467565b5b808601620008aa8982620005bd565b8552602085019450505060208101905062000874565b5050509392505050565b600082601f830112620008e257620008e162000467565b5b8151620008f484826020860162000835565b91505092915050565b600067ffffffffffffffff8211156200091b576200091a62000482565b5b602082029050602081019050919050565b600067ffffffffffffffff8211156200094a576200094962000482565b5b620009558262000471565b9050602081019050919050565b60006200097962000973846200092c565b620004e7565b9050828152602081018484840111156200099857620009976200046c565b5b620009a58482856200053c565b509392505050565b600082601f830112620009c557620009c462000467565b5b8151620009d784826020860162000962565b91505092915050565b6000620009f7620009f184620008fd565b620004e7565b9050808382526020820190506020840283018581111562000a1d5762000a1c6200061f565b5b835b8181101562000a6b57805167ffffffffffffffff81111562000a465762000a4562000467565b5b80860162000a558982620009ad565b8552602085019450505060208101905062000a1f565b5050509392505050565b600082601f83011262000a8d5762000a8c62000467565b5b815162000a9f848260208601620009e0565b91505092915050565b600080600080600080600060e0888a03121562000aca5762000ac962000422565b5b600062000ada8a828b0162000450565b975050602088015167ffffffffffffffff81111562000afe5762000afd62000427565b5b62000b0c8a828b01620005bd565b965050604088015167ffffffffffffffff81111562000b305762000b2f62000427565b5b62000b3e8a828b01620005bd565b955050606088015167ffffffffffffffff81111562000b625762000b6162000427565b5b62000b708a828b01620006fd565b945050608088015167ffffffffffffffff81111562000b945762000b9362000427565b5b62000ba28a828b01620007d3565b93505060a088015167ffffffffffffffff81111562000bc65762000bc562000427565b5b62000bd48a828b01620008ca565b92505060c088015167ffffffffffffffff81111562000bf85762000bf762000427565b5b62000c068a828b0162000a75565b91505092959891949750929550565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168062000c6857607f821691505b60208210810362000c7e5762000c7d62000c20565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b60006008830262000ce87fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000ca9565b62000cf4868362000ca9565b95508019841693508086168417925050509392505050565b6000819050919050565b600062000d3762000d3162000d2b846200042c565b62000d0c565b6200042c565b9050919050565b6000819050919050565b62000d538362000d16565b62000d6b62000d628262000d3e565b84845462000cb6565b825550505050565b600090565b62000d8262000d73565b62000d8f81848462000d48565b505050565b5b8181101562000db75762000dab60008262000d78565b60018101905062000d95565b5050565b601f82111562000e065762000dd08162000c84565b62000ddb8462000c99565b8101602085101562000deb578190505b62000e0362000dfa8562000c99565b83018262000d94565b50505b505050565b600082821c905092915050565b600062000e2b6000198460080262000e0b565b1980831691505092915050565b600062000e46838362000e18565b9150826002028217905092915050565b62000e618262000c15565b67ffffffffffffffff81111562000e7d5762000e7c62000482565b5b62000e89825462000c4f565b62000e9682828562000dbb565b600060209050601f83116001811462000ece576000841562000eb9578287015190505b62000ec5858262000e38565b86555062000f35565b601f19841662000ede8662000c84565b60005b8281101562000f085784890151825560018201915060208501945060208101905062000ee1565b8683101562000f28578489015162000f24601f89168262000e18565b8355505b6001600288020188555050505b505050505050565b600081519050919050565b60008190508160005260206000209050919050565b601f82111562000fa85762000f728162000f48565b62000f7d8462000c99565b8101602085101562000f8d578190505b62000fa562000f9c8562000c99565b83018262000d94565b50505b505050565b62000fb88262000f3d565b67ffffffffffffffff81111562000fd45762000fd362000482565b5b62000fe0825462000c4f565b62000fed82828562000f5d565b600060209050601f83116001811462001025576000841562001010578287015190505b6200101c858262000e38565b8655506200108c565b601f198416620010358662000f48565b60005b828110156200105f5784890151825560018201915060208501945060208101905062001038565b868310156200107f57848901516200107b601f89168262000e18565b8355505b6001600288020188555050505b505050505050565b608051611a73620010af600039600060890152611a736000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806327bc123e1461003b578063f0e147b814610057575b600080fd5b61005560048036038101906100509190610ffd565b610087565b005b610071600480360381019061006c9190611147565b6101e0565b60405161007e9190611610565b60405180910390f35b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146100df57600080fd5b60006040518060e00160405280898152602001888152602001878152602001868152602001858152602001848152602001838152509050806000808a8152602001908152602001600020600082015181600001556020820151816001019081610148919061183e565b50604082015181600201908161015e919061183e565b50606082015181600301908051906020019061017b929190610776565b506080820151816004019080519060200190610198929190610800565b5060a08201518160050190805190602001906101b592919061084d565b5060c08201518160060190805190602001906101d29291906108a6565b509050505050505050505050565b6101e86108ff565b8160008084815260200190815260200160002060000154036105ee576000808381526020019081526020016000206040518060e00160405290816000820154815260200160018201805461023b90611661565b80601f016020809104026020016040519081016040528092919081815260200182805461026790611661565b80156102b45780601f10610289576101008083540402835291602001916102b4565b820191906000526020600020905b81548152906001019060200180831161029757829003601f168201915b505050505081526020016002820180546102cd90611661565b80601f01602080910402602001604051908101604052809291908181526020018280546102f990611661565b80156103465780601f1061031b57610100808354040283529160200191610346565b820191906000526020600020905b81548152906001019060200180831161032957829003601f168201915b50505050508152602001600382018054806020026020016040519081016040528092919081815260200182805480156103d457602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161038a575b505050505081526020016004820180548060200260200160405190810160405280929190818152602001828054801561042c57602002820191906000526020600020905b815481526020019060010190808311610418575b5050505050815260200160058201805480602002602001604051908101604052809291908181526020016000905b8282101561050657838290600052602060002001805461047990611661565b80601f01602080910402602001604051908101604052809291908181526020018280546104a590611661565b80156104f25780601f106104c7576101008083540402835291602001916104f2565b820191906000526020600020905b8154815290600101906020018083116104d557829003601f168201915b50505050508152602001906001019061045a565b50505050815260200160068201805480602002602001604051908101604052809291908181526020016000905b828210156105df57838290600052602060002001805461055290611661565b80601f016020809104026020016040519081016040528092919081815260200182805461057e90611661565b80156105cb5780601f106105a0576101008083540402835291602001916105cb565b820191906000526020600020905b8154815290600101906020018083116105ae57829003601f168201915b505050505081526020019060010190610533565b50505050815250509050610771565b6040518060e0016040528060008152602001604051806020016040528060008152508152602001604051806020016040528060008152508152602001600067ffffffffffffffff81111561064557610644610a86565b5b6040519080825280602002602001820160405280156106735781602001602082028036833780820191505090505b508152602001600067ffffffffffffffff81111561069457610693610a86565b5b6040519080825280602002602001820160405280156106c25781602001602082028036833780820191505090505b508152602001600067ffffffffffffffff8111156106e3576106e2610a86565b5b60405190808252806020026020018201604052801561071657816020015b60608152602001906001900390816107015790505b508152602001600067ffffffffffffffff81111561073757610736610a86565b5b60405190808252806020026020018201604052801561076a57816020015b60608152602001906001900390816107555790505b5081525090505b919050565b8280548282559060005260206000209081019282156107ef579160200282015b828111156107ee5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555091602001919060010190610796565b5b5090506107fc919061093c565b5090565b82805482825590600052602060002090810192821561083c579160200282015b8281111561083b578251825591602001919060010190610820565b5b509050610849919061093c565b5090565b828054828255906000526020600020908101928215610895579160200282015b82811115610894578251829081610884919061183e565b509160200191906001019061086d565b5b5090506108a29190610959565b5090565b8280548282559060005260206000209081019282156108ee579160200282015b828111156108ed5782518290816108dd919061196b565b50916020019190600101906108c6565b5b5090506108fb919061097d565b5090565b6040518060e00160405280600081526020016060815260200160608152602001606081526020016060815260200160608152602001606081525090565b5b8082111561095557600081600090555060010161093d565b5090565b5b80821115610979576000818161097091906109a1565b5060010161095a565b5090565b5b8082111561099d576000818161099491906109e1565b5060010161097e565b5090565b5080546109ad90611661565b6000825580601f106109bf57506109de565b601f0160209004906000526020600020908101906109dd919061093c565b5b50565b5080546109ed90611661565b6000825580601f106109ff5750610a1e565b601f016020900490600052602060002090810190610a1d919061093c565b5b50565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b610a4881610a35565b8114610a5357600080fd5b50565b600081359050610a6581610a3f565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610abe82610a75565b810181811067ffffffffffffffff82111715610add57610adc610a86565b5b80604052505050565b6000610af0610a21565b9050610afc8282610ab5565b919050565b600067ffffffffffffffff821115610b1c57610b1b610a86565b5b610b2582610a75565b9050602081019050919050565b82818337600083830152505050565b6000610b54610b4f84610b01565b610ae6565b905082815260208101848484011115610b7057610b6f610a70565b5b610b7b848285610b32565b509392505050565b600082601f830112610b9857610b97610a6b565b5b8135610ba8848260208601610b41565b91505092915050565b600067ffffffffffffffff821115610bcc57610bcb610a86565b5b602082029050602081019050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610c0d82610be2565b9050919050565b610c1d81610c02565b8114610c2857600080fd5b50565b600081359050610c3a81610c14565b92915050565b6000610c53610c4e84610bb1565b610ae6565b90508083825260208201905060208402830185811115610c7657610c75610bdd565b5b835b81811015610c9f5780610c8b8882610c2b565b845260208401935050602081019050610c78565b5050509392505050565b600082601f830112610cbe57610cbd610a6b565b5b8135610cce848260208601610c40565b91505092915050565b600067ffffffffffffffff821115610cf257610cf1610a86565b5b602082029050602081019050919050565b6000610d16610d1184610cd7565b610ae6565b90508083825260208201905060208402830185811115610d3957610d38610bdd565b5b835b81811015610d625780610d4e8882610a56565b845260208401935050602081019050610d3b565b5050509392505050565b600082601f830112610d8157610d80610a6b565b5b8135610d91848260208601610d03565b91505092915050565b600067ffffffffffffffff821115610db557610db4610a86565b5b602082029050602081019050919050565b6000610dd9610dd484610d9a565b610ae6565b90508083825260208201905060208402830185811115610dfc57610dfb610bdd565b5b835b81811015610e4357803567ffffffffffffffff811115610e2157610e20610a6b565b5b808601610e2e8982610b83565b85526020850194505050602081019050610dfe565b5050509392505050565b600082601f830112610e6257610e61610a6b565b5b8135610e72848260208601610dc6565b91505092915050565b600067ffffffffffffffff821115610e9657610e95610a86565b5b602082029050602081019050919050565b600067ffffffffffffffff821115610ec257610ec1610a86565b5b610ecb82610a75565b9050602081019050919050565b6000610eeb610ee684610ea7565b610ae6565b905082815260208101848484011115610f0757610f06610a70565b5b610f12848285610b32565b509392505050565b600082601f830112610f2f57610f2e610a6b565b5b8135610f3f848260208601610ed8565b91505092915050565b6000610f5b610f5684610e7b565b610ae6565b90508083825260208201905060208402830185811115610f7e57610f7d610bdd565b5b835b81811015610fc557803567ffffffffffffffff811115610fa357610fa2610a6b565b5b808601610fb08982610f1a565b85526020850194505050602081019050610f80565b5050509392505050565b600082601f830112610fe457610fe3610a6b565b5b8135610ff4848260208601610f48565b91505092915050565b600080600080600080600060e0888a03121561101c5761101b610a2b565b5b600061102a8a828b01610a56565b975050602088013567ffffffffffffffff81111561104b5761104a610a30565b5b6110578a828b01610b83565b965050604088013567ffffffffffffffff81111561107857611077610a30565b5b6110848a828b01610b83565b955050606088013567ffffffffffffffff8111156110a5576110a4610a30565b5b6110b18a828b01610ca9565b945050608088013567ffffffffffffffff8111156110d2576110d1610a30565b5b6110de8a828b01610d6c565b93505060a088013567ffffffffffffffff8111156110ff576110fe610a30565b5b61110b8a828b01610e4d565b92505060c088013567ffffffffffffffff81111561112c5761112b610a30565b5b6111388a828b01610fcf565b91505092959891949750929550565b60006020828403121561115d5761115c610a2b565b5b600061116b84828501610a56565b91505092915050565b61117d81610a35565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b838110156111bd5780820151818401526020810190506111a2565b838111156111cc576000848401525b50505050565b60006111dd82611183565b6111e7818561118e565b93506111f781856020860161119f565b61120081610a75565b840191505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b61124081610c02565b82525050565b60006112528383611237565b60208301905092915050565b6000602082019050919050565b60006112768261120b565b6112808185611216565b935061128b83611227565b8060005b838110156112bc5781516112a38882611246565b97506112ae8361125e565b92505060018101905061128f565b5085935050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60006113018383611174565b60208301905092915050565b6000602082019050919050565b6000611325826112c9565b61132f81856112d4565b935061133a836112e5565b8060005b8381101561136b57815161135288826112f5565b975061135d8361130d565b92505060018101905061133e565b5085935050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60006113b083836111d2565b905092915050565b6000602082019050919050565b60006113d082611378565b6113da8185611383565b9350836020820285016113ec85611394565b8060005b85811015611428578484038952815161140985826113a4565b9450611414836113b8565b925060208a019950506001810190506113f0565b50829750879550505050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600081519050919050565b600082825260208201905092915050565b600061148d82611466565b6114978185611471565b93506114a781856020860161119f565b6114b081610a75565b840191505092915050565b60006114c78383611482565b905092915050565b6000602082019050919050565b60006114e78261143a565b6114f18185611445565b93508360208202850161150385611456565b8060005b8581101561153f578484038952815161152085826114bb565b945061152b836114cf565b925060208a01995050600181019050611507565b50829750879550505050505092915050565b600060e0830160008301516115696000860182611174565b506020830151848203602086015261158182826111d2565b9150506040830151848203604086015261159b82826111d2565b915050606083015184820360608601526115b5828261126b565b915050608083015184820360808601526115cf828261131a565b91505060a083015184820360a08601526115e982826113c5565b91505060c083015184820360c086015261160382826114dc565b9150508091505092915050565b6000602082019050818103600083015261162a8184611551565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061167957607f821691505b60208210810361168c5761168b611632565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026116f47fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff826116b7565b6116fe86836116b7565b95508019841693508086168417925050509392505050565b6000819050919050565b600061173b61173661173184610a35565b611716565b610a35565b9050919050565b6000819050919050565b61175583611720565b61176961176182611742565b8484546116c4565b825550505050565b600090565b61177e611771565b61178981848461174c565b505050565b5b818110156117ad576117a2600082611776565b60018101905061178f565b5050565b601f8211156117f2576117c381611692565b6117cc846116a7565b810160208510156117db578190505b6117ef6117e7856116a7565b83018261178e565b50505b505050565b600082821c905092915050565b6000611815600019846008026117f7565b1980831691505092915050565b600061182e8383611804565b9150826002028217905092915050565b61184782611183565b67ffffffffffffffff8111156118605761185f610a86565b5b61186a8254611661565b6118758282856117b1565b600060209050601f8311600181146118a85760008415611896578287015190505b6118a08582611822565b865550611908565b601f1984166118b686611692565b60005b828110156118de578489015182556001820191506020850194506020810190506118b9565b868310156118fb57848901516118f7601f891682611804565b8355505b6001600288020188555050505b505050505050565b60008190508160005260206000209050919050565b601f8211156119665761193781611910565b611940846116a7565b8101602085101561194f578190505b61196361195b856116a7565b83018261178e565b50505b505050565b61197482611466565b67ffffffffffffffff81111561198d5761198c610a86565b5b6119978254611661565b6119a2828285611925565b600060209050601f8311600181146119d557600084156119c3578287015190505b6119cd8582611822565b865550611a35565b601f1984166119e386611910565b60005b82811015611a0b578489015182556001820191506020850194506020810190506119e6565b86831015611a285784890151611a24601f891682611804565b8355505b6001600288020188555050505b50505050505056fea26469706673582212200dba62d022bc9124436e444abbab60b8957053b0afad54f4145c760e340382ec64736f6c634300080f0033",
+    "contractName" : "ProposalStore"
+}
diff --git a/contracts/compiled_contracts/Turnstile.json b/contracts/compiled_contracts/Turnstile.json
new file mode 100644
index 00000000..169f38d3
--- /dev/null
+++ b/contracts/compiled_contracts/Turnstile.json
@@ -0,0 +1,6 @@
+
+{
+    "abi": "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AlreadyRegistered\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTokenId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAnOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotSmartContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NothingToDistribute\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NothingToWithdraw\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Unregistered\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"approved\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"ApprovalForAll\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"smartContract\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Assign\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeAmount\",\"type\":\"uint256\"}],\"name\":\"DistributeFees\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"smartContract\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Register\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeAmount\",\"type\":\"uint256\"}],\"name\":\"Withdraw\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_tokenId\",\"type\":\"uint256\"}],\"name\":\"assign\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"balances\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currentCounterId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_tokenId\",\"type\":\"uint256\"}],\"name\":\"distributeFees\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"feeRecipient\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"registered\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"getApproved\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_smartContract\",\"type\":\"address\"}],\"name\":\"getTokenId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"isApprovedForAll\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_smartContract\",\"type\":\"address\"}],\"name\":\"isRegistered\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ownerOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_recipient\",\"type\":\"address\"}],\"name\":\"register\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"setApprovalForAll\",\"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\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"tokenByIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"tokenOfOwnerByIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"tokenURI\",\"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\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_tokenId\",\"type\":\"uint256\"},{\"internalType\":\"addresspayable\",\"name\":\"_recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
+    "bin": "60806040523480156200001157600080fd5b506040518060400160405280600981526020017f5475726e7374696c6500000000000000000000000000000000000000000000008152506040518060400160405280600981526020017f5475726e7374696c6500000000000000000000000000000000000000000000008152506200009e62000092620000ca60201b60201c565b620000d260201b60201c565b8160019081620000af919062000410565b508060029081620000c1919062000410565b505050620004f7565b600033905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200021857607f821691505b6020821081036200022e576200022d620001d0565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620002987fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000259565b620002a4868362000259565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620002f1620002eb620002e584620002bc565b620002c6565b620002bc565b9050919050565b6000819050919050565b6200030d83620002d0565b620003256200031c82620002f8565b84845462000266565b825550505050565b600090565b6200033c6200032d565b6200034981848462000302565b505050565b5b8181101562000371576200036560008262000332565b6001810190506200034f565b5050565b601f821115620003c0576200038a8162000234565b620003958462000249565b81016020851015620003a5578190505b620003bd620003b48562000249565b8301826200034e565b50505b505050565b600082821c905092915050565b6000620003e560001984600802620003c5565b1980831691505092915050565b6000620004008383620003d2565b9150826002028217905092915050565b6200041b8262000196565b67ffffffffffffffff811115620004375762000436620001a1565b5b620004438254620001ff565b6200045082828562000375565b600060209050601f83116001811462000488576000841562000473578287015190505b6200047f8582620003f2565b865550620004ef565b601f198416620004988662000234565b60005b82811015620004c2578489015182556001820191506020850194506020810190506200049b565b86831015620004e25784890151620004de601f891682620003d2565b8355505b6001600288020188555050505b505050505050565b613cf180620005076000396000f3fe6080604052600436106101b75760003560e01c806370a08231116100ec578063c87b56dd1161008a578063e66bb34511610064578063e66bb34514610682578063e985e9c5146106ad578063f1537686146106ea578063f2fde38b14610727576101b7565b8063c87b56dd146105ca578063d78162e914610607578063e63697c814610645576101b7565b806395d89b41116100c657806395d89b4114610510578063a22cb4651461053b578063b88d4fde14610564578063c3c5a5471461058d576101b7565b806370a0823114610491578063715018a6146104ce5780638da5cb5b146104e5576101b7565b806342842e0e116101595780634c081138116101335780634c081138146103be5780634f6ccce7146103fb5780636029bf9f146104385780636352211e14610454576101b7565b806342842e0e1461031b5780634420e486146103445780634903b0d114610381576101b7565b8063095ea7b311610195578063095ea7b31461026157806318160ddd1461028a57806323b872dd146102b55780632f745c59146102de576101b7565b806301ffc9a7146101bc57806306fdde03146101f9578063081812fc14610224575b600080fd5b3480156101c857600080fd5b506101e360048036038101906101de919061290f565b610750565b6040516101f09190612957565b60405180910390f35b34801561020557600080fd5b5061020e6107ca565b60405161021b9190612a02565b60405180910390f35b34801561023057600080fd5b5061024b60048036038101906102469190612a5a565b61085c565b6040516102589190612ac8565b60405180910390f35b34801561026d57600080fd5b5061028860048036038101906102839190612b0f565b6108a2565b005b34801561029657600080fd5b5061029f6109b9565b6040516102ac9190612b5e565b60405180910390f35b3480156102c157600080fd5b506102dc60048036038101906102d79190612b79565b6109c6565b005b3480156102ea57600080fd5b5061030560048036038101906103009190612b0f565b610a26565b6040516103129190612b5e565b60405180910390f35b34801561032757600080fd5b50610342600480360381019061033d9190612b79565b610acb565b005b34801561035057600080fd5b5061036b60048036038101906103669190612bcc565b610aeb565b6040516103789190612b5e565b60405180910390f35b34801561038d57600080fd5b506103a860048036038101906103a39190612a5a565b610c82565b6040516103b59190612b5e565b60405180910390f35b3480156103ca57600080fd5b506103e560048036038101906103e09190612a5a565b610c9a565b6040516103f29190612b5e565b60405180910390f35b34801561040757600080fd5b50610422600480360381019061041d9190612a5a565b610deb565b60405161042f9190612b5e565b60405180910390f35b610452600480360381019061044d9190612a5a565b610e5c565b005b34801561046057600080fd5b5061047b60048036038101906104769190612a5a565b610f04565b6040516104889190612ac8565b60405180910390f35b34801561049d57600080fd5b506104b860048036038101906104b39190612bcc565b610fb5565b6040516104c59190612b5e565b60405180910390f35b3480156104da57600080fd5b506104e361106c565b005b3480156104f157600080fd5b506104fa611080565b6040516105079190612ac8565b60405180910390f35b34801561051c57600080fd5b506105256110a9565b6040516105329190612a02565b60405180910390f35b34801561054757600080fd5b50610562600480360381019061055d9190612c25565b61113b565b005b34801561057057600080fd5b5061058b60048036038101906105869190612d9a565b611151565b005b34801561059957600080fd5b506105b460048036038101906105af9190612bcc565b6111b3565b6040516105c19190612957565b60405180910390f35b3480156105d657600080fd5b506105f160048036038101906105ec9190612a5a565b61120c565b6040516105fe9190612a02565b60405180910390f35b34801561061357600080fd5b5061062e60048036038101906106299190612bcc565b611274565b60405161063c929190612e1d565b60405180910390f35b34801561065157600080fd5b5061066c60048036038101906106679190612e84565b6112a5565b6040516106799190612b5e565b60405180910390f35b34801561068e57600080fd5b506106976113f3565b6040516106a49190612b5e565b60405180910390f35b3480156106b957600080fd5b506106d460048036038101906106cf9190612ed7565b611404565b6040516106e19190612957565b60405180910390f35b3480156106f657600080fd5b50610711600480360381019061070c9190612bcc565b611498565b60405161071e9190612b5e565b60405180910390f35b34801561073357600080fd5b5061074e60048036038101906107499190612bcc565b611523565b005b60007f780e9d63000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806107c357506107c2826115a6565b5b9050919050565b6060600180546107d990612f46565b80601f016020809104026020016040519081016040528092919081815260200182805461080590612f46565b80156108525780601f1061082757610100808354040283529160200191610852565b820191906000526020600020905b81548152906001019060200180831161083557829003601f168201915b5050505050905090565b600061086782611688565b6005600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b60006108ad82610f04565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361091d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161091490612fe9565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1661093c6116d3565b73ffffffffffffffffffffffffffffffffffffffff16148061096b575061096a816109656116d3565b611404565b5b6109aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109a19061307b565b60405180910390fd5b6109b483836116db565b505050565b6000600980549050905090565b6109d76109d16116d3565b82611794565b610a16576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a0d9061310d565b60405180910390fd5b610a21838383611829565b505050565b6000610a3183610fb5565b8210610a72576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a699061319f565b60405180910390fd5b600760008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002054905092915050565b610ae683838360405180602001604052806000815250611151565b505050565b600080339050610afa816111b3565b15610b31576040517f3a81d6fc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000339050600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603610b9c576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ba6600b611a8f565b9250610bb28484611a9d565b610bbc600b611c76565b7fcc0bec1447060c88cdc5a739cf29cfa26c453574dd3f5b9e4dcc317d6401cb1c818585604051610bef939291906131bf565b60405180910390a1604051806040016040528084815260200160011515815250600c60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000820151816000015560208201518160010160006101000a81548160ff0219169083151502179055509050505050919050565b600d6020528060005260406000206000915090505481565b600080339050610ca9816111b3565b15610ce0576040517f3a81d6fc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000339050610cee84611c8c565b610d24576040517f3f6cc76800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f8a0e37b73a0d9c82e205d4d1a3ff3d0b57ce5f4d7bccf6bac03336dc101cb7ba8185604051610d559291906131f6565b60405180910390a1604051806040016040528085815260200160011515815250600c60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000820151816000015560208201518160010160006101000a81548160ff0219169083151502179055509050508392505050919050565b6000610df56109b9565b8210610e36576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e2d90613291565b60405180910390fd5b60098281548110610e4a57610e496132b1565b5b90600052602060002001549050919050565b610e64611cf8565b60003403610e9e576040517f01663f2400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34600d60008381526020019081526020016000206000828254610ec1919061330f565b925050819055507f916ad8171ef8c567c7790377a142f0200f9565940680c06e30dd105cfd9249688134604051610ef9929190613343565b60405180910390a150565b6000806003600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610fac576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610fa3906133b8565b60405180910390fd5b80915050919050565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611025576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161101c9061344a565b60405180910390fd5b600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b611074611cf8565b61107e6000611d76565b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6060600280546110b890612f46565b80601f01602080910402602001604051908101604052809291908181526020018280546110e490612f46565b80156111315780601f1061110657610100808354040283529160200191611131565b820191906000526020600020905b81548152906001019060200180831161111457829003601f168201915b5050505050905090565b61114d6111466116d3565b8383611e3a565b5050565b61116261115c6116d3565b83611794565b6111a1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111989061310d565b60405180910390fd5b6111ad84848484611fa6565b50505050565b6000600c60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160009054906101000a900460ff169050919050565b606061121782611688565b6000611221612002565b90506000815111611241576040518060200160405280600081525061126c565b8061124b84612019565b60405160200161125c9291906134a6565b6040516020818303038152906040525b915050919050565b600c6020528060005260406000206000915090508060000154908060010160009054906101000a900460ff16905082565b6000833373ffffffffffffffffffffffffffffffffffffffff166112c882610f04565b73ffffffffffffffffffffffffffffffffffffffff1614611315576040517feea91ff800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600d6000878152602001908152602001600020549050600081148061133c5750600084145b15611373576040517fd0d04f6000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8084111561137f578093505b838161138b91906134ca565b600d6000888152602001908152602001600020819055507f9da6493a92039daf47d1f2d7a782299c5994c6323eb1e972f69c432089ec52bf8686866040516113d59392919061355d565b60405180910390a16113e78585612179565b83925050509392505050565b60006113ff600b611a8f565b905090565b6000600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b60006114a3826111b3565b6114d9576040517f7748bce600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001549050919050565b61152b611cf8565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361159a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161159190613606565b60405180910390fd5b6115a381611d76565b50565b60007f80ac58cd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061167157507f5b5e139f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061168157506116808261226d565b5b9050919050565b61169181611c8c565b6116d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116c7906133b8565b60405180910390fd5b50565b600033905090565b816005600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff1661174e83610f04565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000806117a083610f04565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614806117e257506117e18185611404565b5b8061182057508373ffffffffffffffffffffffffffffffffffffffff166118088461085c565b73ffffffffffffffffffffffffffffffffffffffff16145b91505092915050565b8273ffffffffffffffffffffffffffffffffffffffff1661184982610f04565b73ffffffffffffffffffffffffffffffffffffffff161461189f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161189690613698565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361190e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119059061372a565b60405180910390fd5b6119198383836122d7565b6119246000826116db565b6001600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461197491906134ca565b925050819055506001600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546119cb919061330f565b92505081905550816003600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4611a8a8383836123e9565b505050565b600081600001549050919050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611b0c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b0390613796565b60405180910390fd5b611b1581611c8c565b15611b55576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b4c90613802565b60405180910390fd5b611b61600083836122d7565b6001600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611bb1919061330f565b92505081905550816003600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4611c72600083836123e9565b5050565b6001816000016000828254019250508190555050565b60008073ffffffffffffffffffffffffffffffffffffffff166003600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614159050919050565b611d006116d3565b73ffffffffffffffffffffffffffffffffffffffff16611d1e611080565b73ffffffffffffffffffffffffffffffffffffffff1614611d74576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d6b9061386e565b60405180910390fd5b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603611ea8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e9f906138da565b60405180910390fd5b80600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051611f999190612957565b60405180910390a3505050565b611fb1848484611829565b611fbd848484846123ee565b611ffc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611ff39061396c565b60405180910390fd5b50505050565b606060405180602001604052806000815250905090565b606060008203612060576040518060400160405280600181526020017f30000000000000000000000000000000000000000000000000000000000000008152509050612174565b600082905060005b6000821461209257808061207b9061398c565b915050600a8261208b9190613a03565b9150612068565b60008167ffffffffffffffff8111156120ae576120ad612c6f565b5b6040519080825280601f01601f1916602001820160405280156120e05781602001600182028036833780820191505090505b5090505b6000851461216d576001826120f991906134ca565b9150600a856121089190613a34565b6030612114919061330f565b60f81b81838151811061212a576121296132b1565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600a856121669190613a03565b94506120e4565b8093505050505b919050565b804710156121bc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121b390613ab1565b60405180910390fd5b60008273ffffffffffffffffffffffffffffffffffffffff16826040516121e290613b02565b60006040518083038185875af1925050503d806000811461221f576040519150601f19603f3d011682016040523d82523d6000602084013e612224565b606091505b5050905080612268576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161225f90613b89565b60405180910390fd5b505050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b6122e2838383612575565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036123245761231f8161257a565b612363565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16146123625761236183826125c3565b5b5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036123a5576123a081612730565b6123e4565b8273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16146123e3576123e28282612801565b5b5b505050565b505050565b600061240f8473ffffffffffffffffffffffffffffffffffffffff16612880565b15612568578373ffffffffffffffffffffffffffffffffffffffff1663150b7a026124386116d3565b8786866040518563ffffffff1660e01b815260040161245a9493929190613bfe565b6020604051808303816000875af192505050801561249657506040513d601f19601f820116820180604052508101906124939190613c5f565b60015b612518573d80600081146124c6576040519150601f19603f3d011682016040523d82523d6000602084013e6124cb565b606091505b506000815103612510576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016125079061396c565b60405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161491505061256d565b600190505b949350505050565b505050565b600980549050600a600083815260200190815260200160002081905550600981908060018154018082558091505060019003906000526020600020016000909190919091505550565b600060016125d084610fb5565b6125da91906134ca565b90506000600860008481526020019081526020016000205490508181146126bf576000600760008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002054905080600760008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002081905550816008600083815260200190815260200160002081905550505b6008600084815260200190815260200160002060009055600760008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008381526020019081526020016000206000905550505050565b6000600160098054905061274491906134ca565b90506000600a6000848152602001908152602001600020549050600060098381548110612774576127736132b1565b5b906000526020600020015490508060098381548110612796576127956132b1565b5b906000526020600020018190555081600a600083815260200190815260200160002081905550600a60008581526020019081526020016000206000905560098054806127e5576127e4613c8c565b5b6001900381819060005260206000200160009055905550505050565b600061280c83610fb5565b905081600760008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002081905550806008600084815260200190815260200160002081905550505050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000604051905090565b600080fd5b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6128ec816128b7565b81146128f757600080fd5b50565b600081359050612909816128e3565b92915050565b600060208284031215612925576129246128ad565b5b6000612933848285016128fa565b91505092915050565b60008115159050919050565b6129518161293c565b82525050565b600060208201905061296c6000830184612948565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156129ac578082015181840152602081019050612991565b60008484015250505050565b6000601f19601f8301169050919050565b60006129d482612972565b6129de818561297d565b93506129ee81856020860161298e565b6129f7816129b8565b840191505092915050565b60006020820190508181036000830152612a1c81846129c9565b905092915050565b6000819050919050565b612a3781612a24565b8114612a4257600080fd5b50565b600081359050612a5481612a2e565b92915050565b600060208284031215612a7057612a6f6128ad565b5b6000612a7e84828501612a45565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000612ab282612a87565b9050919050565b612ac281612aa7565b82525050565b6000602082019050612add6000830184612ab9565b92915050565b612aec81612aa7565b8114612af757600080fd5b50565b600081359050612b0981612ae3565b92915050565b60008060408385031215612b2657612b256128ad565b5b6000612b3485828601612afa565b9250506020612b4585828601612a45565b9150509250929050565b612b5881612a24565b82525050565b6000602082019050612b736000830184612b4f565b92915050565b600080600060608486031215612b9257612b916128ad565b5b6000612ba086828701612afa565b9350506020612bb186828701612afa565b9250506040612bc286828701612a45565b9150509250925092565b600060208284031215612be257612be16128ad565b5b6000612bf084828501612afa565b91505092915050565b612c028161293c565b8114612c0d57600080fd5b50565b600081359050612c1f81612bf9565b92915050565b60008060408385031215612c3c57612c3b6128ad565b5b6000612c4a85828601612afa565b9250506020612c5b85828601612c10565b9150509250929050565b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b612ca7826129b8565b810181811067ffffffffffffffff82111715612cc657612cc5612c6f565b5b80604052505050565b6000612cd96128a3565b9050612ce58282612c9e565b919050565b600067ffffffffffffffff821115612d0557612d04612c6f565b5b612d0e826129b8565b9050602081019050919050565b82818337600083830152505050565b6000612d3d612d3884612cea565b612ccf565b905082815260208101848484011115612d5957612d58612c6a565b5b612d64848285612d1b565b509392505050565b600082601f830112612d8157612d80612c65565b5b8135612d91848260208601612d2a565b91505092915050565b60008060008060808587031215612db457612db36128ad565b5b6000612dc287828801612afa565b9450506020612dd387828801612afa565b9350506040612de487828801612a45565b925050606085013567ffffffffffffffff811115612e0557612e046128b2565b5b612e1187828801612d6c565b91505092959194509250565b6000604082019050612e326000830185612b4f565b612e3f6020830184612948565b9392505050565b6000612e5182612a87565b9050919050565b612e6181612e46565b8114612e6c57600080fd5b50565b600081359050612e7e81612e58565b92915050565b600080600060608486031215612e9d57612e9c6128ad565b5b6000612eab86828701612a45565b9350506020612ebc86828701612e6f565b9250506040612ecd86828701612a45565b9150509250925092565b60008060408385031215612eee57612eed6128ad565b5b6000612efc85828601612afa565b9250506020612f0d85828601612afa565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680612f5e57607f821691505b602082108103612f7157612f70612f17565b5b50919050565b7f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560008201527f7200000000000000000000000000000000000000000000000000000000000000602082015250565b6000612fd360218361297d565b9150612fde82612f77565b604082019050919050565b6000602082019050818103600083015261300281612fc6565b9050919050565b7f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60008201527f6b656e206f776e6572206e6f7220617070726f76656420666f7220616c6c0000602082015250565b6000613065603e8361297d565b915061307082613009565b604082019050919050565b6000602082019050818103600083015261309481613058565b9050919050565b7f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560008201527f72206e6f7220617070726f766564000000000000000000000000000000000000602082015250565b60006130f7602e8361297d565b91506131028261309b565b604082019050919050565b60006020820190508181036000830152613126816130ea565b9050919050565b7f455243373231456e756d657261626c653a206f776e657220696e646578206f7560008201527f74206f6620626f756e6473000000000000000000000000000000000000000000602082015250565b6000613189602b8361297d565b91506131948261312d565b604082019050919050565b600060208201905081810360008301526131b88161317c565b9050919050565b60006060820190506131d46000830186612ab9565b6131e16020830185612ab9565b6131ee6040830184612b4f565b949350505050565b600060408201905061320b6000830185612ab9565b6132186020830184612b4f565b9392505050565b7f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60008201527f7574206f6620626f756e64730000000000000000000000000000000000000000602082015250565b600061327b602c8361297d565b91506132868261321f565b604082019050919050565b600060208201905081810360008301526132aa8161326e565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061331a82612a24565b915061332583612a24565b925082820190508082111561333d5761333c6132e0565b5b92915050565b60006040820190506133586000830185612b4f565b6133656020830184612b4f565b9392505050565b7f4552433732313a20696e76616c696420746f6b656e2049440000000000000000600082015250565b60006133a260188361297d565b91506133ad8261336c565b602082019050919050565b600060208201905081810360008301526133d181613395565b9050919050565b7f4552433732313a2061646472657373207a65726f206973206e6f74206120766160008201527f6c6964206f776e65720000000000000000000000000000000000000000000000602082015250565b600061343460298361297d565b915061343f826133d8565b604082019050919050565b6000602082019050818103600083015261346381613427565b9050919050565b600081905092915050565b600061348082612972565b61348a818561346a565b935061349a81856020860161298e565b80840191505092915050565b60006134b28285613475565b91506134be8284613475565b91508190509392505050565b60006134d582612a24565b91506134e083612a24565b92508282039050818111156134f8576134f76132e0565b5b92915050565b6000819050919050565b600061352361351e61351984612a87565b6134fe565b612a87565b9050919050565b600061353582613508565b9050919050565b60006135478261352a565b9050919050565b6135578161353c565b82525050565b60006060820190506135726000830186612b4f565b61357f602083018561354e565b61358c6040830184612b4f565b949350505050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b60006135f060268361297d565b91506135fb82613594565b604082019050919050565b6000602082019050818103600083015261361f816135e3565b9050919050565b7f4552433732313a207472616e736665722066726f6d20696e636f72726563742060008201527f6f776e6572000000000000000000000000000000000000000000000000000000602082015250565b600061368260258361297d565b915061368d82613626565b604082019050919050565b600060208201905081810360008301526136b181613675565b9050919050565b7f4552433732313a207472616e7366657220746f20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b600061371460248361297d565b915061371f826136b8565b604082019050919050565b6000602082019050818103600083015261374381613707565b9050919050565b7f4552433732313a206d696e7420746f20746865207a65726f2061646472657373600082015250565b600061378060208361297d565b915061378b8261374a565b602082019050919050565b600060208201905081810360008301526137af81613773565b9050919050565b7f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000600082015250565b60006137ec601c8361297d565b91506137f7826137b6565b602082019050919050565b6000602082019050818103600083015261381b816137df565b9050919050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b600061385860208361297d565b915061386382613822565b602082019050919050565b600060208201905081810360008301526138878161384b565b9050919050565b7f4552433732313a20617070726f766520746f2063616c6c657200000000000000600082015250565b60006138c460198361297d565b91506138cf8261388e565b602082019050919050565b600060208201905081810360008301526138f3816138b7565b9050919050565b7f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560008201527f63656976657220696d706c656d656e7465720000000000000000000000000000602082015250565b600061395660328361297d565b9150613961826138fa565b604082019050919050565b6000602082019050818103600083015261398581613949565b9050919050565b600061399782612a24565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036139c9576139c86132e0565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000613a0e82612a24565b9150613a1983612a24565b925082613a2957613a286139d4565b5b828204905092915050565b6000613a3f82612a24565b9150613a4a83612a24565b925082613a5a57613a596139d4565b5b828206905092915050565b7f416464726573733a20696e73756666696369656e742062616c616e6365000000600082015250565b6000613a9b601d8361297d565b9150613aa682613a65565b602082019050919050565b60006020820190508181036000830152613aca81613a8e565b9050919050565b600081905092915050565b50565b6000613aec600083613ad1565b9150613af782613adc565b600082019050919050565b6000613b0d82613adf565b9150819050919050565b7f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260008201527f6563697069656e74206d61792068617665207265766572746564000000000000602082015250565b6000613b73603a8361297d565b9150613b7e82613b17565b604082019050919050565b60006020820190508181036000830152613ba281613b66565b9050919050565b600081519050919050565b600082825260208201905092915050565b6000613bd082613ba9565b613bda8185613bb4565b9350613bea81856020860161298e565b613bf3816129b8565b840191505092915050565b6000608082019050613c136000830187612ab9565b613c206020830186612ab9565b613c2d6040830185612b4f565b8181036060830152613c3f8184613bc5565b905095945050505050565b600081519050613c59816128e3565b92915050565b600060208284031215613c7557613c746128ad565b5b6000613c8384828501613c4a565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea2646970667358221220af8e0e24f2d12a183489099cadea3fda44860b88b97c863a8ef139d5fee2459164736f6c63430008110033",
+    "contractName" : "Turnstile"
+}
diff --git a/contracts/compiled_contracts/callee.json b/contracts/compiled_contracts/callee.json
new file mode 100644
index 00000000..93922ed2
--- /dev/null
+++ b/contracts/compiled_contracts/callee.json
@@ -0,0 +1,6 @@
+
+{
+    "abi":"[{\"inputs\":[],\"name\":\"Int\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"setInt\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
+    "bin":"608060405234801561001057600080fd5b50610133806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806344420311146037578063b6c27e5314604f575b600080fd5b604d60048036038101906049919060af565b6069565b005b60556073565b6040516060919060e4565b60405180910390f35b8060008190555050565b60005481565b600080fd5b6000819050919050565b608f81607e565b8114609957600080fd5b50565b60008135905060a9816088565b92915050565b60006020828403121560c25760c16079565b5b600060ce84828501609c565b91505092915050565b60de81607e565b82525050565b600060208201905060f7600083018460d7565b9291505056fea2646970667358221220419f2ec3c731b473ed042fc9894a33156ca82941a01a95cf30ddd465f0fa469164736f6c634300080f0033",
+    "contractName" : "callee"
+}
diff --git a/contracts/compiled_contracts/caller.json b/contracts/compiled_contracts/caller.json
new file mode 100644
index 00000000..1cdcb984
--- /dev/null
+++ b/contracts/compiled_contracts/caller.json
@@ -0,0 +1,5 @@
+{
+"abi":"[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"propId\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"title\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"desc\",\"type\":\"string\"},{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"propId\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"title\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"desc\",\"type\":\"string\"},{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"}],\"name\":\"AddProposal\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"propId\",\"type\":\"uint256\"}],\"name\":\"QueryProp\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"title\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"desc\",\"type\":\"string\"},{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"}],\"internalType\":\"struct ProposalStore.Proposal\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]",
+"bin":"60806040523480156200001157600080fd5b5060405162002acf38038062002acf833981810160405281019062000037919062000ab5565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060006040518060e0016040528089815260200188815260200187815260200186815260200185815260200184815260200183815250905080600160008a8152602001908152602001600020600082015181600001556020820151816001019081620000e3919062000e63565b506040820151816002019081620000fb919062000e63565b5060608201518160030190805190602001906200011a92919062000189565b5060808201518160040190805190602001906200013992919062000218565b5060a0820151816005019080519060200190620001589291906200026a565b5060c082015181600601908051906020019062000177929190620002ca565b509050505050505050505050620010a1565b82805482825590600052602060002090810192821562000205579160200282015b82811115620002045782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555091602001919060010190620001aa565b5b5090506200021491906200032a565b5090565b82805482825590600052602060002090810192821562000257579160200282015b828111156200025657825182559160200191906001019062000239565b5b5090506200026691906200032a565b5090565b828054828255906000526020600020908101928215620002b7579160200282015b82811115620002b6578251829081620002a5919062000e63565b50916020019190600101906200028b565b5b509050620002c6919062000349565b5090565b82805482825590600052602060002090810192821562000317579160200282015b828111156200031657825182908162000305919062000fba565b5091602001919060010190620002eb565b5b50905062000326919062000371565b5090565b5b80821115620003455760008160009055506001016200032b565b5090565b5b808211156200036d576000818162000363919062000399565b506001016200034a565b5090565b5b808211156200039557600081816200038b9190620003df565b5060010162000372565b5090565b508054620003a79062000c5c565b6000825580601f10620003bb5750620003dc565b601f016020900490600052602060002090810190620003db91906200032a565b5b50565b508054620003ed9062000c5c565b6000825580601f1062000401575062000422565b601f0160209004906000526020600020908101906200042191906200032a565b5b50565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b6200044e8162000439565b81146200045a57600080fd5b50565b6000815190506200046e8162000443565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620004c9826200047e565b810181811067ffffffffffffffff82111715620004eb57620004ea6200048f565b5b80604052505050565b60006200050062000425565b90506200050e8282620004be565b919050565b600067ffffffffffffffff8211156200053157620005306200048f565b5b6200053c826200047e565b9050602081019050919050565b60005b83811015620005695780820151818401526020810190506200054c565b8381111562000579576000848401525b50505050565b600062000596620005908462000513565b620004f4565b905082815260208101848484011115620005b557620005b462000479565b5b620005c284828562000549565b509392505050565b600082601f830112620005e257620005e162000474565b5b8151620005f48482602086016200057f565b91505092915050565b600067ffffffffffffffff8211156200061b576200061a6200048f565b5b602082029050602081019050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200065e8262000631565b9050919050565b620006708162000651565b81146200067c57600080fd5b50565b600081519050620006908162000665565b92915050565b6000620006ad620006a784620005fd565b620004f4565b90508083825260208201905060208402830185811115620006d357620006d26200062c565b5b835b81811015620007005780620006eb88826200067f565b845260208401935050602081019050620006d5565b5050509392505050565b600082601f83011262000722576200072162000474565b5b81516200073484826020860162000696565b91505092915050565b600067ffffffffffffffff8211156200075b576200075a6200048f565b5b602082029050602081019050919050565b6000620007836200077d846200073d565b620004f4565b90508083825260208201905060208402830185811115620007a957620007a86200062c565b5b835b81811015620007d65780620007c188826200045d565b845260208401935050602081019050620007ab565b5050509392505050565b600082601f830112620007f857620007f762000474565b5b81516200080a8482602086016200076c565b91505092915050565b600067ffffffffffffffff8211156200083157620008306200048f565b5b602082029050602081019050919050565b600062000859620008538462000813565b620004f4565b905080838252602082019050602084028301858111156200087f576200087e6200062c565b5b835b81811015620008cd57805167ffffffffffffffff811115620008a857620008a762000474565b5b808601620008b78982620005ca565b8552602085019450505060208101905062000881565b5050509392505050565b600082601f830112620008ef57620008ee62000474565b5b81516200090184826020860162000842565b91505092915050565b600067ffffffffffffffff8211156200092857620009276200048f565b5b602082029050602081019050919050565b600067ffffffffffffffff8211156200095757620009566200048f565b5b62000962826200047e565b9050602081019050919050565b600062000986620009808462000939565b620004f4565b905082815260208101848484011115620009a557620009a462000479565b5b620009b284828562000549565b509392505050565b600082601f830112620009d257620009d162000474565b5b8151620009e48482602086016200096f565b91505092915050565b600062000a04620009fe846200090a565b620004f4565b9050808382526020820190506020840283018581111562000a2a5762000a296200062c565b5b835b8181101562000a7857805167ffffffffffffffff81111562000a535762000a5262000474565b5b80860162000a628982620009ba565b8552602085019450505060208101905062000a2c565b5050509392505050565b600082601f83011262000a9a5762000a9962000474565b5b815162000aac848260208601620009ed565b91505092915050565b600080600080600080600060e0888a03121562000ad75762000ad66200042f565b5b600062000ae78a828b016200045d565b975050602088015167ffffffffffffffff81111562000b0b5762000b0a62000434565b5b62000b198a828b01620005ca565b965050604088015167ffffffffffffffff81111562000b3d5762000b3c62000434565b5b62000b4b8a828b01620005ca565b955050606088015167ffffffffffffffff81111562000b6f5762000b6e62000434565b5b62000b7d8a828b016200070a565b945050608088015167ffffffffffffffff81111562000ba15762000ba062000434565b5b62000baf8a828b01620007e0565b93505060a088015167ffffffffffffffff81111562000bd35762000bd262000434565b5b62000be18a828b01620008d7565b92505060c088015167ffffffffffffffff81111562000c055762000c0462000434565b5b62000c138a828b0162000a82565b91505092959891949750929550565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168062000c7557607f821691505b60208210810362000c8b5762000c8a62000c2d565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b60006008830262000cf57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000cb6565b62000d01868362000cb6565b95508019841693508086168417925050509392505050565b6000819050919050565b600062000d4462000d3e62000d388462000439565b62000d19565b62000439565b9050919050565b6000819050919050565b62000d608362000d23565b62000d7862000d6f8262000d4b565b84845462000cc3565b825550505050565b600090565b62000d8f62000d80565b62000d9c81848462000d55565b505050565b5b8181101562000dc45762000db860008262000d85565b60018101905062000da2565b5050565b601f82111562000e135762000ddd8162000c91565b62000de88462000ca6565b8101602085101562000df8578190505b62000e1062000e078562000ca6565b83018262000da1565b50505b505050565b600082821c905092915050565b600062000e386000198460080262000e18565b1980831691505092915050565b600062000e53838362000e25565b9150826002028217905092915050565b62000e6e8262000c22565b67ffffffffffffffff81111562000e8a5762000e896200048f565b5b62000e96825462000c5c565b62000ea382828562000dc8565b600060209050601f83116001811462000edb576000841562000ec6578287015190505b62000ed2858262000e45565b86555062000f42565b601f19841662000eeb8662000c91565b60005b8281101562000f155784890151825560018201915060208501945060208101905062000eee565b8683101562000f35578489015162000f31601f89168262000e25565b8355505b6001600288020188555050505b505050505050565b600081519050919050565b60008190508160005260206000209050919050565b601f82111562000fb55762000f7f8162000f55565b62000f8a8462000ca6565b8101602085101562000f9a578190505b62000fb262000fa98562000ca6565b83018262000da1565b50505b505050565b62000fc58262000f4a565b67ffffffffffffffff81111562000fe15762000fe06200048f565b5b62000fed825462000c5c565b62000ffa82828562000f6a565b600060209050601f8311600181146200103257600084156200101d578287015190505b62001029858262000e45565b86555062001099565b601f198416620010428662000f55565b60005b828110156200106c5784890151825560018201915060208501945060208101905062001045565b868310156200108c578489015162001088601f89168262000e25565b8355505b6001600288020188555050505b505050505050565b611a1e80620010b16000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806327bc123e1461003b578063f0e147b814610057575b600080fd5b61005560048036038101906100509190610fa8565b610087565b005b610071600480360381019061006c91906110f2565b610189565b60405161007e91906115bb565b60405180910390f35b60006040518060e0016040528089815260200188815260200187815260200186815260200185815260200184815260200183815250905080600160008a81526020019081526020016000206000820151816000015560208201518160010190816100f191906117e9565b50604082015181600201908161010791906117e9565b506060820151816003019080519060200190610124929190610721565b5060808201518160040190805190602001906101419291906107ab565b5060a082015181600501908051906020019061015e9291906107f8565b5060c082015181600601908051906020019061017b929190610851565b509050505050505050505050565b6101916108aa565b8160016000848152602001908152602001600020600001540361059957600160008381526020019081526020016000206040518060e0016040529081600082015481526020016001820180546101e69061160c565b80601f01602080910402602001604051908101604052809291908181526020018280546102129061160c565b801561025f5780601f106102345761010080835404028352916020019161025f565b820191906000526020600020905b81548152906001019060200180831161024257829003601f168201915b505050505081526020016002820180546102789061160c565b80601f01602080910402602001604051908101604052809291908181526020018280546102a49061160c565b80156102f15780601f106102c6576101008083540402835291602001916102f1565b820191906000526020600020905b8154815290600101906020018083116102d457829003601f168201915b505050505081526020016003820180548060200260200160405190810160405280929190818152602001828054801561037f57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610335575b50505050508152602001600482018054806020026020016040519081016040528092919081815260200182805480156103d757602002820191906000526020600020905b8154815260200190600101908083116103c3575b5050505050815260200160058201805480602002602001604051908101604052809291908181526020016000905b828210156104b15783829060005260206000200180546104249061160c565b80601f01602080910402602001604051908101604052809291908181526020018280546104509061160c565b801561049d5780601f106104725761010080835404028352916020019161049d565b820191906000526020600020905b81548152906001019060200180831161048057829003601f168201915b505050505081526020019060010190610405565b50505050815260200160068201805480602002602001604051908101604052809291908181526020016000905b8282101561058a5783829060005260206000200180546104fd9061160c565b80601f01602080910402602001604051908101604052809291908181526020018280546105299061160c565b80156105765780601f1061054b57610100808354040283529160200191610576565b820191906000526020600020905b81548152906001019060200180831161055957829003601f168201915b5050505050815260200190600101906104de565b5050505081525050905061071c565b6040518060e0016040528060008152602001604051806020016040528060008152508152602001604051806020016040528060008152508152602001600067ffffffffffffffff8111156105f0576105ef610a31565b5b60405190808252806020026020018201604052801561061e5781602001602082028036833780820191505090505b508152602001600067ffffffffffffffff81111561063f5761063e610a31565b5b60405190808252806020026020018201604052801561066d5781602001602082028036833780820191505090505b508152602001600067ffffffffffffffff81111561068e5761068d610a31565b5b6040519080825280602002602001820160405280156106c157816020015b60608152602001906001900390816106ac5790505b508152602001600067ffffffffffffffff8111156106e2576106e1610a31565b5b60405190808252806020026020018201604052801561071557816020015b60608152602001906001900390816107005790505b5081525090505b919050565b82805482825590600052602060002090810192821561079a579160200282015b828111156107995782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555091602001919060010190610741565b5b5090506107a791906108e7565b5090565b8280548282559060005260206000209081019282156107e7579160200282015b828111156107e65782518255916020019190600101906107cb565b5b5090506107f491906108e7565b5090565b828054828255906000526020600020908101928215610840579160200282015b8281111561083f57825182908161082f91906117e9565b5091602001919060010190610818565b5b50905061084d9190610904565b5090565b828054828255906000526020600020908101928215610899579160200282015b828111156108985782518290816108889190611916565b5091602001919060010190610871565b5b5090506108a69190610928565b5090565b6040518060e00160405280600081526020016060815260200160608152602001606081526020016060815260200160608152602001606081525090565b5b808211156109005760008160009055506001016108e8565b5090565b5b80821115610924576000818161091b919061094c565b50600101610905565b5090565b5b80821115610948576000818161093f919061098c565b50600101610929565b5090565b5080546109589061160c565b6000825580601f1061096a5750610989565b601f01602090049060005260206000209081019061098891906108e7565b5b50565b5080546109989061160c565b6000825580601f106109aa57506109c9565b601f0160209004906000526020600020908101906109c891906108e7565b5b50565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b6109f3816109e0565b81146109fe57600080fd5b50565b600081359050610a10816109ea565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610a6982610a20565b810181811067ffffffffffffffff82111715610a8857610a87610a31565b5b80604052505050565b6000610a9b6109cc565b9050610aa78282610a60565b919050565b600067ffffffffffffffff821115610ac757610ac6610a31565b5b610ad082610a20565b9050602081019050919050565b82818337600083830152505050565b6000610aff610afa84610aac565b610a91565b905082815260208101848484011115610b1b57610b1a610a1b565b5b610b26848285610add565b509392505050565b600082601f830112610b4357610b42610a16565b5b8135610b53848260208601610aec565b91505092915050565b600067ffffffffffffffff821115610b7757610b76610a31565b5b602082029050602081019050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610bb882610b8d565b9050919050565b610bc881610bad565b8114610bd357600080fd5b50565b600081359050610be581610bbf565b92915050565b6000610bfe610bf984610b5c565b610a91565b90508083825260208201905060208402830185811115610c2157610c20610b88565b5b835b81811015610c4a5780610c368882610bd6565b845260208401935050602081019050610c23565b5050509392505050565b600082601f830112610c6957610c68610a16565b5b8135610c79848260208601610beb565b91505092915050565b600067ffffffffffffffff821115610c9d57610c9c610a31565b5b602082029050602081019050919050565b6000610cc1610cbc84610c82565b610a91565b90508083825260208201905060208402830185811115610ce457610ce3610b88565b5b835b81811015610d0d5780610cf98882610a01565b845260208401935050602081019050610ce6565b5050509392505050565b600082601f830112610d2c57610d2b610a16565b5b8135610d3c848260208601610cae565b91505092915050565b600067ffffffffffffffff821115610d6057610d5f610a31565b5b602082029050602081019050919050565b6000610d84610d7f84610d45565b610a91565b90508083825260208201905060208402830185811115610da757610da6610b88565b5b835b81811015610dee57803567ffffffffffffffff811115610dcc57610dcb610a16565b5b808601610dd98982610b2e565b85526020850194505050602081019050610da9565b5050509392505050565b600082601f830112610e0d57610e0c610a16565b5b8135610e1d848260208601610d71565b91505092915050565b600067ffffffffffffffff821115610e4157610e40610a31565b5b602082029050602081019050919050565b600067ffffffffffffffff821115610e6d57610e6c610a31565b5b610e7682610a20565b9050602081019050919050565b6000610e96610e9184610e52565b610a91565b905082815260208101848484011115610eb257610eb1610a1b565b5b610ebd848285610add565b509392505050565b600082601f830112610eda57610ed9610a16565b5b8135610eea848260208601610e83565b91505092915050565b6000610f06610f0184610e26565b610a91565b90508083825260208201905060208402830185811115610f2957610f28610b88565b5b835b81811015610f7057803567ffffffffffffffff811115610f4e57610f4d610a16565b5b808601610f5b8982610ec5565b85526020850194505050602081019050610f2b565b5050509392505050565b600082601f830112610f8f57610f8e610a16565b5b8135610f9f848260208601610ef3565b91505092915050565b600080600080600080600060e0888a031215610fc757610fc66109d6565b5b6000610fd58a828b01610a01565b975050602088013567ffffffffffffffff811115610ff657610ff56109db565b5b6110028a828b01610b2e565b965050604088013567ffffffffffffffff811115611023576110226109db565b5b61102f8a828b01610b2e565b955050606088013567ffffffffffffffff8111156110505761104f6109db565b5b61105c8a828b01610c54565b945050608088013567ffffffffffffffff81111561107d5761107c6109db565b5b6110898a828b01610d17565b93505060a088013567ffffffffffffffff8111156110aa576110a96109db565b5b6110b68a828b01610df8565b92505060c088013567ffffffffffffffff8111156110d7576110d66109db565b5b6110e38a828b01610f7a565b91505092959891949750929550565b600060208284031215611108576111076109d6565b5b600061111684828501610a01565b91505092915050565b611128816109e0565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561116857808201518184015260208101905061114d565b83811115611177576000848401525b50505050565b60006111888261112e565b6111928185611139565b93506111a281856020860161114a565b6111ab81610a20565b840191505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6111eb81610bad565b82525050565b60006111fd83836111e2565b60208301905092915050565b6000602082019050919050565b6000611221826111b6565b61122b81856111c1565b9350611236836111d2565b8060005b8381101561126757815161124e88826111f1565b975061125983611209565b92505060018101905061123a565b5085935050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60006112ac838361111f565b60208301905092915050565b6000602082019050919050565b60006112d082611274565b6112da818561127f565b93506112e583611290565b8060005b838110156113165781516112fd88826112a0565b9750611308836112b8565b9250506001810190506112e9565b5085935050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600061135b838361117d565b905092915050565b6000602082019050919050565b600061137b82611323565b611385818561132e565b9350836020820285016113978561133f565b8060005b858110156113d357848403895281516113b4858261134f565b94506113bf83611363565b925060208a0199505060018101905061139b565b50829750879550505050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600081519050919050565b600082825260208201905092915050565b600061143882611411565b611442818561141c565b935061145281856020860161114a565b61145b81610a20565b840191505092915050565b6000611472838361142d565b905092915050565b6000602082019050919050565b6000611492826113e5565b61149c81856113f0565b9350836020820285016114ae85611401565b8060005b858110156114ea57848403895281516114cb8582611466565b94506114d68361147a565b925060208a019950506001810190506114b2565b50829750879550505050505092915050565b600060e083016000830151611514600086018261111f565b506020830151848203602086015261152c828261117d565b91505060408301518482036040860152611546828261117d565b915050606083015184820360608601526115608282611216565b9150506080830151848203608086015261157a82826112c5565b91505060a083015184820360a08601526115948282611370565b91505060c083015184820360c08601526115ae8282611487565b9150508091505092915050565b600060208201905081810360008301526115d581846114fc565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061162457607f821691505b602082108103611637576116366115dd565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b60006008830261169f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82611662565b6116a98683611662565b95508019841693508086168417925050509392505050565b6000819050919050565b60006116e66116e16116dc846109e0565b6116c1565b6109e0565b9050919050565b6000819050919050565b611700836116cb565b61171461170c826116ed565b84845461166f565b825550505050565b600090565b61172961171c565b6117348184846116f7565b505050565b5b818110156117585761174d600082611721565b60018101905061173a565b5050565b601f82111561179d5761176e8161163d565b61177784611652565b81016020851015611786578190505b61179a61179285611652565b830182611739565b50505b505050565b600082821c905092915050565b60006117c0600019846008026117a2565b1980831691505092915050565b60006117d983836117af565b9150826002028217905092915050565b6117f28261112e565b67ffffffffffffffff81111561180b5761180a610a31565b5b611815825461160c565b61182082828561175c565b600060209050601f8311600181146118535760008415611841578287015190505b61184b85826117cd565b8655506118b3565b601f1984166118618661163d565b60005b8281101561188957848901518255600182019150602085019450602081019050611864565b868310156118a657848901516118a2601f8916826117af565b8355505b6001600288020188555050505b505050505050565b60008190508160005260206000209050919050565b601f821115611911576118e2816118bb565b6118eb84611652565b810160208510156118fa578190505b61190e61190685611652565b830182611739565b50505b505050565b61191f82611411565b67ffffffffffffffff81111561193857611937610a31565b5b611942825461160c565b61194d8282856118d0565b600060209050601f831160018114611980576000841561196e578287015190505b61197885826117cd565b8655506119e0565b601f19841661198e866118bb565b60005b828110156119b657848901518255600182019150602085019450602081019050611991565b868310156119d357848901516119cf601f8916826117af565b8355505b6001600288020188555050505b50505050505056fea26469706673582212201d571f701f84db94a39e107bb15ae56c5d73c1c37a07b755e0d36211a2eed7f864736f6c634300080f0033\"},\"caller.sol:caller\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"queryProp\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"propContract\",\"type\":\"address\"}],\"name\":\"setPropContract\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"govshuttleContract\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"bin\":\"608060405234801561001057600080fd5b50610cde806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80633fa7b4e81461004657806363abc2c314610064578063e26e848214610080575b600080fd5b61004e61009c565b60405161005b9190610360565b60405180910390f35b61007e600480360381019061007991906103c5565b6100c0565b005b61009a6004803603810190610095919061041e565b610283565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905060008173ffffffffffffffffffffffffffffffffffffffff1663f0e147b8846040518263ffffffff1660e01b8152600401610121919061045a565b600060405180830381865afa15801561013e573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906101679190610b5d565b905060008160a0015160008151811061018357610182610ba6565b5b6020026020010151805190602001208260c001516000815181106101aa576101a9610ba6565b5b60200260200101516040516020016101c3929190610c69565b604051602081830303815290604052905081606001516000815181106101ec576101eb610ba6565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16826080015160008151811061022257610221610ba6565b5b6020026020010151826040516102389190610c91565b60006040518083038185875af1925050503d8060008114610275576040519150601f19603f3d011682016040523d82523d6000602084013e61027a565b606091505b50505050505050565b600073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146102dc57600080fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061034a8261031f565b9050919050565b61035a8161033f565b82525050565b60006020820190506103756000830184610351565b92915050565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b6103a28161038f565b81146103ad57600080fd5b50565b6000813590506103bf81610399565b92915050565b6000602082840312156103db576103da610385565b5b60006103e9848285016103b0565b91505092915050565b6103fb8161033f565b811461040657600080fd5b50565b600081359050610418816103f2565b92915050565b60006020828403121561043457610433610385565b5b600061044284828501610409565b91505092915050565b6104548161038f565b82525050565b600060208201905061046f600083018461044b565b92915050565b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6104c38261047a565b810181811067ffffffffffffffff821117156104e2576104e161048b565b5b80604052505050565b60006104f561037b565b905061050182826104ba565b919050565b600080fd5b60008151905061051a81610399565b92915050565b600080fd5b600080fd5b600067ffffffffffffffff8211156105455761054461048b565b5b61054e8261047a565b9050602081019050919050565b60005b8381101561057957808201518184015260208101905061055e565b83811115610588576000848401525b50505050565b60006105a161059c8461052a565b6104eb565b9050828152602081018484840111156105bd576105bc610525565b5b6105c884828561055b565b509392505050565b600082601f8301126105e5576105e4610520565b5b81516105f584826020860161058e565b91505092915050565b600067ffffffffffffffff8211156106195761061861048b565b5b602082029050602081019050919050565b600080fd5b60008151905061063e816103f2565b92915050565b6000610657610652846105fe565b6104eb565b9050808382526020820190506020840283018581111561067a5761067961062a565b5b835b818110156106a3578061068f888261062f565b84526020840193505060208101905061067c565b5050509392505050565b600082601f8301126106c2576106c1610520565b5b81516106d2848260208601610644565b91505092915050565b600067ffffffffffffffff8211156106f6576106f561048b565b5b602082029050602081019050919050565b600061071a610715846106db565b6104eb565b9050808382526020820190506020840283018581111561073d5761073c61062a565b5b835b818110156107665780610752888261050b565b84526020840193505060208101905061073f565b5050509392505050565b600082601f83011261078557610784610520565b5b8151610795848260208601610707565b91505092915050565b600067ffffffffffffffff8211156107b9576107b861048b565b5b602082029050602081019050919050565b60006107dd6107d88461079e565b6104eb565b90508083825260208201905060208402830185811115610800576107ff61062a565b5b835b8181101561084757805167ffffffffffffffff81111561082557610824610520565b5b80860161083289826105d0565b85526020850194505050602081019050610802565b5050509392505050565b600082601f83011261086657610865610520565b5b81516108768482602086016107ca565b91505092915050565b600067ffffffffffffffff82111561089a5761089961048b565b5b602082029050602081019050919050565b600067ffffffffffffffff8211156108c6576108c561048b565b5b6108cf8261047a565b9050602081019050919050565b60006108ef6108ea846108ab565b6104eb565b90508281526020810184848401111561090b5761090a610525565b5b61091684828561055b565b509392505050565b600082601f83011261093357610932610520565b5b81516109438482602086016108dc565b91505092915050565b600061095f61095a8461087f565b6104eb565b905080838252602082019050602084028301858111156109825761098161062a565b5b835b818110156109c957805167ffffffffffffffff8111156109a7576109a6610520565b5b8086016109b4898261091e565b85526020850194505050602081019050610984565b5050509392505050565b600082601f8301126109e8576109e7610520565b5b81516109f884826020860161094c565b91505092915050565b600060e08284031215610a1757610a16610475565b5b610a2160e06104eb565b90506000610a318482850161050b565b600083015250602082015167ffffffffffffffff811115610a5557610a54610506565b5b610a61848285016105d0565b602083015250604082015167ffffffffffffffff811115610a8557610a84610506565b5b610a91848285016105d0565b604083015250606082015167ffffffffffffffff811115610ab557610ab4610506565b5b610ac1848285016106ad565b606083015250608082015167ffffffffffffffff811115610ae557610ae4610506565b5b610af184828501610770565b60808301525060a082015167ffffffffffffffff811115610b1557610b14610506565b5b610b2184828501610851565b60a08301525060c082015167ffffffffffffffff811115610b4557610b44610506565b5b610b51848285016109d3565b60c08301525092915050565b600060208284031215610b7357610b72610385565b5b600082015167ffffffffffffffff811115610b9157610b9061038a565b5b610b9d84828501610a01565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6000819050919050565b610c1c610c1782610bd5565b610c01565b82525050565b600081519050919050565b600081905092915050565b6000610c4382610c22565b610c4d8185610c2d565b9350610c5d81856020860161055b565b80840191505092915050565b6000610c758285610c0b565b600482019150610c858284610c38565b91508190509392505050565b6000610c9d8284610c38565b91508190509291505056fea26469706673582212207e23adca640af3ebc42115e2da4ac7e5b9658141443bc45f2d21eb2eef06f7c264736f6c634300080f0033",
+"contractName": "caller" 
+}
diff --git a/contracts/csr.go b/contracts/csr.go
new file mode 100644
index 00000000..a299a547
--- /dev/null
+++ b/contracts/csr.go
@@ -0,0 +1,25 @@
+package contracts
+
+import (
+	_ "embed" // embed compiled smart contract
+	"encoding/json"
+
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+)
+
+var (
+	//go:embed compiled_contracts/Turnstile.json
+	TurnstileJSON     []byte
+	TurnstileContract evmtypes.CompiledContract
+)
+
+func init() {
+	err := json.Unmarshal(TurnstileJSON, &TurnstileContract)
+	if err != nil {
+		panic(err)
+	}
+
+	if len(TurnstileContract.Bin) == 0 {
+		panic("The turnstile contract was not loaded")
+	}
+}
diff --git a/contracts/erc20.go b/contracts/erc20.go
new file mode 100644
index 00000000..e5f488f3
--- /dev/null
+++ b/contracts/erc20.go
@@ -0,0 +1,35 @@
+package contracts
+
+import (
+	_ "embed" // embed compiled smart contract
+	"encoding/json"
+
+	"github.com/ethereum/go-ethereum/common"
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+var (
+	//go:embed compiled_contracts/ERC20MinterBurnerDecimals.json
+	ERC20MinterBurnerDecimalsJSON []byte // nolint: golint
+
+	// ERC20MinterBurnerDecimalsContract is the compiled erc20 contract
+	ERC20MinterBurnerDecimalsContract evmtypes.CompiledContract
+
+	// ERC20MinterBurnerDecimalsAddress is the erc20 module address
+	ERC20MinterBurnerDecimalsAddress common.Address
+)
+
+func init() {
+	ERC20MinterBurnerDecimalsAddress = types.ModuleAddress
+
+	err := json.Unmarshal(ERC20MinterBurnerDecimalsJSON, &ERC20MinterBurnerDecimalsContract)
+	if err != nil {
+		panic(err)
+	}
+
+	if len(ERC20MinterBurnerDecimalsContract.Bin) == 0 {
+		panic("load contract failed")
+	}
+}
diff --git a/contracts/erc20DirectBalanceManipulation.go b/contracts/erc20DirectBalanceManipulation.go
new file mode 100644
index 00000000..0238a89f
--- /dev/null
+++ b/contracts/erc20DirectBalanceManipulation.go
@@ -0,0 +1,37 @@
+package contracts
+
+import (
+	_ "embed" // embed compiled smart contract
+	"encoding/json"
+
+	"github.com/ethereum/go-ethereum/common"
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+// This is an evil token. Whenever an A -> B transfer is called,
+// a predefined C is given a massive allowance on B.
+var (
+	//go:embed compiled_contracts/ERC20DirectBalanceManipulation.json
+	ERC20DirectBalanceManipulationJSON []byte // nolint: golint
+
+	// ERC20DirectBalanceManipulationContract is the compiled erc20 contract
+	ERC20DirectBalanceManipulationContract evmtypes.CompiledContract
+
+	// ERC20DirectBalanceManipulationAddress is the erc20 module address
+	ERC20DirectBalanceManipulationAddress common.Address
+)
+
+func init() {
+	ERC20DirectBalanceManipulationAddress = types.ModuleAddress
+
+	err := json.Unmarshal(ERC20DirectBalanceManipulationJSON, &ERC20DirectBalanceManipulationContract)
+	if err != nil {
+		panic(err)
+	}
+
+	if len(ERC20DirectBalanceManipulationContract.Bin) == 0 {
+		panic("load contract failed")
+	}
+}
diff --git a/contracts/erc20burnable.go b/contracts/erc20burnable.go
new file mode 100644
index 00000000..d5e79449
--- /dev/null
+++ b/contracts/erc20burnable.go
@@ -0,0 +1,23 @@
+package contracts
+
+import (
+	_ "embed" // embed compiled smart contract
+	"encoding/json"
+
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+)
+
+var (
+	//go:embed compiled_contracts/ERC20Burnable.json
+	erc20BurnableJSON []byte
+
+	// ERC20BurnableContract is the compiled ERC20Burnable contract
+	ERC20BurnableContract evmtypes.CompiledContract
+)
+
+func init() {
+	err := json.Unmarshal(erc20BurnableJSON, &ERC20BurnableContract)
+	if err != nil {
+		panic(err)
+	}
+}
diff --git a/contracts/erc20maliciousdelayed.go b/contracts/erc20maliciousdelayed.go
new file mode 100644
index 00000000..3f38eb38
--- /dev/null
+++ b/contracts/erc20maliciousdelayed.go
@@ -0,0 +1,37 @@
+package contracts
+
+import (
+	_ "embed" // embed compiled smart contract
+	"encoding/json"
+
+	"github.com/ethereum/go-ethereum/common"
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+
+	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
+)
+
+// This is an evil token. Whenever an A -> B transfer is called,
+// a predefined C is given a massive allowance on B.
+var (
+	//go:embed compiled_contracts/ERC20MaliciousDelayed.json
+	ERC20MaliciousDelayedJSON []byte // nolint: golint
+
+	// ERC20MaliciousDelayedContract is the compiled erc20 contract
+	ERC20MaliciousDelayedContract evmtypes.CompiledContract
+
+	// ERC20MaliciousDelayedAddress is the erc20 module address
+	ERC20MaliciousDelayedAddress common.Address
+)
+
+func init() {
+	ERC20MaliciousDelayedAddress = types.ModuleAddress
+
+	err := json.Unmarshal(ERC20MaliciousDelayedJSON, &ERC20MaliciousDelayedContract)
+	if err != nil {
+		panic(err)
+	}
+
+	if len(ERC20MaliciousDelayedContract.Bin) == 0 {
+		panic("load contract failed")
+	}
+}
diff --git a/contracts/package-lock.json b/contracts/package-lock.json
new file mode 100644
index 00000000..f00733d6
--- /dev/null
+++ b/contracts/package-lock.json
@@ -0,0 +1,13 @@
+{
+  "name": "canto",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@openzeppelin/contracts": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.4.2.tgz",
+      "integrity": "sha512-NyJV7sJgoGYqbtNUWgzzOGW4T6rR19FmX1IJgXGdapGPWsuMelGJn9h03nos0iqfforCbCB0iYIR0MtIuIFLLw=="
+    }
+  }
+}
diff --git a/contracts/package.json b/contracts/package.json
new file mode 100644
index 00000000..b40e940d
--- /dev/null
+++ b/contracts/package.json
@@ -0,0 +1,26 @@
+{
+  "name": "canto",
+  "version": "1.0.0",
+  "description": "<!-- parent:   order: false -->",
+  "main": "index.js",
+  "directories": {
+    "doc": "docs"
+  },
+  "dependencies": {
+    "@openzeppelin/contracts": "^4.4.2"
+  },
+  "devDependencies": {},
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/canto/canto.git"
+  },
+  "author": "",
+  "license": "ISC",
+  "bugs": {
+    "url": "https://github.com/canto/canto/issues"
+  },
+  "homepage": "https://github.com/canto/canto#readme"
+}
diff --git a/contracts/turnstile.sol b/contracts/turnstile.sol
new file mode 100644
index 00000000..2b7b23e3
--- /dev/null
+++ b/contracts/turnstile.sol
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPLv3
+pragma solidity 0.8.17;
+
+import "@openzeppelin/contracts/access/Ownable.sol";
+import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
+import "@openzeppelin/contracts/utils/Counters.sol";
+
+/// @notice Implementation of CIP-001 https://github.com/Canto-Improvement-Proposals/CIPs/blob/main/CIP-001.md
+/// @dev Every contract is responsible to register itself in the constructor by calling `register(address)`.
+///      If contract is using proxy pattern, it's possible to register retroactively, however past fees will be lost.
+///      Recipient withdraws fees by calling `withdraw(uint256,address,uint256)`.
+contract Turnstile is Ownable, ERC721Enumerable {
+    using Counters for Counters.Counter;
+
+    struct NftData {
+        uint256 tokenId;
+        bool registered;
+    }
+
+    Counters.Counter private _tokenIdTracker;
+
+    /// @notice maps smart contract address to tokenId
+    mapping(address => NftData) public feeRecipient;
+
+    /// @notice maps tokenId to fees earned
+    mapping(uint256 => uint256) public balances;
+
+    event Register(address smartContract, address recipient, uint256 tokenId);
+    event Assign(address smartContract, uint256 tokenId);
+    event Withdraw(uint256 tokenId, address recipient, uint256 feeAmount);
+    event DistributeFees(uint256 tokenId, uint256 feeAmount);
+
+    error NotAnOwner();
+    error NotSmartContract();
+    error AlreadyRegistered();
+    error Unregistered();
+    error InvalidRecipient();
+    error InvalidTokenId();
+    error NothingToWithdraw();
+    error NothingToDistribute();
+
+    /// @dev only owner of _tokenId can call this function
+    modifier onlyNftOwner(uint256 _tokenId) {
+        if (ownerOf(_tokenId) != msg.sender) revert NotAnOwner();
+
+        _;
+    }
+
+    /// @dev only smart contract that is unregistered can call this function
+    modifier onlyUnregistered() {
+        address smartContract = msg.sender;
+
+        if (isRegistered(smartContract)) revert AlreadyRegistered();
+
+        _;
+    }
+
+    constructor() ERC721("Turnstile", "Turnstile") {}
+
+    /// @notice Returns current value of counter used to tokenId of new minted NFTs
+    /// @return current counter value
+    function currentCounterId() external view returns (uint256) {
+        return _tokenIdTracker.current();
+    }
+
+    /// @notice Returns tokenId that collects fees generated by the smart contract
+    /// @param _smartContract address of the smart contract
+    /// @return tokenId that collects fees generated by the smart contract
+    function getTokenId(address _smartContract) external view returns (uint256) {
+        if (!isRegistered(_smartContract)) revert Unregistered();
+
+        return feeRecipient[_smartContract].tokenId;
+    }
+
+    /// @notice Returns true if smart contract is registered to collect fees
+    /// @param _smartContract address of the smart contract
+    /// @return true if smart contract is registered to collect fees, false otherwise
+    function isRegistered(address _smartContract) public view returns (bool) {
+        return feeRecipient[_smartContract].registered;
+    }
+
+    /// @notice Mints ownership NFT that allows the owner to collect fees earned by the smart contract.
+    ///         `msg.sender` is assumed to be a smart contract that earns fees. Only smart contract itself
+    ///         can register a fee receipient.
+    /// @param _recipient recipient of the ownership NFT
+    /// @return tokenId of the ownership NFT that collects fees
+    function register(address _recipient) public onlyUnregistered returns (uint256 tokenId) {
+        address smartContract = msg.sender;
+
+        if (_recipient == address(0)) revert InvalidRecipient();
+
+        tokenId = _tokenIdTracker.current();
+        _mint(_recipient, tokenId);
+        _tokenIdTracker.increment();
+
+        emit Register(smartContract, _recipient, tokenId);
+
+        feeRecipient[smartContract] = NftData({
+            tokenId: tokenId,
+            registered: true
+        });
+    }
+
+    /// @notice Assigns smart contract to existing NFT. That NFT will collect fees generated by the smart contract.
+    ///         Callable only by smart contract itself.
+    /// @param _tokenId tokenId which will collect fees
+    /// @return tokenId of the ownership NFT that collects fees
+    function assign(uint256 _tokenId) public onlyUnregistered returns (uint256) {
+        address smartContract = msg.sender;
+
+        if (!_exists(_tokenId)) revert InvalidTokenId();
+
+        emit Assign(smartContract, _tokenId);
+
+        feeRecipient[smartContract] = NftData({
+            tokenId: _tokenId,
+            registered: true
+        });
+
+        return _tokenId;
+    }
+
+    /// @notice Withdraws earned fees to `_recipient` address. Only callable by NFT owner.
+    /// @param _tokenId token Id
+    /// @param _recipient recipient of fees
+    /// @param _amount amount of fees to withdraw
+    /// @return amount of fees withdrawn
+    function withdraw(uint256 _tokenId, address payable _recipient, uint256 _amount)
+        public
+        onlyNftOwner(_tokenId)
+        returns (uint256)
+    {
+        uint256 earnedFees = balances[_tokenId];
+
+        if (earnedFees == 0 || _amount == 0) revert NothingToWithdraw();
+        if (_amount > earnedFees) _amount = earnedFees;
+
+        balances[_tokenId] = earnedFees - _amount;
+
+        emit Withdraw(_tokenId, _recipient, _amount);
+
+        Address.sendValue(_recipient, _amount);
+
+        return _amount;
+    }
+
+    /// @notice Distributes collected fees to the smart contract. Only callable by owner.
+    /// @param _tokenId NFT that earned fees
+    function distributeFees(uint256 _tokenId) public onlyOwner payable {
+        if (msg.value == 0) revert NothingToDistribute();
+
+        balances[_tokenId] += msg.value;
+        emit DistributeFees(_tokenId, msg.value);
+    }
+}
\ No newline at end of file

From b190479f8b99832918d87ee2b17a1a692deec79b Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Mon, 7 Oct 2024 15:20:54 -0400
Subject: [PATCH 07/63] Add Multicall3 to test env

---
 integration_tests/test_runner/src/bin/main.rs |  3 ++
 .../test_runner/src/bootstrapping.rs          | 43 +++++++++++++++++++
 solidity-dex                                  |  2 +-
 3 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/integration_tests/test_runner/src/bin/main.rs b/integration_tests/test_runner/src/bin/main.rs
index 965fd6b7..b391fb67 100644
--- a/integration_tests/test_runner/src/bin/main.rs
+++ b/integration_tests/test_runner/src/bin/main.rs
@@ -10,6 +10,7 @@ use deep_space::Contact;
 use deep_space::PrivateKey;
 use std::env;
 use test_runner::bootstrapping::deploy_dex;
+use test_runner::bootstrapping::deploy_multicall;
 use test_runner::bootstrapping::parse_contract_addresses;
 use test_runner::bootstrapping::parse_dex_contract_addresses;
 use test_runner::bootstrapping::parse_ibc_validator_keys;
@@ -68,6 +69,8 @@ pub async fn main() {
         deploy_contracts(&contact).await;
         info!("Deploying DEX");
         deploy_dex().await;
+        info!("Deploying Multicall3");
+        deploy_multicall().await;
         return;
     }
 
diff --git a/integration_tests/test_runner/src/bootstrapping.rs b/integration_tests/test_runner/src/bootstrapping.rs
index 635a27b1..f163aa08 100644
--- a/integration_tests/test_runner/src/bootstrapping.rs
+++ b/integration_tests/test_runner/src/bootstrapping.rs
@@ -186,6 +186,49 @@ pub async fn deploy_dex() {
     file.write_all(&output.stdout).unwrap();
 }
 
+/// This function deploys Multicall3, which is used by various frontends
+pub async fn deploy_multicall() {
+    const A: [&str; 1] = ["multicall-deployer"];
+    // files are placed in a root /solidity-dex/ folder
+    const B: [&str; 1] = ["/solidity-dex/multicall-deployer"];
+    // the default unmoved locations for the Gravity repo
+    const C: [&str; 2] = [
+        "/althea/solidity-dex/misc/scripts/multicall-deployer.ts",
+        "/althea/solidity-dex/",
+    ];
+    let output = if all_paths_exist(&A) || all_paths_exist(&B) {
+        let paths = return_existing(A, B);
+        Command::new(paths[0])
+            .args([
+                &format!("--eth-node={}", ETH_NODE.as_str()),
+                &format!("--eth-privkey={:#x}", *MINER_PRIVATE_KEY),
+            ])
+            .output()
+            .expect("Failed to deploy contracts!")
+    } else if all_paths_exist(&C) {
+        Command::new("npx")
+            .args([
+                "ts-node",
+                C[0],
+                &format!("--eth-node={}", ETH_NODE.as_str()),
+                &format!("--eth-privkey={:#x}", *MINER_PRIVATE_KEY),
+            ])
+            .current_dir(C[1])
+            .output()
+            .expect("Failed to deploy contracts!")
+    } else {
+        panic!("Could not find json contract artifacts in any known location!")
+    };
+
+    info!("stdout: {}", String::from_utf8_lossy(&output.stdout));
+    info!("stderr: {}", String::from_utf8_lossy(&output.stderr));
+    if !ExitStatus::success(&output.status) {
+        panic!("Contract deploy failed!")
+    }
+    let mut file = File::create("/multicall-contract").unwrap();
+    file.write_all(&output.stdout).unwrap();
+}
+
 // TODO: Fix send_erc20_bulk to make this method not so slow
 pub async fn send_erc20s_to_evm_users(
     web3: &Web3,
diff --git a/solidity-dex b/solidity-dex
index 48df2d75..144348db 160000
--- a/solidity-dex
+++ b/solidity-dex
@@ -1 +1 @@
-Subproject commit 48df2d75f968ed608a7aa94ea73f56c451dda3b7
+Subproject commit 144348db7f7179c5fda65a4f0bf66247fe917bb6

From b66792d07061a11107e4df58ef716eaa88360284 Mon Sep 17 00:00:00 2001
From: Justin Kilpatrick <justin@althea.net>
Date: Tue, 8 Oct 2024 15:36:03 -0400
Subject: [PATCH 08/63] Run Integration tests natively in CI

This patch modifies the integration tests to run natively, as in without
a container wrapping them, in github actions CI.
---
 .github/workflows/integration-tests.yml       | 192 ++++++++----------
 integration_tests/Cargo.lock                  |  10 +-
 integration_tests/test_runner/src/bin/main.rs |   5 +-
 .../test_runner/src/bootstrapping.rs          | 186 ++++++++---------
 .../test_runner/src/tests/mod.rs              |   2 +-
 integration_tests/test_runner/src/utils.rs    |  12 +-
 solidity-dex                                  |   2 +-
 solidity/contract-deployer.ts                 | 138 +++++--------
 tests/all-up-test-ci.sh                       |  61 ++++++
 tests/container-scripts/run-testnet.sh        |   2 +-
 10 files changed, 306 insertions(+), 304 deletions(-)
 create mode 100755 tests/all-up-test-ci.sh

diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
index 2ade30ea..7fb20a8b 100644
--- a/.github/workflows/integration-tests.yml
+++ b/.github/workflows/integration-tests.yml
@@ -14,9 +14,10 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - uses: Swatinem/rust-cache@v1
+      - uses: Swatinem/rust-cache@v2
         with:
-          working-directory: integration-tests/
+          workspaces: integration_tests/
+          cache-on-failure: true
       - name: Build Integration Tests
         run: cd integration_tests && cargo check --all --verbose
   native_token:
@@ -24,211 +25,178 @@ jobs:
     needs: build
     steps:
       - uses: actions/checkout@v2
-      - uses: jpribyl/action-docker-layer-caching@v0.1.1
-        continue-on-error: true
+      - uses: actions/setup-go@v5
+      - uses: Swatinem/rust-cache@v2
         with:
-          key: integration-test-cache-{hash}
-          restore-keys: |
-            integration-test-cache-
-      - name: Prune cache to keep the size down
-        run: docker builder prune -af && docker system prune -af
+          workspaces: integration_tests/
+          cache-on-failure: true
       - name: Test the native token features of the EVM
-        run: tests/all-up-test.sh NATIVE_TOKEN
+        run: tests/all-up-test-ci.sh NATIVE_TOKEN
   lockup:
     runs-on: ubuntu-latest
     needs: native_token
     steps:
       - uses: actions/checkout@v2
-      - uses: jpribyl/action-docker-layer-caching@v0.1.1
+      - uses: actions/setup-go@v5
+      - uses: Swatinem/rust-cache@v2
         with:
-          key: integration-test-cache-{hash}
-          restore-keys: |
-            integration-test-cache-
+          workspaces: integration_tests/
+          cache-on-failure: true
       - name: Lock up the chain and ensure funds are not transferrable
-        run: tests/all-up-test.sh LOCKUP
-        env:
-          NO_IMAGE_BUILD: True
+        run: tests/all-up-test-ci.sh LOCKUP
   microtx_fees:
     runs-on: ubuntu-latest
     needs: native_token
     steps:
       - uses: actions/checkout@v2
-      - uses: jpribyl/action-docker-layer-caching@v0.1.1
+      - uses: actions/setup-go@v5
+      - uses: Swatinem/rust-cache@v2
         with:
-          key: integration-test-cache-{hash}
-          restore-keys: |
-            integration-test-cache-
+          workspaces: integration_tests/
+          cache-on-failure: true
       - name: Assert that fees are collected by the microtx module
-        run: tests/all-up-test.sh MICROTX_FEES
-        env:
-          NO_IMAGE_BUILD: True
+        run: tests/all-up-test-ci.sh MICROTX_FEES
   erc20_conversion:
     needs: native_token
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - uses: jpribyl/action-docker-layer-caching@v0.1.1
+      - uses: actions/setup-go@v5
+      - uses: Swatinem/rust-cache@v2
         with:
-          key: integration-test-cache-{hash}
-          restore-keys: |
-            integration-test-cache-
+          workspaces: integration_tests/
+          cache-on-failure: true
       - name: Test the erc20 module's token conversion functionality
-        run: tests/all-up-test.sh ERC20_CONVERSION
-        env:
-          NO_IMAGE_BUILD: True
+        run: tests/all-up-test-ci.sh ERC20_CONVERSION
   liquid_accounts:
     needs: native_token
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - uses: jpribyl/action-docker-layer-caching@v0.1.1
+      - uses: actions/setup-go@v5
+      - uses: Swatinem/rust-cache@v2
         with:
-          key: integration-test-cache-{hash}
-          restore-keys: |
-            integration-test-cache-
+          workspaces: integration_tests/
+          cache-on-failure: true
       - name: Test the microtx module's liquid infrastructure accounts functions
-        run: tests/all-up-test.sh LIQUID_ACCOUNTS
-        env:
-          NO_IMAGE_BUILD: True
+        run: tests/all-up-test-ci.sh LIQUID_ACCOUNTS
   ica_host:
-    needs: native_token
+    #needs: native_token
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - uses: jpribyl/action-docker-layer-caching@v0.1.1
+      - uses: actions/setup-go@v5
+      - uses: Swatinem/rust-cache@v2
         with:
-          key: integration-test-cache-{hash}
-          restore-keys: |
-            integration-test-cache-
+          workspaces: integration_tests/
+          cache-on-failure: true
       - name: Test the interchain accounts host module on Althea-L1
-        run: tests/all-up-test.sh ICA_HOST
-        env:
-          NO_IMAGE_BUILD: True
+        run: tests/all-up-test-ci.sh ICA_HOST
   ONBOARDING_DEFAULT_PARAMS:
     needs: native_token
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - uses: jpribyl/action-docker-layer-caching@v0.1.1
+      - uses: actions/setup-go@v5
+      - uses: Swatinem/rust-cache@v2
         with:
-          key: integration-test-cache-{hash}
-          restore-keys: |
-            integration-test-cache-
+          workspaces: integration_tests/
+          cache-on-failure: true
       - name: Starts the onboarding module with the default params
-        run: tests/all-up-test.sh ONBOARDING_DEFAULT_PARAMS
-        env:
-          NO_IMAGE_BUILD: True
+        run: tests/all-up-test-ci.sh ONBOARDING_DEFAULT_PARAMS
   ONBOARDING_DISABLED_WHITELISTED:
     needs: native_token
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - uses: jpribyl/action-docker-layer-caching@v0.1.1
+      - uses: actions/setup-go@v5
+      - uses: Swatinem/rust-cache@v2
         with:
-          key: integration-test-cache-{hash}
-          restore-keys: |
-            integration-test-cache-
+          workspaces: integration_tests/
+          cache-on-failure: true
       - name: Starts the onboarding module disabled with a whitelisted channel
-        run: tests/all-up-test.sh ONBOARDING_DISABLED_WHITELISTED
-        env:
-          NO_IMAGE_BUILD: True
+        run: tests/all-up-test-ci.sh ONBOARDING_DISABLED_WHITELISTED
   ONBOARDING_DISABLE_AFTER:
     needs: native_token
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - uses: jpribyl/action-docker-layer-caching@v0.1.1
+      - uses: actions/setup-go@v5
+      - uses: Swatinem/rust-cache@v2
         with:
-          key: integration-test-cache-{hash}
-          restore-keys: |
-            integration-test-cache-
+          workspaces: integration_tests/
+          cache-on-failure: true
       - name: Starts the onboarding module permissively, but disables it after a transfer
-        run: tests/all-up-test.sh ONBOARDING_DISABLE_AFTER
-        env:
-          NO_IMAGE_BUILD: True
+        run: tests/all-up-test-ci.sh ONBOARDING_DISABLE_AFTER
   ONBOARDING_DELIST_AFTER:
     needs: native_token
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - uses: jpribyl/action-docker-layer-caching@v0.1.1
+      - uses: actions/setup-go@v5
+      - uses: Swatinem/rust-cache@v2
         with:
-          key: integration-test-cache-{hash}
-          restore-keys: |
-            integration-test-cache-
+          workspaces: integration_tests/
+          cache-on-failure: true
       - name: Starts the onboarding module permissively, but removes the channel from the whilelist after a transfer
-        run: tests/all-up-test.sh ONBOARDING_DELIST_AFTER
-        env:
-          NO_IMAGE_BUILD: True
+        run: tests/all-up-test-ci.sh ONBOARDING_DELIST_AFTER
   DEX:
     needs: native_token
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - uses: jpribyl/action-docker-layer-caching@v0.1.1
+      - uses: actions/setup-go@v5
+      - uses: Swatinem/rust-cache@v2
         with:
-          key: integration-test-cache-{hash}
-          restore-keys: |
-            integration-test-cache-
+          workspaces: integration_tests/
+          cache-on-failure: true
       - name: Performs basic DEX tests
-        run: tests/all-up-test.sh DEX
-        env:
-          NO_IMAGE_BUILD: True
+        run: tests/all-up-test-ci.sh DEX
   DEX_UPGRADE:
     needs: native_token
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - uses: jpribyl/action-docker-layer-caching@v0.1.1
+      - uses: actions/setup-go@v5
+      - uses: Swatinem/rust-cache@v2
         with:
-          key: integration-test-cache-{hash}
-          restore-keys: |
-            integration-test-cache-
+          workspaces: integration_tests/
+          cache-on-failure: true
       - name: Tests the DEX callpath upgrade functionality
-        run: tests/all-up-test.sh DEX_UPGRADE
-        env:
-          NO_IMAGE_BUILD: True
+        run: tests/all-up-test-ci.sh DEX_UPGRADE
   DEX_SAFE_MODE:
     needs: native_token
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - uses: jpribyl/action-docker-layer-caching@v0.1.1
+      - uses: actions/setup-go@v5
+      - uses: Swatinem/rust-cache@v2
         with:
-          key: integration-test-cache-{hash}
-          restore-keys: |
-            integration-test-cache-
+          workspaces: integration_tests/
+          cache-on-failure: true
       - name: Tests the DEX safe mode lockdown functionality
-        run: tests/all-up-test.sh DEX_SAFE_MODE
-        env:
-          NO_IMAGE_BUILD: True
+        run: tests/all-up-test-ci.sh DEX_SAFE_MODE
   DEX_OPS_PROPOSAL:
     needs: native_token
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - uses: jpribyl/action-docker-layer-caching@v0.1.1
+      - uses: actions/setup-go@v5
+      - uses: Swatinem/rust-cache@v2
         with:
-          key: integration-test-cache-{hash}
-          restore-keys: |
-            integration-test-cache-
+          workspaces: integration_tests/
+          cache-on-failure: true
       - name: Tests the nativedex OpsProposal function
-        run: tests/all-up-test.sh DEX_OPS_PROPOSAL
-        env:
-          NO_IMAGE_BUILD: True
+        run: tests/all-up-test-ci.sh DEX_OPS_PROPOSAL
   UPGRADE:
     needs: native_token
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - uses: jpribyl/action-docker-layer-caching@v0.1.1
+      - uses: actions/setup-go@v5
+      - uses: Swatinem/rust-cache@v2
         with:
-          key: integration-test-cache-{hash}
-          restore-keys: |
-            integration-test-cache-
-      - name: Tests the nativedex OpsProposal function
-        run: tests/all-up-test.sh DEX_OPS_PROPOSAL
-      - name: Tests the Neutrino
-        run: tests/run-upgrade-test.sh v1.4.0
-        env:
-          NO_IMAGE_BUILD: True
+          workspaces: integration_tests/
+          cache-on-failure: true
+      - name: Tests the Neutrino upgrade
+        run: tests/run-upgrade-test.sh v1.4.0
\ No newline at end of file
diff --git a/integration_tests/Cargo.lock b/integration_tests/Cargo.lock
index 843feb29..132f5e17 100644
--- a/integration_tests/Cargo.lock
+++ b/integration_tests/Cargo.lock
@@ -1364,11 +1364,11 @@ checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
 
 [[package]]
 name = "openssl"
-version = "0.10.55"
+version = "0.10.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d"
+checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.5.0",
  "cfg-if",
  "foreign-types",
  "libc",
@@ -1396,9 +1396,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.90"
+version = "0.9.103"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6"
+checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
 dependencies = [
  "cc",
  "libc",
diff --git a/integration_tests/test_runner/src/bin/main.rs b/integration_tests/test_runner/src/bin/main.rs
index b391fb67..ace25b52 100644
--- a/integration_tests/test_runner/src/bin/main.rs
+++ b/integration_tests/test_runner/src/bin/main.rs
@@ -4,7 +4,6 @@
 #[macro_use]
 extern crate log;
 
-use althea_proto::cosmos_sdk_proto::ibc;
 use deep_space::Coin;
 use deep_space::Contact;
 use deep_space::PrivateKey;
@@ -16,7 +15,7 @@ use test_runner::bootstrapping::parse_dex_contract_addresses;
 use test_runner::bootstrapping::parse_ibc_validator_keys;
 use test_runner::bootstrapping::send_erc20s_to_evm_users;
 use test_runner::bootstrapping::start_ibc_relayer;
-use test_runner::bootstrapping::{deploy_contracts, get_keys};
+use test_runner::bootstrapping::{deploy_erc20_contracts, get_keys};
 use test_runner::tests::dex::dex_ops_proposal_test;
 use test_runner::tests::dex::dex_safe_mode_test;
 use test_runner::tests::dex::dex_test;
@@ -66,7 +65,7 @@ pub async fn main() {
 
     if should_deploy_contracts() {
         info!("test-runner in contract deploying mode, deploying contracts, then exiting");
-        deploy_contracts(&contact).await;
+        deploy_erc20_contracts(&contact).await;
         info!("Deploying DEX");
         deploy_dex().await;
         info!("Deploying Multicall3");
diff --git a/integration_tests/test_runner/src/bootstrapping.rs b/integration_tests/test_runner/src/bootstrapping.rs
index f163aa08..99d53377 100644
--- a/integration_tests/test_runner/src/bootstrapping.rs
+++ b/integration_tests/test_runner/src/bootstrapping.rs
@@ -91,7 +91,7 @@ pub fn parse_ibc_validator_keys() -> (Vec<CosmosPrivateKey>, Vec<String>) {
 /// this runs only when the DEPLOY_CONTRACTS env var is set right after
 /// the Ethereum test chain starts in the testing environment. We write
 /// the stdout of this to a file for later test runs to parse
-pub async fn deploy_contracts(contact: &Contact) {
+pub async fn deploy_erc20_contracts(contact: &Contact) {
     // prevents the node deployer from failing (rarely) when the chain has not
     // yet produced the next block after submitting each eth address
     contact.wait_for_next_block(TOTAL_TIMEOUT).await.unwrap();
@@ -100,132 +100,113 @@ pub async fn deploy_contracts(contact: &Contact) {
     // and the gravity contract itself, feel free to expand this if it makes your
     // deployments more straightforward.
 
-    // both files are just in the PWD
-    const A: [&str; 1] = ["contract-deployer"];
-    // files are placed in a root /solidity/ folder
-    const B: [&str; 1] = ["/solidity/contract-deployer"];
     // the default unmoved locations for the Gravity repo
-    const C: [&str; 2] = ["/althea/solidity/contract-deployer.ts", "/althea/solidity/"];
-    let output = if all_paths_exist(&A) || all_paths_exist(&B) {
-        let paths = return_existing(A, B);
-        Command::new(paths[0])
-            .args([
-                &format!("--eth-node={}", ETH_NODE.as_str()),
-                &format!("--eth-privkey={:#x}", *MINER_PRIVATE_KEY),
-                "--test-mode=true",
-            ])
-            .output()
-            .expect("Failed to deploy contracts!")
-    } else if all_paths_exist(&C) {
-        Command::new("npx")
+    const A: [&str; 2] = ["/althea/solidity/contract-deployer.ts", "/althea/solidity/"];
+    // the default unmoved locations for Github Actions
+    const B: [&str; 2] = [
+        "/home/runner/work/althea-L1/althea-L1/solidity/contract-deployer.ts",
+        "/home/runner/work/althea-L1/althea-L1/solidity/",
+    ];
+    let output = match return_existing(vec![A, B]) {
+        Some(path) => Command::new("npx")
             .args([
                 "ts-node",
-                C[0],
+                path[0],
                 &format!("--eth-node={}", ETH_NODE.as_str()),
                 &format!("--eth-privkey={:#x}", *MINER_PRIVATE_KEY),
-                "--test-mode=true",
+                &format!("--artifacts-root={}", path[1]),
             ])
-            .current_dir(C[1])
+            .current_dir(path[1])
             .output()
-            .expect("Failed to deploy contracts!")
-    } else {
-        panic!("Could not find json contract artifacts in any known location!")
+            .expect("Failed to deploy contracts!"),
+        None => {
+            panic!("Could not find json contract artifacts in any known location!")
+        }
     };
-
     info!("stdout: {}", String::from_utf8_lossy(&output.stdout));
     info!("stderr: {}", String::from_utf8_lossy(&output.stderr));
     if !ExitStatus::success(&output.status) {
         panic!("Contract deploy failed!")
     }
-    let mut file = File::create("/contracts").unwrap();
+    let mut file = File::create(ERC20_CONTRACTS_FILE).unwrap();
     file.write_all(&output.stdout).unwrap();
 }
 
+/// The file where the contract addresses are stored
+const DEX_CONTRACTS_FILE: &str = "/tmp/dex-contracts";
+
 /// This function deploys the Ambient (aka CrocSwap) dex contracts and configures them
 pub async fn deploy_dex() {
-    const A: [&str; 1] = ["dex-deployer"];
-    // files are placed in a root /solidity-dex/ folder
-    const B: [&str; 1] = ["/solidity-dex/dex-deployer"];
-    // the default unmoved locations for the Gravity repo
-    const C: [&str; 2] = [
+    // the default unmoved locations for the Gravity repo in the docker container
+    const A: [&str; 2] = [
         "/althea/solidity-dex/misc/scripts/dex-deployer.ts",
-        "/althea/solidity-dex/",
+        "/althea/solidity-dex/artifacts/contracts/",
     ];
-    let output = if all_paths_exist(&A) || all_paths_exist(&B) {
-        let paths = return_existing(A, B);
-        Command::new(paths[0])
-            .args([
-                &format!("--eth-node={}", ETH_NODE.as_str()),
-                &format!("--eth-privkey={:#x}", *MINER_PRIVATE_KEY),
-                "--test-mode=true",
-            ])
-            .output()
-            .expect("Failed to deploy contracts!")
-    } else if all_paths_exist(&C) {
-        Command::new("npx")
+    // the default unmoved locations for Github Actions
+    const B: [&str; 2] = [
+        "/home/runner/work/althea-L1/althea-L1/solidity-dex/misc/scripts/dex-deployer.ts",
+        "/home/runner/work/althea-L1/althea-L1/solidity-dex/artifacts/contracts/",
+    ];
+    let output = match return_existing(vec![A, B]) {
+        Some(path) => Command::new("npx")
             .args([
                 "ts-node",
-                C[0],
+                path[0],
                 &format!("--eth-node={}", ETH_NODE.as_str()),
                 &format!("--eth-privkey={:#x}", *MINER_PRIVATE_KEY),
-                "--test-mode=true",
+                &format!("--artifacts-root={}", path[1]),
             ])
-            .current_dir(C[1])
+            .current_dir(path[1])
             .output()
-            .expect("Failed to deploy contracts!")
-    } else {
-        panic!("Could not find json contract artifacts in any known location!")
+            .expect("Failed to deploy contracts!"),
+        None => {
+            panic!("Could not find dex artifacts in any known location!")
+        }
     };
-
     info!("stdout: {}", String::from_utf8_lossy(&output.stdout));
     info!("stderr: {}", String::from_utf8_lossy(&output.stderr));
     if !ExitStatus::success(&output.status) {
         panic!("Contract deploy failed!")
     }
-    let mut file = File::create("/dex-contracts").unwrap();
+    let mut file = File::create(DEX_CONTRACTS_FILE).unwrap();
     file.write_all(&output.stdout).unwrap();
 }
 
 /// This function deploys Multicall3, which is used by various frontends
 pub async fn deploy_multicall() {
-    const A: [&str; 1] = ["multicall-deployer"];
-    // files are placed in a root /solidity-dex/ folder
-    const B: [&str; 1] = ["/solidity-dex/multicall-deployer"];
-    // the default unmoved locations for the Gravity repo
-    const C: [&str; 2] = [
+    // the default unmoved locations for the Gravity repo in the docker container
+    const A: [&str; 2] = [
         "/althea/solidity-dex/misc/scripts/multicall-deployer.ts",
-        "/althea/solidity-dex/",
+        "/althea/solidity-dex/artifacts/contracts/periphery/",
     ];
-    let output = if all_paths_exist(&A) || all_paths_exist(&B) {
-        let paths = return_existing(A, B);
-        Command::new(paths[0])
-            .args([
-                &format!("--eth-node={}", ETH_NODE.as_str()),
-                &format!("--eth-privkey={:#x}", *MINER_PRIVATE_KEY),
-            ])
-            .output()
-            .expect("Failed to deploy contracts!")
-    } else if all_paths_exist(&C) {
-        Command::new("npx")
+    // locations for Github Actions
+    const B: [&str; 2] = [
+        "/home/runner/work/althea-L1/althea-L1/solidity-dex/misc/scripts/multicall-deployer.ts",
+        "/home/runner/work/althea-L1/althea-L1/solidity-dex/artifacts/contracts/periphery/",
+    ];
+    let paths = vec![A, B];
+    let output = match return_existing(paths) {
+        Some(path) => Command::new("npx")
             .args([
                 "ts-node",
-                C[0],
+                path[0],
                 &format!("--eth-node={}", ETH_NODE.as_str()),
                 &format!("--eth-privkey={:#x}", *MINER_PRIVATE_KEY),
+                &format!("--artifacts-root={}", path[1]),
             ])
-            .current_dir(C[1])
+            .current_dir(path[1])
             .output()
-            .expect("Failed to deploy contracts!")
-    } else {
-        panic!("Could not find json contract artifacts in any known location!")
+            .expect("Failed to deploy contracts!"),
+        None => {
+            panic!("Could not find json contract artifacts in any known location!")
+        }
     };
-
     info!("stdout: {}", String::from_utf8_lossy(&output.stdout));
     info!("stderr: {}", String::from_utf8_lossy(&output.stderr));
     if !ExitStatus::success(&output.status) {
         panic!("Contract deploy failed!")
     }
-    let mut file = File::create("/multicall-contract").unwrap();
+    let mut file = File::create("/tmp/multicall-contract").unwrap();
     file.write_all(&output.stdout).unwrap();
 }
 
@@ -268,14 +249,8 @@ fn all_paths_exist(input: &[&str]) -> bool {
     true
 }
 
-fn return_existing<'a>(a: [&'a str; 1], b: [&'a str; 1]) -> [&'a str; 1] {
-    if all_paths_exist(&a) {
-        a
-    } else if all_paths_exist(&b) {
-        b
-    } else {
-        panic!("No paths exist!")
-    }
+fn return_existing(paths: Vec<[&str; 2]>) -> Option<[&str; 2]> {
+    paths.into_iter().find(|&path| all_paths_exist(&path))
 }
 
 pub struct BootstrapContractAddresses {
@@ -284,11 +259,13 @@ pub struct BootstrapContractAddresses {
     pub uniswap_liquidity_address: Option<EthAddress>,
 }
 
+pub const ERC20_CONTRACTS_FILE: &str = "/tmp/contracts";
+
 /// Parses the ERC20 and Gravity contract addresses from the file created
 /// in deploy_contracts()
 pub fn parse_contract_addresses() -> BootstrapContractAddresses {
     let mut file =
-        File::open("/contracts").expect("Failed to find contracts! did they not deploy?");
+        File::open(ERC20_CONTRACTS_FILE).expect("Failed to find contracts! did they not deploy?");
     let mut output = String::new();
     file.read_to_string(&mut output).unwrap();
     let mut erc20_addresses = Vec::new();
@@ -328,7 +305,7 @@ pub struct DexAddresses {
 /// in deploy_dex()
 pub fn parse_dex_contract_addresses() -> DexAddresses {
     let mut file =
-        File::open("/dex-contracts").expect("Failed to find dex contracts! did they not deploy?");
+        File::open(DEX_CONTRACTS_FILE).expect("Failed to find dex contracts! did they not deploy?");
     let mut output = String::new();
     file.read_to_string(&mut output).unwrap();
     let mut dex: EthAddress = EthAddress::default();
@@ -372,6 +349,14 @@ pub fn parse_dex_contract_addresses() -> DexAddresses {
 // Hermes stores its keys in hermes_home/
 pub fn setup_relayer_keys() -> Result<(), Box<dyn std::error::Error>> {
     let mut command = hermes_base();
+    let mut mnemonic_path: Option<&str> = None;
+    for path in RELAYER_MNEMONIC_FILE {
+        if Path::new(path).exists() {
+            mnemonic_path = Some(path);
+            break;
+        }
+    }
+    let mnemonic_path = mnemonic_path.expect("Could not find relayer mnemonic file");
     let _althea_key = command
         .args([
             "keys",
@@ -383,7 +368,7 @@ pub fn setup_relayer_keys() -> Result<(), Box<dyn std::error::Error>> {
             "--hd-path",
             "m/44'/60'/0'/0/0",
             "--mnemonic-file",
-            RELAYER_MNEMONIC_FILE,
+            mnemonic_path,
         ])
         .spawn()
         .expect("Failed to add althea key");
@@ -399,7 +384,7 @@ pub fn setup_relayer_keys() -> Result<(), Box<dyn std::error::Error>> {
             "--chain",
             &get_ibc_chain_id(),
             "--mnemonic-file",
-            RELAYER_MNEMONIC_FILE,
+            mnemonic_path,
         ])
         .spawn()
         .expect("Failed to add ibc key");
@@ -408,6 +393,8 @@ pub fn setup_relayer_keys() -> Result<(), Box<dyn std::error::Error>> {
     Ok(())
 }
 
+const IBC_RELAYER_LOGS_ROOT: &str = "/tmp/ibc-relayer-logs/";
+
 // Create a channel between gravity chain and the ibc test chain over the "transfer" port
 // Writes the output to /ibc-relayer-logs/channel-creation
 pub fn create_ibc_channel(hermes_base: Command) {
@@ -429,9 +416,11 @@ pub fn create_ibc_channel(hermes_base: Command) {
         "--yes",
     ]);
 
+    std::fs::create_dir_all(IBC_RELAYER_LOGS_ROOT).unwrap();
     let out_file = File::options()
         .write(true)
-        .open("/ibc-relayer-logs/channel-creation")
+        .create(true)
+        .open(format!("{}channel-creation", IBC_RELAYER_LOGS_ROOT))
         .unwrap()
         .into_raw_fd();
     unsafe {
@@ -453,9 +442,11 @@ pub fn run_ibc_relayer(hermes_base: Command, full_scan: bool) {
     if full_scan {
         start = start.arg("--full-scan");
     }
+    std::fs::create_dir_all(IBC_RELAYER_LOGS_ROOT).unwrap();
     let out_file = File::options()
         .write(true)
-        .open("/ibc-relayer-logs/hermes-logs")
+        .create(true)
+        .open(format!("{}hermes-logs", IBC_RELAYER_LOGS_ROOT))
         .unwrap()
         .into_raw_fd();
     unsafe {
@@ -526,11 +517,22 @@ pub async fn start_ibc_relayer(
     thread::spawn(|| {
         run_ibc_relayer(hermes_base(), true); // likely will not return from here, just keep running
     });
-    info!("Running ibc relayer in the background, directing output to /ibc-relayer-logs");
+    info!(
+        "Running ibc relayer in the background, directing output to {}",
+        IBC_RELAYER_LOGS_ROOT
+    );
 }
 
 fn hermes_base() -> Command {
     let mut hermes_base = Command::new("hermes");
-    hermes_base.arg("--config").arg(HERMES_CONFIG);
+    let mut config: Option<&str> = None;
+    for path in HERMES_CONFIG {
+        if Path::new(path).exists() {
+            config = Some(path);
+            break;
+        }
+    }
+    let config = config.expect("Could not find hermes config file");
+    hermes_base.arg("--config").arg(config);
     hermes_base
 }
diff --git a/integration_tests/test_runner/src/tests/mod.rs b/integration_tests/test_runner/src/tests/mod.rs
index 1ebfcd3b..191584e3 100644
--- a/integration_tests/test_runner/src/tests/mod.rs
+++ b/integration_tests/test_runner/src/tests/mod.rs
@@ -6,4 +6,4 @@ pub mod lockup;
 pub mod microtx_fees;
 pub mod native_token;
 pub mod onboarding;
-pub mod upgrade;
\ No newline at end of file
+pub mod upgrade;
diff --git a/integration_tests/test_runner/src/utils.rs b/integration_tests/test_runner/src/utils.rs
index b6fa15c7..8c4a51ff 100644
--- a/integration_tests/test_runner/src/utils.rs
+++ b/integration_tests/test_runner/src/utils.rs
@@ -36,9 +36,15 @@ use web30::{client::Web3, jsonrpc::error::Web3Error, types::SendTxOption};
 pub const OPERATION_TIMEOUT: Duration = Duration::from_secs(30);
 /// the timeout for the total system
 pub const TOTAL_TIMEOUT: Duration = Duration::from_secs(300);
-// The config file location for hermes
-pub const HERMES_CONFIG: &str = "/althea/tests/assets/ibc-relayer-config.toml";
-pub const RELAYER_MNEMONIC_FILE: &str = "/althea/tests/assets/relayer-mnemonic.txt";
+// The config file location for hermes, two possibe locations are provided
+pub const HERMES_CONFIG: [&str; 2] = [
+    "/althea/tests/assets/ibc-relayer-config.toml",
+    "/home/runner/work/althea-L1/althea-L1/tests/assets/ibc-relayer-config.toml",
+];
+pub const RELAYER_MNEMONIC_FILE: [&str; 2] = [
+    "/althea/tests/assets/relayer-mnemonic.txt",
+    "/home/runner/work/althea-L1/althea-L1/tests/assets/relayer-mnemonic.txt",
+];
 pub const ALTHEA_RELAYER_ADDRESS: &str = "althea1zcr3730w7cwl5q7n28yuu3l9hmuq4w9j8rg8at";
 pub const IBC_RELAYER_ADDRESS: &str = "cosmos1vdv5jau58qxv2xgzw6fj3ql70txnpl08z9pngs";
 
diff --git a/solidity-dex b/solidity-dex
index 144348db..433af992 160000
--- a/solidity-dex
+++ b/solidity-dex
@@ -1 +1 @@
-Subproject commit 144348db7f7179c5fda65a4f0bf66247fe917bb6
+Subproject commit 433af992fda3f055026d9c10e7cc47f6515a6eb5
diff --git a/solidity/contract-deployer.ts b/solidity/contract-deployer.ts
index cd14cb02..ccb4621a 100644
--- a/solidity/contract-deployer.ts
+++ b/solidity/contract-deployer.ts
@@ -12,8 +12,8 @@ const args = commandLineArgs([
   { name: "eth-node", type: String },
   // the Ethereum private key that will contain the gas required to pay for the contact deployment
   { name: "eth-privkey", type: String },
-  // test mode, if enabled this script deploys three ERC20 contracts for testing
-  { name: "test-mode", type: String },
+  // path to the artifacts folder
+  { name: "artifacts-root", type: String },
 ]);
 
 // 4. Now, the deployer script hits a full node api, gets the Eth signatures of the valset from the latest block, and deploys the Ethereum contract.
@@ -51,96 +51,62 @@ async function deploy() {
   var startTime = new Date();
   const provider = await new ethers.providers.JsonRpcProvider(args["eth-node"]);
   let wallet = new ethers.Wallet(args["eth-privkey"], provider);
-  const testMode = args["test-mode"] == "True" || args["test-mode"] == "true";
-
-  if (testMode) {
-    var success = false;
-    while (!success) {
-      var present = new Date();
-      var timeDiff: number = present.getTime() - startTime.getTime();
-      timeDiff = timeDiff / 1000
-      provider.getBlockNumber().then(_ => success = true).catch(_ => console.log("Ethereum RPC error, trying again"))
-
-      if (timeDiff > 600) {
-        console.log("Could not contact Ethereum RPC after 10 minutes, check the URL!")
-        exit(1)
-      }
-      await sleep(1000);
-    }
-  }
+  let artifacts = args["artifacts-root"];
+
+  var success = false;
+  while (!success) {
+    var present = new Date();
+    var timeDiff: number = present.getTime() - startTime.getTime();
+    timeDiff = timeDiff / 1000
+    provider.getBlockNumber().then(_ => success = true).catch(_ => console.log("Ethereum RPC error, trying again"))
 
-  if (testMode) {
-    console.log("Test mode, deploying ERC20 contracts");
-
-    // this handles several possible locations for the ERC20 artifacts
-    var erc20_a_path: string
-    var erc20_b_path: string
-    var erc20_c_path: string
-    var erc721_a_path: string
-    const main_location_a = "/althea/solidity/artifacts/contracts/TestERC20A.sol/TestERC20A.json"
-    const main_location_b = "/althea/solidity/artifacts/contracts/TestERC20B.sol/TestERC20B.json"
-    const main_location_c = "/althea/solidity/artifacts/contracts/TestERC20C.sol/TestERC20C.json"
-    const main_location_721_a = "/althea/solidity/artifacts/contracts/TestERC721A.sol/TestERC721A.json"
-    
-    const alt_location_1_a = "/solidity/TestERC20A.json"
-    const alt_location_1_b = "/solidity/TestERC20B.json"
-    const alt_location_1_c = "/solidity/TestERC20C.json"
-    const alt_location_1_721a = "/solidity/TestERC721A.json"
-
-    const alt_location_2_a = "TestERC20A.json"
-    const alt_location_2_b = "TestERC20B.json"
-    const alt_location_2_c = "TestERC20C.json"
-    const alt_location_2_721a = "TestERC721A.json"
-
-    if (fs.existsSync(main_location_a)) {
-      erc20_a_path = main_location_a
-      erc20_b_path = main_location_b
-      erc20_c_path = main_location_c
-      erc721_a_path = main_location_721_a
-    } else if (fs.existsSync(alt_location_1_a)) {
-      erc20_a_path = alt_location_1_a
-      erc20_b_path = alt_location_1_b
-      erc20_c_path = alt_location_1_c
-      erc721_a_path = alt_location_1_721a
-    } else if (fs.existsSync(alt_location_2_a)) {
-      erc20_a_path = alt_location_2_a
-      erc20_b_path = alt_location_2_b
-      erc20_c_path = alt_location_2_c
-      erc721_a_path = alt_location_2_721a
-    } else {
-      console.log("Test mode was enabled but the ERC20 contracts can't be found!")
+    if (timeDiff > 600) {
+      console.log("Could not contact Ethereum RPC after 10 minutes, check the URL!")
       exit(1)
     }
+    await sleep(1000);
+  }
+
+  console.log("Deploying ERC20 contracts");
 
+  // this handles several possible locations for the ERC20 artifacts
+  var erc20_a_path: string = artifacts + "/artifacts/contracts/TestERC20A.sol/TestERC20A.json"
+  var erc20_b_path: string = artifacts + "/artifacts/contracts/TestERC20B.sol/TestERC20B.json"
+  var erc20_c_path: string = artifacts + "/artifacts/contracts/TestERC20C.sol/TestERC20C.json"
+  var erc721_a_path: string = artifacts + "/artifacts/contracts/TestERC721A.sol/TestERC721A.json"
 
-    const { abi, bytecode } = getContractArtifacts(erc20_a_path);
-    const erc20Factory = new ethers.ContractFactory(abi, bytecode, wallet);
-    const testERC20 = (await erc20Factory.deploy(overrides)) as TestERC20A;
-    await testERC20.deployed();
-    const erc20TestAddress = testERC20.address;
-    console.log("ERC20 deployed at Address - ", erc20TestAddress);
-
-    const { abi: abi1, bytecode: bytecode1 } = getContractArtifacts(erc20_b_path);
-    const erc20Factory1 = new ethers.ContractFactory(abi1, bytecode1, wallet);
-    const testERC201 = (await erc20Factory1.deploy(overrides)) as TestERC20B;
-    await testERC201.deployed();
-    const erc20TestAddress1 = testERC201.address;
-    console.log("ERC20 deployed at Address - ", erc20TestAddress1);
-
-    const { abi: abi2, bytecode: bytecode2 } = getContractArtifacts(erc20_c_path);
-    const erc20Factory2 = new ethers.ContractFactory(abi2, bytecode2, wallet);
-    const testERC202 = (await erc20Factory2.deploy(overrides)) as TestERC20C;
-    await testERC202.deployed();
-    const erc20TestAddress2 = testERC202.address;
-    console.log("ERC20 deployed at Address - ", erc20TestAddress2);
-    
-    const { abi: abi3, bytecode: bytecode3 } = getContractArtifacts(erc721_a_path);
-    const erc721Factory1 = new ethers.ContractFactory(abi3, bytecode3, wallet);
-    const testERC721 = (await erc721Factory1.deploy(overrides)) as TestERC721A;
-    await testERC721.deployed();
-    const erc721TestAddress = testERC721.address;
-    console.log("ERC721 deployed at Address - ", erc721TestAddress);
+  if (!fs.existsSync(artifacts)) {
+    console.log("Artifacts folder not found, please specify the correct path using the --artifacts-root flag")
+    exit(1)
   }
+
+  const { abi, bytecode } = getContractArtifacts(erc20_a_path);
+  const erc20Factory = new ethers.ContractFactory(abi, bytecode, wallet);
+  const testERC20 = (await erc20Factory.deploy(overrides)) as TestERC20A;
+  await testERC20.deployed();
+  const erc20TestAddress = testERC20.address;
+  console.log("ERC20 deployed at Address - ", erc20TestAddress);
+
+  const { abi: abi1, bytecode: bytecode1 } = getContractArtifacts(erc20_b_path);
+  const erc20Factory1 = new ethers.ContractFactory(abi1, bytecode1, wallet);
+  const testERC201 = (await erc20Factory1.deploy(overrides)) as TestERC20B;
+  await testERC201.deployed();
+  const erc20TestAddress1 = testERC201.address;
+  console.log("ERC20 deployed at Address - ", erc20TestAddress1);
+
+  const { abi: abi2, bytecode: bytecode2 } = getContractArtifacts(erc20_c_path);
+  const erc20Factory2 = new ethers.ContractFactory(abi2, bytecode2, wallet);
+  const testERC202 = (await erc20Factory2.deploy(overrides)) as TestERC20C;
+  await testERC202.deployed();
+  const erc20TestAddress2 = testERC202.address;
+  console.log("ERC20 deployed at Address - ", erc20TestAddress2);
+
+  const { abi: abi3, bytecode: bytecode3 } = getContractArtifacts(erc721_a_path);
+  const erc721Factory1 = new ethers.ContractFactory(abi3, bytecode3, wallet);
+  const testERC721 = (await erc721Factory1.deploy(overrides)) as TestERC721A;
+  await testERC721.deployed();
+  const erc721TestAddress = testERC721.address;
+  console.log("ERC721 deployed at Address - ", erc721TestAddress);
 }
 
 function getContractArtifacts(path: string): { bytecode: string; abi: string } {
diff --git a/tests/all-up-test-ci.sh b/tests/all-up-test-ci.sh
new file mode 100755
index 00000000..91a0b5ac
--- /dev/null
+++ b/tests/all-up-test-ci.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+# This script is used in Github actions CI to prep and run a testnet environment
+TEST_TYPE=$1
+set -eux
+NODES=4
+
+sudo apt-get update
+sudo apt-get install -y git make gcc g++ iproute2 iputils-ping procps vim tmux net-tools htop tar jq npm libssl-dev perl rustc cargo wget
+
+# Setup Althea L1 binary
+GOPROXY=https://proxy.golang.org make
+make install
+sudo cp ~/go/bin/althea /usr/bin/althea
+
+# Download the althea gaia fork as a IBC test chain
+sudo wget https://github.com/althea-net/ibc-test-chain/releases/download/v9.1.2/gaiad-v9.1.2-linux-amd64 -O /usr/bin/gaiad
+
+# Setup Hermes for IBC connections between chains
+pushd /tmp/
+wget https://github.com/informalsystems/ibc-rs/releases/download/v1.7.0/hermes-v1.7.0-x86_64-unknown-linux-gnu.tar.gz
+tar -xvf hermes-v1.7.0-x86_64-unknown-linux-gnu.tar.gz
+sudo mv hermes /usr/bin/
+popd
+
+# make log dirs
+sudo mkdir /ibc-relayer-logs
+sudo touch /ibc-relayer-logs/hermes-logs
+sudo touch /ibc-relayer-logs/channel-creation
+
+# Compile the solidity contracts
+pushd solidity/
+HUSKY_SKIP_INSTALL=1 npm install
+npm run typechain
+ls -lah
+ls -lah artifacts/
+ls -lah artifacts/contracts/
+pwd
+popd
+
+git clone https://github.com/AltheaFoundation/althea-dex.git solidity-dex/
+pushd solidity-dex/
+HUSKY_SKIP_INSTALL=1 npm install
+npx hardhat compile
+ls -lah
+ls -lah misc/scripts/
+pwd
+popd
+
+sudo bash tests/container-scripts/setup-validators.sh $NODES
+sudo bash tests/container-scripts/setup-ibc-validators.sh $NODES
+sudo bash tests/container-scripts/run-testnet.sh $NODES $TEST_TYPE
+
+# deploy the ethereum contracts
+pushd integration_tests/test_runner
+DEPLOY_CONTRACTS=1 RUST_BACKTRACE=full TEST_TYPE=$TEST_TYPE NO_GAS_OPT=1 RUST_LOG="INFO" PATH=$PATH:$HOME/.cargo/bin cargo run --release --bin test-runner
+popd
+
+echo "Running ibc relayer in the background, directing output to /ibc-relayer-logs"
+
+pushd integration_tests
+RUST_BACKTRACE=full TEST_TYPE=$TEST_TYPE RUST_LOG=INFO PATH=$PATH:$HOME/.cargo/bin cargo run --release --bin test-runner
diff --git a/tests/container-scripts/run-testnet.sh b/tests/container-scripts/run-testnet.sh
index 149346c5..7f48bcc6 100755
--- a/tests/container-scripts/run-testnet.sh
+++ b/tests/container-scripts/run-testnet.sh
@@ -7,7 +7,7 @@ NODES=$1
 set +u
 TEST_TYPE=$2
 
-GITHUB_ACTIONS_PATH="/home/runner/work/AltheaFoundation/althea-L1/"
+GITHUB_ACTIONS_PATH="/home/runner/work/althea-L1/althea-L1/"
 DOCKER_PATH="/althea/"
 
 if [[ -d "$GITHUB_ACTIONS_PATH" ]]; then

From 46ec8164c88de6079d56a23b98f4e348a718ef7a Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Fri, 15 Nov 2024 15:26:56 -0500
Subject: [PATCH 09/63] Advanced dex usage

---
 integration_tests/Cargo.lock                  |   1 +
 integration_tests/Cargo.toml                  |   3 +-
 integration_tests/test_runner/Cargo.toml      |   1 +
 integration_tests/test_runner/src/bin/main.rs |   4 +-
 .../test_runner/src/dex_utils.rs              | 587 +++++++++++++++++-
 .../test_runner/src/tests/dex.rs              | 264 ++++++--
 6 files changed, 801 insertions(+), 59 deletions(-)

diff --git a/integration_tests/Cargo.lock b/integration_tests/Cargo.lock
index 132f5e17..72a4e750 100644
--- a/integration_tests/Cargo.lock
+++ b/integration_tests/Cargo.lock
@@ -2110,6 +2110,7 @@ dependencies = [
  "lazy_static",
  "log",
  "num",
+ "num-traits",
  "num256",
  "prost",
  "prost-types",
diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml
index e493b648..4251149e 100644
--- a/integration_tests/Cargo.toml
+++ b/integration_tests/Cargo.toml
@@ -12,4 +12,5 @@ actix = "0.13"
 actix-rt = "2.2"
 log = "0.4"
 env_logger = "0.9"
-num = "0.4.0"
\ No newline at end of file
+num = "0.4.0"
+num-traits = "0.2"
\ No newline at end of file
diff --git a/integration_tests/test_runner/Cargo.toml b/integration_tests/test_runner/Cargo.toml
index dc789c48..6b243df3 100644
--- a/integration_tests/test_runner/Cargo.toml
+++ b/integration_tests/test_runner/Cargo.toml
@@ -32,6 +32,7 @@ actix-rt = {workspace = true}
 lazy_static = "1"
 url = "2"
 num = {workspace = true}
+num-traits = {workspace = true}
 log = {workspace = true}
 env_logger = {workspace = true}
 tokio = "1.4.0"
diff --git a/integration_tests/test_runner/src/bin/main.rs b/integration_tests/test_runner/src/bin/main.rs
index ace25b52..e4ba8ccd 100644
--- a/integration_tests/test_runner/src/bin/main.rs
+++ b/integration_tests/test_runner/src/bin/main.rs
@@ -16,9 +16,9 @@ use test_runner::bootstrapping::parse_ibc_validator_keys;
 use test_runner::bootstrapping::send_erc20s_to_evm_users;
 use test_runner::bootstrapping::start_ibc_relayer;
 use test_runner::bootstrapping::{deploy_erc20_contracts, get_keys};
+use test_runner::tests::dex::basic_dex_test;
 use test_runner::tests::dex::dex_ops_proposal_test;
 use test_runner::tests::dex::dex_safe_mode_test;
-use test_runner::tests::dex::dex_test;
 use test_runner::tests::dex::dex_upgrade_test;
 use test_runner::tests::erc20_conversion::erc20_conversion_test;
 use test_runner::tests::ica_host::ica_host_happy_path;
@@ -227,7 +227,7 @@ pub async fn main() {
             return;
         } else if test_type == "DEX" {
             info!("Start dex test");
-            dex_test(
+            basic_dex_test(
                 &contact,
                 &web30,
                 keys,
diff --git a/integration_tests/test_runner/src/dex_utils.rs b/integration_tests/test_runner/src/dex_utils.rs
index 899a6f88..d56b60a8 100644
--- a/integration_tests/test_runner/src/dex_utils.rs
+++ b/integration_tests/test_runner/src/dex_utils.rs
@@ -1,16 +1,19 @@
-use std::time::Duration;
+use std::{convert::TryInto, time::Duration};
 
-use clarity::{abi::AbiToken, Address as EthAddress, PrivateKey, Uint256};
+use clarity::{
+    abi::{encode_tokens, AbiToken},
+    Address as EthAddress, PrivateKey, Uint256,
+};
 use num256::Int256;
 
-use num::ToPrimitive;
+use num::{ToPrimitive, Zero};
 use web30::{
     client::Web3,
     jsonrpc::error::Web3Error,
     types::{TransactionRequest, TransactionResponse},
 };
 
-use crate::utils::OPERATION_TIMEOUT;
+use crate::utils::{EthermintUserKey, OPERATION_TIMEOUT};
 
 // Callpath Indices
 pub const BOOT_PATH: u16 = 0;
@@ -312,6 +315,9 @@ pub struct CrocQueryRangePosition {
     pub atomic: bool,
 }
 
+/// This query returns the stored ranged position information, which is not an accurate reflection of
+/// the tokens that the position represents. Calling croc_query_ranged_tokens may be more useful since
+/// it will return the token amounts which would be paid out on burning BUT NOT THE ACCUMULATED FEE REWARDS
 #[allow(clippy::too_many_arguments)]
 pub async fn croc_query_range_position(
     web30: &Web3,
@@ -373,11 +379,145 @@ pub async fn croc_query_range_position(
     })
 }
 
+#[derive(Debug, Clone)]
+pub struct CrocQueryTokens {
+    pub liq: Uint256,
+    pub base_qty: Uint256,
+    pub quote_qty: Uint256,
+}
+
+/// This query returns the liquidity and tokens that the ranged position represents.
+/// If you instead need the stored position information, use croc_query_range_position
+#[allow(clippy::too_many_arguments)]
+pub async fn croc_query_range_tokens(
+    web30: &Web3,
+    croc_query_contract: EthAddress,
+    caller: Option<EthAddress>,
+    owner: EthAddress,
+    base: EthAddress,   // The base token, must be lexically smaller than quote
+    quote: EthAddress,  // The quote token, must be lexically larger than base
+    pool_idx: Uint256,  // The index of the pool's template
+    lower_tick: Int256, // The lower tick boundary of the position
+    upper_tick: Int256, // The lower tick boundary of the position
+) -> Result<CrocQueryTokens, Web3Error> {
+    if base.gt(&quote) {
+        return Err(Web3Error::ContractCallError(
+            "croc_query_range_tokens: base must be lexically smaller than quote".to_string(),
+        ));
+    }
+
+    // ABI: queryRangeTokens(address owner, address base, address quote, uint256 poolIdx, int24 lowerTick, int24 upperTick)
+    // returns (uint128 liq, uint128 baseQty, uint128 quoteQty)
+    let caller = caller.unwrap_or(owner);
+    let payload = clarity::abi::encode_call(
+        "queryRangeTokens(address,address,address,uint256,int24,int24)",
+        &[
+            owner.into(),
+            base.into(),
+            quote.into(),
+            pool_idx.into(),
+            lower_tick.into(),
+            upper_tick.into(),
+        ],
+    )?;
+
+    let query_res = web30
+        .simulate_transaction(
+            TransactionRequest::quick_tx(caller, croc_query_contract, payload),
+            None,
+        )
+        .await?;
+
+    let mut i: usize = 0;
+    let liq = Uint256::from_be_bytes(&query_res[i..i + 32]);
+    i += 32;
+    let base_qty = Uint256::from_be_bytes(&query_res[i..i + 32]);
+    i += 32;
+    let quote_qty = Uint256::from_be_bytes(&query_res[i..i + 32]);
+
+    Ok(CrocQueryTokens {
+        liq,
+        base_qty,
+        quote_qty,
+    })
+}
+
+pub struct CrocQueryKnockoutTokens {
+    pub liq: Uint256,
+    pub base_qty: Uint256,
+    pub quote_qty: Uint256,
+    pub knocked_out: bool,
+}
+
+pub async fn croc_query_knockout_tokens(
+    web30: &Web3,
+    croc_query_contract: EthAddress,
+    caller: Option<EthAddress>,
+    owner: EthAddress,
+    base: EthAddress,   // The base token, must be lexically smaller than quote
+    quote: EthAddress,  // The quote token, must be lexically larger than base
+    pool_idx: Uint256,  // The index of the pool's template
+    pivot: u32, // The block timestamp associated with the minting of the knockout position (cast to a u32, which will allegedly work until the year 2106)
+    lower_tick: Int256, // The lower tick boundary of the position
+    upper_tick: Int256, // The lower tick boundary of the position
+    is_bid: bool,
+) -> Result<CrocQueryKnockoutTokens, Web3Error> {
+    if base.gt(&quote) {
+        return Err(Web3Error::ContractCallError(
+            "croc_query_knockout_tokens: base must be lexically smaller than quote".to_string(),
+        ));
+    }
+
+    // ABI: queryKnockoutTokens (address owner, address base, address quote, uint256 poolIdx, uint32 pivot, bool isBid, int24 lowerTick, int24 upperTick)
+    // returns (uint128 liq, uint128 baseQty, uint128 quoteQty, bool knockedOut)
+    let caller = caller.unwrap_or(owner);
+    let payload = clarity::abi::encode_call(
+        "queryKnockoutTokens(address,address,address,uint256,uint32,bool,int24,int24)",
+        &[
+            owner.into(),
+            base.into(),
+            quote.into(),
+            pool_idx.into(),
+            pivot.into(),
+            is_bid.into(),
+            lower_tick.into(),
+            upper_tick.into(),
+        ],
+    )?;
+
+    let query_res = web30
+        .simulate_transaction(
+            TransactionRequest::quick_tx(caller, croc_query_contract, payload),
+            None,
+        )
+        .await?;
+
+    let mut i: usize = 0;
+    let liq = Uint256::from_be_bytes(&query_res[i..i + 32]);
+    i += 32;
+    let base_qty = Uint256::from_be_bytes(&query_res[i..i + 32]);
+    i += 32;
+    let quote_qty = Uint256::from_be_bytes(&query_res[i..i + 32]);
+    i += 32;
+    let knocked_out = query_res[i + 32] > 0u8;
+
+    Ok(CrocQueryKnockoutTokens {
+        liq,
+        base_qty,
+        quote_qty,
+        knocked_out,
+    })
+}
+
 #[derive(Debug, Clone)]
 pub struct CrocQueryAmbientPosition {
     pub seeds: Uint256,
     pub timestamp: u32,
 }
+
+/// This query returns the stored ambient position information, which is not an accurate reflection of
+/// the tokens that the position represents. Calling croc_query_ambient_tokens may be more useful since
+/// it will return the "inflated" liquidity as the base and quote tokens which would be paid out on burning.
 #[allow(clippy::too_many_arguments)]
 pub async fn croc_query_ambient_position(
     web30: &Web3,
@@ -419,6 +559,53 @@ pub async fn croc_query_ambient_position(
     Ok(CrocQueryAmbientPosition { seeds, timestamp })
 }
 
+/// This query returns the liquidity and tokens that the ambient position represents.
+/// If you instead need the stored position information, use croc_query_ambient_position
+#[allow(clippy::too_many_arguments)]
+pub async fn croc_query_ambient_tokens(
+    web30: &Web3,
+    croc_query_contract: EthAddress,
+    caller: Option<EthAddress>,
+    owner: EthAddress,
+    base: EthAddress,  // The base token, must be lexically smaller than quote
+    quote: EthAddress, // The quote token, must be lexically larger than base
+    pool_idx: Uint256, // The index of the pool's template
+) -> Result<CrocQueryTokens, Web3Error> {
+    if base.gt(&quote) {
+        return Err(Web3Error::ContractCallError(
+            "croc_query_ambient_tokens: base must be lexically smaller than quote".to_string(),
+        ));
+    }
+
+    // ABI: queryAmbientTokens(address owner, address base, address quote, uint256 poolIdx)
+    // returns (uint128 liq, uint128 baseQty, uint128 quoteQty)
+    let caller = caller.unwrap_or(owner);
+    let payload = clarity::abi::encode_call(
+        "queryAmbientTokens(address,address,address,uint256)",
+        &[owner.into(), base.into(), quote.into(), pool_idx.into()],
+    )?;
+
+    let query_res = web30
+        .simulate_transaction(
+            TransactionRequest::quick_tx(caller, croc_query_contract, payload),
+            None,
+        )
+        .await?;
+
+    let mut i: usize = 0;
+    let liq = Uint256::from_be_bytes(&query_res[i..i + 32]);
+    i += 32;
+    let base_qty = Uint256::from_be_bytes(&query_res[i..i + 32]);
+    i += 32;
+    let quote_qty = Uint256::from_be_bytes(&query_res[i..i + 32]);
+
+    Ok(CrocQueryTokens {
+        liq,
+        base_qty,
+        quote_qty,
+    })
+}
+
 #[derive(Debug, Clone)]
 pub struct CrocQueryConcRewards {
     pub liq_rewards: Uint256,
@@ -750,3 +937,395 @@ pub async fn croc_policy_treasury_resolution(
         .await?;
     web30.wait_for_transaction(txhash, timeout, None).await
 }
+
+#[allow(clippy::too_many_arguments)]
+pub async fn mint_ranged_pos(
+    web3: &Web3,
+    dex: EthAddress,
+    query: EthAddress,
+    evm_privkey: PrivateKey,
+    evm_user: &EthermintUserKey,
+    base: EthAddress,
+    quote: EthAddress,
+    pool_idx: Uint256,
+    bid_tick: Int256,
+    ask_tick: Int256,
+    liq: Uint256, // The liquidity to mint (must be a multiple of 1024)
+) {
+    let start_pos = croc_query_range_position(
+        web3,
+        query,
+        None,
+        evm_user.eth_address,
+        base,
+        quote,
+        pool_idx,
+        bid_tick,
+        ask_tick,
+    )
+    .await
+    .expect("Could not query position");
+
+    let mint_ranged_pos_args = UserCmdArgs {
+        callpath: WARM_PATH, // Warm Path index
+        cmd: vec![
+            Uint256::from(1u8).into(),    // Mint Ranged Liq code
+            base.into(),                  // base
+            quote.into(),                 // quote
+            pool_idx.into(),              // poolIdx
+            bid_tick.into(),              // bid (lower) tick
+            ask_tick.into(),              // ask (upper) tick
+            liq.into(), // liq (in liquidity units, which must be a multiple of 1024)
+            (*MIN_PRICE).into(), // limitLower
+            (*MAX_PRICE).into(), // limitHigher
+            Uint256::from(0u8).into(), // reserveFlags
+            EthAddress::default().into(), // lpConduit
+        ],
+    };
+    info!("Minting position in both tokens: {mint_ranged_pos_args:?}");
+    dex_user_cmd(web3, dex, evm_privkey, mint_ranged_pos_args, None, None)
+        .await
+        .expect("Failed to mint position in pool");
+    let range_pos = croc_query_range_position(
+        web3,
+        query,
+        None,
+        evm_user.eth_address,
+        base,
+        quote,
+        pool_idx,
+        bid_tick,
+        ask_tick,
+    )
+    .await
+    .expect("Could not query position");
+    assert_eq!(range_pos.liq - start_pos.liq, liq);
+}
+
+#[allow(clippy::too_many_arguments)]
+pub async fn burn_ranged_pos(
+    web3: &Web3,
+    dex: EthAddress,
+    query: EthAddress,
+    evm_privkey: PrivateKey,
+    evm_user: &EthermintUserKey,
+    base: EthAddress,
+    quote: EthAddress,
+    pool_idx: Uint256,
+    bid_tick: Int256,
+    ask_tick: Int256,
+    liq: Uint256, // The liquidity to burn (must be a multiple of 1024)
+) {
+    let pos_start = croc_query_range_position(
+        web3,
+        query,
+        None,
+        evm_user.eth_address,
+        base,
+        quote,
+        pool_idx,
+        bid_tick,
+        ask_tick,
+    )
+    .await
+    .expect("Could not query position");
+
+    let burn_ranged_pos_args = UserCmdArgs {
+        callpath: WARM_PATH, // Warm Path index
+        cmd: vec![
+            Uint256::from(2u8).into(),    // Burn Ranged Liq code
+            base.into(),                  // base
+            quote.into(),                 // quote
+            (pool_idx).into(),            // poolIdx
+            bid_tick.into(),              // bid (lower) tick
+            ask_tick.into(),              // ask (upper) tick
+            liq.into(), // liq (in liquidity units, which must be a multiple of 1024)
+            (*MIN_PRICE).into(), // limitLower
+            (*MAX_PRICE).into(), // limitHigher
+            Uint256::from(0u8).into(), // reserveFlags
+            EthAddress::default().into(), // lpConduit
+        ],
+    };
+    info!("Burning position: {burn_ranged_pos_args:?}");
+    dex_user_cmd(web3, dex, evm_privkey, burn_ranged_pos_args, None, None)
+        .await
+        .expect("Failed to burn position in pool");
+    let range_pos = croc_query_range_position(
+        web3,
+        query,
+        None,
+        evm_user.eth_address,
+        base,
+        quote,
+        pool_idx,
+        bid_tick,
+        ask_tick,
+    )
+    .await
+    .expect("Could not query position");
+
+    assert_eq!(pos_start.liq - range_pos.liq, liq);
+}
+
+#[allow(clippy::too_many_arguments)]
+pub async fn mint_knockout_pos(
+    web3: &Web3,
+    dex: EthAddress,
+    query: EthAddress,
+    evm_privkey: PrivateKey,
+    evm_user: &EthermintUserKey,
+    base: EthAddress,
+    quote: EthAddress,
+    pool_idx: Uint256,
+    bid_tick: Int256,
+    ask_tick: Int256,
+    is_bid: bool,
+    reserve_flags: u8, // Controls what happens with "surplus" values held by the dex
+    qty: Uint256,
+    inside_mid: bool,
+) {
+    let arg_bytes = encode_tokens(&[qty.into(), inside_mid.into()]);
+    let mint_ko_pos_args = UserCmdArgs {
+        callpath: KNOCKOUT_LIQ_PATH, // KnockoutLiqPath index
+        cmd: vec![
+            Uint256::from(91u8).into(), // Mint Knockout code
+            base.into(),                // base
+            quote.into(),               // quote
+            (pool_idx).into(),          // poolIdx
+            bid_tick.into(),            // bid (lower) tick
+            ask_tick.into(),            // ask (upper) tick
+            is_bid.into(),
+            reserve_flags.into(),
+            arg_bytes.into(),
+        ],
+    };
+    info!("Minting knockout position: {mint_ko_pos_args:?}");
+    let res = dex_user_cmd(web3, dex, evm_privkey, mint_ko_pos_args, None, None)
+        .await
+        .expect("Failed to mint knockout position in pool");
+
+    // Querying the knockout position requires the "pivotTime", which is the timestamp of the block that the position was minted in
+    let pivot = web3
+        .eth_get_block_by_number(res.get_block_number().unwrap())
+        .await
+        .unwrap()
+        .timestamp;
+    let pivot: u32 = u32::from_be_bytes(pivot.to_be_bytes()[28..32].try_into().unwrap());
+
+    let ko_pos = croc_query_knockout_tokens(
+        web3,
+        query,
+        None,
+        evm_user.eth_address,
+        base,
+        quote,
+        pool_idx,
+        pivot,
+        bid_tick,
+        ask_tick,
+        is_bid,
+    )
+    .await
+    .expect("Could not query position");
+    assert!(ko_pos.base_qty > 0u8.into() || ko_pos.quote_qty > 0u8.into());
+}
+
+#[allow(clippy::too_many_arguments)]
+pub async fn burn_knockout_pos(
+    web3: &Web3,
+    dex: EthAddress,
+    query: EthAddress,
+    evm_privkey: PrivateKey,
+    evm_user: &EthermintUserKey,
+    base: EthAddress,
+    quote: EthAddress,
+    pool_idx: Uint256,
+    bid_tick: Int256,
+    ask_tick: Int256,
+    is_bid: bool,
+    reserve_flags: u8, // Controls what happens with "surplus" values held by the dex
+    qty: Uint256,
+    in_liq_qty: bool,
+    inside_mid: bool,
+    pivot: Option<u32>,
+) {
+    let start_pos = if let Some(pivot) = pivot {
+        Some(
+            croc_query_knockout_tokens(
+                web3,
+                query,
+                None,
+                evm_user.eth_address,
+                base,
+                quote,
+                pool_idx,
+                pivot,
+                bid_tick,
+                ask_tick,
+                is_bid,
+            )
+            .await
+            .expect("Could not query position"),
+        )
+    } else {
+        None
+    };
+
+    let arg_bytes = encode_tokens(&[qty.into(), in_liq_qty.into(), inside_mid.into()]);
+    let burn_ko_pos_args = UserCmdArgs {
+        callpath: KNOCKOUT_LIQ_PATH, // KnockoutLiqPath index
+        cmd: vec![
+            Uint256::from(92u8).into(), // Burn Knockout code
+            base.into(),                // base
+            quote.into(),               // quote
+            (pool_idx).into(),          // poolIdx
+            bid_tick.into(),            // bid (lower) tick
+            ask_tick.into(),            // ask (upper) tick
+            is_bid.into(),
+            reserve_flags.into(),
+            arg_bytes.into(),
+        ],
+    };
+    info!("Burning position: {burn_ko_pos_args:?}");
+    dex_user_cmd(web3, dex, evm_privkey, burn_ko_pos_args, None, None)
+        .await
+        .expect("Failed to burn position in pool");
+
+    // Unfortunately there is no way to compare the `qty` we put into the position with the `liq` result we can query
+    // but we can determine if the position's liquidity has changed
+    if let Some(start_pos) = start_pos {
+        let pivot = pivot.unwrap();
+        let ko_pos = croc_query_knockout_tokens(
+            web3,
+            query,
+            None,
+            evm_user.eth_address,
+            base,
+            quote,
+            pool_idx,
+            pivot,
+            bid_tick,
+            ask_tick,
+            is_bid,
+        )
+        .await
+        .expect("Could not query position");
+        assert!(ko_pos.liq < start_pos.liq);
+    }
+}
+
+#[allow(clippy::too_many_arguments)]
+pub async fn mint_ambient_pos(
+    web3: &Web3,
+    dex: EthAddress,
+    query: EthAddress,
+    evm_privkey: PrivateKey,
+    evm_user: &EthermintUserKey,
+    base: EthAddress,
+    quote: EthAddress,
+    pool_idx: Uint256,
+    liq: Uint256, // The liquidity to mint (must be a multiple of 1024)
+) {
+    let start_pos = croc_query_ambient_tokens(
+        web3,
+        query,
+        None,
+        evm_user.eth_address,
+        base,
+        quote,
+        pool_idx,
+    )
+    .await
+    .expect("Could not query position");
+
+    let mint_ambient_pos_args = UserCmdArgs {
+        callpath: WARM_PATH, // Warm Path index
+        cmd: vec![
+            Uint256::from(3u8).into(),    // Mint Ambient Liq code
+            base.into(),                  // base
+            quote.into(),                 // quote
+            (pool_idx).into(),            // poolIdx
+            Uint256::zero().into(),       // bid (lower) tick
+            Uint256::zero().into(),       // ask (upper) tick
+            liq.into(), // liq (in liquidity units, which must be a multiple of 1024)
+            (*MIN_PRICE).into(), // limitLower
+            (*MAX_PRICE).into(), // limitHigher
+            Uint256::zero().into(), // reserveFlags
+            EthAddress::default().into(), // lpConduit
+        ],
+    };
+    info!("Minting ambient position: {mint_ambient_pos_args:?}");
+    dex_user_cmd(web3, dex, evm_privkey, mint_ambient_pos_args, None, None)
+        .await
+        .expect("Failed to mint position in pool");
+    let amb_pos = croc_query_ambient_tokens(
+        web3,
+        query,
+        None,
+        evm_user.eth_address,
+        base,
+        quote,
+        pool_idx,
+    )
+    .await
+    .expect("Could not query position");
+    assert_eq!(amb_pos.liq - start_pos.liq, liq);
+}
+
+#[allow(clippy::too_many_arguments)]
+pub async fn burn_ambient_pos(
+    web3: &Web3,
+    dex: EthAddress,
+    query: EthAddress,
+    evm_privkey: PrivateKey,
+    evm_user: &EthermintUserKey,
+    base: EthAddress,
+    quote: EthAddress,
+    pool_idx: Uint256,
+    liq: Uint256, // The liquidity to mint (must be a multiple of 1024)
+) {
+    let start_pos = croc_query_ambient_tokens(
+        web3,
+        query,
+        None,
+        evm_user.eth_address,
+        base,
+        quote,
+        pool_idx,
+    )
+    .await
+    .expect("Could not query position");
+
+    let burn_ambient_pos_args = UserCmdArgs {
+        callpath: WARM_PATH, // Warm Path index
+        cmd: vec![
+            Uint256::from(4u8).into(),    // Burn Ambient Liq code
+            base.into(),                  // base
+            quote.into(),                 // quote
+            (pool_idx).into(),            // poolIdx
+            Uint256::zero().into(),       // bid (lower) tick
+            Uint256::zero().into(),       // ask (upper) tick
+            liq.into(), // liq (in liquidity units, which must be a multiple of 1024)
+            (*MIN_PRICE).into(), // limitLower
+            (*MAX_PRICE).into(), // limitHigher
+            Uint256::zero().into(), // reserveFlags
+            EthAddress::default().into(), // lpConduit
+        ],
+    };
+    info!("Burning ambient position: {burn_ambient_pos_args:?}");
+    dex_user_cmd(web3, dex, evm_privkey, burn_ambient_pos_args, None, None)
+        .await
+        .expect("Failed to burn position in pool");
+    let amb_pos = croc_query_ambient_tokens(
+        web3,
+        query,
+        None,
+        evm_user.eth_address,
+        base,
+        quote,
+        pool_idx,
+    )
+    .await
+    .expect("Could not query position");
+    assert_eq!(start_pos.liq - amb_pos.liq, liq);
+}
diff --git a/integration_tests/test_runner/src/tests/dex.rs b/integration_tests/test_runner/src/tests/dex.rs
index 12bf84bf..b55ce520 100644
--- a/integration_tests/test_runner/src/tests/dex.rs
+++ b/integration_tests/test_runner/src/tests/dex.rs
@@ -4,11 +4,12 @@ use std::time::Duration;
 
 use crate::bootstrapping::DexAddresses;
 use crate::dex_utils::{
-    croc_policy_ops_resolution, croc_policy_treasury_resolution, croc_query_curve_tick,
-    croc_query_dex, croc_query_pool_params, croc_query_pool_template, croc_query_range_position,
-    dex_authority_transfer, dex_direct_protocol_cmd, dex_query_authority, dex_query_safe_mode,
-    dex_swap, dex_user_cmd, OpsResolutionArgs, ProtocolCmdArgs, SwapArgs, UserCmdArgs, BOOT_PATH,
-    COLD_PATH, MAX_PRICE, MIN_PRICE, WARM_PATH,
+    burn_ambient_pos, burn_knockout_pos, burn_ranged_pos, croc_policy_ops_resolution,
+    croc_policy_treasury_resolution, croc_query_curve_tick, croc_query_dex, croc_query_pool_params,
+    croc_query_pool_template, croc_query_range_position, dex_authority_transfer,
+    dex_direct_protocol_cmd, dex_query_authority, dex_query_safe_mode, dex_swap, dex_user_cmd,
+    mint_ambient_pos, mint_knockout_pos, mint_ranged_pos, OpsResolutionArgs, ProtocolCmdArgs,
+    SwapArgs, UserCmdArgs, BOOT_PATH, COLD_PATH, MAX_PRICE, MIN_PRICE, WARM_PATH,
 };
 use crate::type_urls::{
     COLLECT_TREASURY_PROPOSAL_TYPE_URL, HOT_PATH_OPEN_PROPOSAL_TYPE_URL, OPS_PROPOSAL_TYPE_URL,
@@ -31,8 +32,7 @@ use althea_proto::cosmos_sdk_proto::cosmos::params::v1beta1::{
 };
 use clarity::{Address as EthAddress, PrivateKey, Uint256};
 use deep_space::{Coin, Contact};
-use num::ToPrimitive;
-
+use num_traits::ToPrimitive;
 use rand::Rng;
 use web30::client::Web3;
 use web30::jsonrpc::error::Web3Error;
@@ -42,7 +42,7 @@ lazy_static! {
     pub static ref POOL_IDX: Uint256 = 36000u32.into();
 }
 
-pub async fn dex_test(
+pub async fn basic_dex_test(
     contact: &Contact,
     web3: &Web3,
     validator_keys: Vec<ValidatorKeys>,
@@ -50,16 +50,21 @@ pub async fn dex_test(
     erc20_contracts: Vec<EthAddress>,
     dex_contracts: DexAddresses,
 ) {
-    let evm_user = evm_user_keys.first().unwrap();
-    let evm_privkey = evm_user.eth_privkey;
-    let optional_caller = Some(evm_user.eth_address);
-    let croc_query_contract = dex_contracts.query;
-    let dex_result = croc_query_dex(web3, croc_query_contract, optional_caller).await;
-    assert!(dex_result.is_ok(), "Bad result");
-    let dex = dex_result.unwrap();
-    assert_eq!(dex, dex_contracts.dex, "Dex contract address mismatch");
-
-    let (pool_base, pool_quote) = pool_tokens(erc20_contracts.clone());
+    let DexTestParams {
+        evm_user,
+        evm_privkey,
+        caller,
+        query,
+        dex,
+        pool_base,
+        pool_quote,
+    } = setup_params(
+        web3,
+        evm_user_keys,
+        dex_contracts.clone(),
+        erc20_contracts.clone(),
+    )
+    .await;
 
     basic_dex_setup(
         contact,
@@ -67,7 +72,7 @@ pub async fn dex_test(
         dex_contracts.dex,
         dex_contracts.query,
         dex_contracts.policy,
-        evm_user,
+        &evm_user,
         &validator_keys,
         pool_base,
         pool_quote,
@@ -106,55 +111,210 @@ pub async fn dex_test(
     if range_pos.liq > 0u8.into() {
         info!("Range position already exists: {:?}", range_pos);
     } else {
-        let mint_ranged_pos_args = UserCmdArgs {
-            callpath: WARM_PATH, // Warm Path index
-            cmd: vec![
-                Uint256::from(1u8).into(),            // Mint Ranged Liq in base token code
-                pool_base.into(),                     // base
-                pool_quote.into(),                    // quote
-                (*POOL_IDX).into(),                   // poolIdx
-                bid_tick.into(),                      // bid (lower) tick
-                ask_tick.into(),                      // ask (upper) tick
-                (one_eth() * 10240u32.into()).into(), // liq (in liquidity units, which must be a multiple of 1024)
-                (*MIN_PRICE).into(),                  // limitLower
-                (*MAX_PRICE).into(),                  // limitHigher
-                Uint256::from(0u8).into(),            // reserveFlags
-                EthAddress::default().into(),         // lpConduit
-            ],
-        };
-        info!("Minting position in both tokens: {mint_ranged_pos_args:?}");
-        dex_user_cmd(
+        let liq = one_eth() * 10240u32.into();
+        mint_ranged_pos(
             web3,
             dex_contracts.dex,
-            evm_privkey,
-            mint_ranged_pos_args,
-            None,
-            None,
-        )
-        .await
-        .expect("Failed to mint position in pool");
-        let range_pos = croc_query_range_position(
-            web3,
             dex_contracts.query,
-            None,
-            evm_user.eth_address,
+            evm_privkey,
+            &evm_user,
             pool_base,
             pool_quote,
             *POOL_IDX,
             bid_tick,
             ask_tick,
+            liq,
         )
-        .await
-        .expect("Could not query position");
-        assert!(range_pos.liq > 0u8.into());
+        .await;
     }
 
     // Finally, perform many smaller swaps to ensure the pool is working as expected
-    swap_many(web3, dex_contracts, pool_base, pool_quote, evm_user, 30).await;
+    swap_many(web3, dex_contracts, pool_base, pool_quote, &evm_user, 30).await;
+
+    info!("Successfully tested DEX");
+}
+
+// Generates some additional positions, minting and burning ambient and ranged and knockout positions
+pub async fn advanced_dex_test(
+    contact: &Contact,
+    web3: &Web3,
+    validator_keys: Vec<ValidatorKeys>,
+    evm_user_keys: Vec<EthermintUserKey>,
+    erc20_contracts: Vec<EthAddress>,
+    dex_contracts: DexAddresses,
+) {
+    let DexTestParams {
+        evm_user,
+        evm_privkey,
+        caller: _,
+        query: _,
+        dex: _,
+        pool_base,
+        pool_quote,
+    } = setup_params(
+        web3,
+        evm_user_keys,
+        dex_contracts.clone(),
+        erc20_contracts.clone(),
+    )
+    .await;
+
+    basic_dex_setup(
+        contact,
+        web3,
+        dex_contracts.dex,
+        dex_contracts.query,
+        dex_contracts.policy,
+        &evm_user,
+        &validator_keys,
+        pool_base,
+        pool_quote,
+    )
+    .await;
 
+    let tick = croc_query_curve_tick(
+        web3,
+        dex_contracts.query,
+        Some(evm_user.eth_address),
+        pool_base,
+        pool_quote,
+        *POOL_IDX,
+    )
+    .await
+    .expect("Could not get curve tick for pool");
+
+    let bid_tick = tick - 75u8.into();
+    let ask_tick = tick + 75u8.into();
+    let liq = one_eth() * 10240u32.into();
+    mint_ranged_pos(
+        web3,
+        dex_contracts.dex,
+        dex_contracts.query,
+        evm_privkey,
+        &evm_user,
+        pool_base,
+        pool_quote,
+        *POOL_IDX,
+        bid_tick,
+        ask_tick,
+        liq,
+    )
+    .await;
+    mint_knockout_pos(
+        web3,
+        dex_contracts.dex,
+        dex_contracts.query,
+        evm_privkey,
+        &evm_user,
+        pool_base,
+        pool_quote,
+        *POOL_IDX,
+        bid_tick,
+        ask_tick,
+        true,
+        0u8,
+        liq,
+        true,
+    )
+    .await;
+    mint_ambient_pos(
+        web3,
+        dex_contracts.dex,
+        dex_contracts.query,
+        evm_privkey,
+        &evm_user,
+        pool_base,
+        pool_quote,
+        *POOL_IDX,
+        liq,
+    )
+    .await;
+    burn_ranged_pos(
+        web3,
+        dex_contracts.dex,
+        dex_contracts.query,
+        evm_privkey,
+        &evm_user,
+        pool_base,
+        pool_quote,
+        *POOL_IDX,
+        bid_tick,
+        ask_tick,
+        liq,
+    )
+    .await;
+    burn_knockout_pos(
+        web3,
+        dex_contracts.dex,
+        dex_contracts.query,
+        evm_privkey,
+        &evm_user,
+        pool_base,
+        pool_quote,
+        *POOL_IDX,
+        bid_tick,
+        ask_tick,
+        true,
+        0u8,
+        liq,
+        true,
+        true,
+        None,
+    )
+    .await;
+    burn_ambient_pos(
+        web3,
+        dex_contracts.dex,
+        dex_contracts.query,
+        evm_privkey,
+        &evm_user,
+        pool_base,
+        pool_quote,
+        *POOL_IDX,
+        liq,
+    )
+    .await;
     info!("Successfully tested DEX");
 }
 
+pub struct DexTestParams {
+    pub evm_user: EthermintUserKey,
+    pub evm_privkey: PrivateKey,
+    pub caller: Option<EthAddress>,
+    pub query: EthAddress,
+    pub dex: EthAddress,
+    pub pool_base: EthAddress,
+    pub pool_quote: EthAddress,
+}
+
+pub async fn setup_params(
+    web3: &Web3,
+    evm_user_keys: Vec<EthermintUserKey>,
+    dex_contracts: DexAddresses,
+    erc20_contracts: Vec<EthAddress>,
+) -> DexTestParams {
+    let evm_user = *evm_user_keys.first().unwrap();
+    let evm_privkey = evm_user.eth_privkey;
+    let optional_caller = Some(evm_user.eth_address);
+    let croc_query_contract = dex_contracts.query;
+    let dex_result = croc_query_dex(web3, croc_query_contract, optional_caller).await;
+    assert!(dex_result.is_ok(), "Bad result");
+    let dex = dex_result.unwrap();
+    assert_eq!(dex, dex_contracts.dex, "Dex contract address mismatch");
+
+    let (pool_base, pool_quote) = pool_tokens(erc20_contracts);
+
+    DexTestParams {
+        evm_user,
+        evm_privkey,
+        caller: optional_caller,
+        query: croc_query_contract,
+        dex,
+        pool_base,
+        pool_quote,
+    }
+}
+
 pub async fn dex_upgrade_test(
     contact: &Contact,
     web3: &Web3,

From 65a728bdf8da1b61c2cf3300f486a69dd204c168 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Wed, 20 Nov 2024 14:27:19 -0500
Subject: [PATCH 10/63] Change testnet chain id to 6633438

---
 x/gasfree/module_test.go | 62 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 60 insertions(+), 2 deletions(-)

diff --git a/x/gasfree/module_test.go b/x/gasfree/module_test.go
index 517fdb5f..ade306e2 100644
--- a/x/gasfree/module_test.go
+++ b/x/gasfree/module_test.go
@@ -1,6 +1,7 @@
 package gasfree_test
 
 import (
+	"encoding/json"
 	"testing"
 	"time"
 
@@ -86,6 +87,37 @@ func (suite *GasfreeTestSuite) DoSetupTest(t require.TestingT) {
 		return genesis
 	})
 
+	coins := sdk.NewCoins(sdk.NewCoin(altheaconfig.BaseDenom, sdk.NewInt(100000000000000)))
+	genesisState := althea.ModuleBasics.DefaultGenesis(suite.app.AppCodec())
+	b32address := sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), priv.PubKey().Address().Bytes())
+	balances := []banktypes.Balance{
+		{
+			Address: b32address,
+			Coins:   coins,
+		},
+		{
+			Address: suite.app.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName).String(),
+			Coins:   coins,
+		},
+	}
+	// Update total supply
+	bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, sdk.NewCoins(sdk.NewCoin(altheaconfig.BaseDenom, sdk.NewInt(200000000000000))), []banktypes.Metadata{})
+	genesisState[banktypes.ModuleName] = suite.app.AppCodec().MustMarshalJSON(bankGenesis)
+
+	stateBytes, err := tmjson.MarshalIndent(genesisState, "", " ")
+	require.NoError(t, err)
+
+	// Initialize the chain
+	suite.app.InitChain(
+		// nolint: exhaustruct
+		abci.RequestInitChain{
+			ChainId:         "althea_6633438-1",
+			Validators:      []abci.ValidatorUpdate{},
+			ConsensusParams: DefaultConsensusParams,
+			AppStateBytes:   stateBytes,
+		},
+	)
+
 	// nolint: exhaustruct
 	suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{
 		Height:          1,
@@ -139,8 +171,34 @@ func (suite *GasfreeTestSuite) DoSetupTest(t require.TestingT) {
 }
 
 // Setup initializes a new Althea app. A Nop logger is set in AltheaApp.
-func Setup(isCheckTx bool, patchGenesis func(*althea.AltheaApp, simapp.GenesisState) simapp.GenesisState) *althea.AltheaApp {
-	return althea.NewSetup(isCheckTx, patchGenesis)
+func Setup(isCheckTx bool, patchGenesis func(*althea.AltheaApp, althea.GenesisState) althea.GenesisState) *althea.AltheaApp {
+	db := dbm.NewMemDB()
+	app := althea.NewAltheaApp(tmlog.NewNopLogger(), db, nil, true, map[int64]bool{}, althea.DefaultNodeHome, 5, althea.MakeEncodingConfig(), simapp.EmptyAppOptions{})
+	if !isCheckTx {
+		// init chain must be called to stop deliverState from being nil
+		genesisState := althea.NewDefaultGenesisState()
+		if patchGenesis != nil {
+			genesisState = patchGenesis(app, genesisState)
+		}
+
+		stateBytes, err := json.MarshalIndent(genesisState, "", " ")
+		if err != nil {
+			panic(err)
+		}
+
+		// Initialize the chain
+		app.InitChain(
+			// nolint: exhaustruct
+			abci.RequestInitChain{
+				ChainId:         "althea_6633438-1",
+				Validators:      []abci.ValidatorUpdate{},
+				ConsensusParams: DefaultConsensusParams,
+				AppStateBytes:   stateBytes,
+			},
+		)
+	}
+
+	return app
 }
 
 // DefaultConsensusParams defines the default Tendermint consensus params used in

From 3fa9bdf90acc0efd3acd816422750290c2a763b0 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Wed, 20 Nov 2024 14:35:15 -0500
Subject: [PATCH 11/63] Fix contract deployment

---
 integration_tests/test_runner/src/bootstrapping.rs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/integration_tests/test_runner/src/bootstrapping.rs b/integration_tests/test_runner/src/bootstrapping.rs
index 99d53377..c11ea579 100644
--- a/integration_tests/test_runner/src/bootstrapping.rs
+++ b/integration_tests/test_runner/src/bootstrapping.rs
@@ -192,7 +192,6 @@ pub async fn deploy_multicall() {
                 path[0],
                 &format!("--eth-node={}", ETH_NODE.as_str()),
                 &format!("--eth-privkey={:#x}", *MINER_PRIVATE_KEY),
-                &format!("--artifacts-root={}", path[1]),
             ])
             .current_dir(path[1])
             .output()

From 057d6886087ec9186b4e83fecd5cedd7dce195f7 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Wed, 20 Nov 2024 14:47:53 -0500
Subject: [PATCH 12/63] Update solidity-dex

---
 solidity-dex | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/solidity-dex b/solidity-dex
index 433af992..7b10334c 160000
--- a/solidity-dex
+++ b/solidity-dex
@@ -1 +1 @@
-Subproject commit 433af992fda3f055026d9c10e7cc47f6515a6eb5
+Subproject commit 7b10334c2de6855613b8f3cce23372e65e6ca5ee

From d24593c680fd6726332728518c2cd0139c237647 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Wed, 20 Nov 2024 14:58:26 -0500
Subject: [PATCH 13/63] Fix multicall deployment

---
 integration_tests/test_runner/src/bootstrapping.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/integration_tests/test_runner/src/bootstrapping.rs b/integration_tests/test_runner/src/bootstrapping.rs
index c11ea579..99d53377 100644
--- a/integration_tests/test_runner/src/bootstrapping.rs
+++ b/integration_tests/test_runner/src/bootstrapping.rs
@@ -192,6 +192,7 @@ pub async fn deploy_multicall() {
                 path[0],
                 &format!("--eth-node={}", ETH_NODE.as_str()),
                 &format!("--eth-privkey={:#x}", *MINER_PRIVATE_KEY),
+                &format!("--artifacts-root={}", path[1]),
             ])
             .current_dir(path[1])
             .output()

From bae9262ca4663d0a858a9e89047802168250e25a Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Wed, 20 Nov 2024 15:44:43 -0500
Subject: [PATCH 14/63] Update solidity-dex

---
 solidity-dex | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/solidity-dex b/solidity-dex
index 7b10334c..dd37563b 160000
--- a/solidity-dex
+++ b/solidity-dex
@@ -1 +1 @@
-Subproject commit 7b10334c2de6855613b8f3cce23372e65e6ca5ee
+Subproject commit dd37563b69ef401d04e9f116cb3d1930e001350a

From 5868a623bee42c48c38fe7f229e6691ed6d37afa Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Thu, 21 Nov 2024 09:38:45 -0500
Subject: [PATCH 15/63] Add CONTRACTS_ROOT, DEX_CONTRACTS_ROOT env var support

---
 .../test_runner/src/bootstrapping.rs          | 128 ++++++++++++------
 integration_tests/test_runner/src/utils.rs    |   8 ++
 2 files changed, 91 insertions(+), 45 deletions(-)

diff --git a/integration_tests/test_runner/src/bootstrapping.rs b/integration_tests/test_runner/src/bootstrapping.rs
index 99d53377..eb91b469 100644
--- a/integration_tests/test_runner/src/bootstrapping.rs
+++ b/integration_tests/test_runner/src/bootstrapping.rs
@@ -1,8 +1,8 @@
 use crate::ibc_utils::get_channel;
 use crate::utils::{
-    get_chain_id, get_deposit, get_ibc_chain_id, ALTHEA_RELAYER_ADDRESS, COSMOS_NODE_GRPC,
-    HERMES_CONFIG, IBC_RELAYER_ADDRESS, IBC_STAKING_TOKEN, OPERATION_TIMEOUT,
-    RELAYER_MNEMONIC_FILE,
+    get_chain_id, get_deposit, get_ibc_chain_id, parse_contracts_root, parse_dex_contracts_root,
+    ALTHEA_RELAYER_ADDRESS, COSMOS_NODE_GRPC, HERMES_CONFIG, IBC_RELAYER_ADDRESS,
+    IBC_STAKING_TOKEN, OPERATION_TIMEOUT, RELAYER_MNEMONIC_FILE,
 };
 use crate::utils::{
     send_erc20_bulk, EthermintUserKey, ValidatorKeys, ETH_NODE, MINER_PRIVATE_KEY, TOTAL_TIMEOUT,
@@ -96,10 +96,6 @@ pub async fn deploy_erc20_contracts(contact: &Contact) {
     // yet produced the next block after submitting each eth address
     contact.wait_for_next_block(TOTAL_TIMEOUT).await.unwrap();
 
-    // these are the possible paths where we could find the contract deployer
-    // and the gravity contract itself, feel free to expand this if it makes your
-    // deployments more straightforward.
-
     // the default unmoved locations for the Gravity repo
     const A: [&str; 2] = ["/althea/solidity/contract-deployer.ts", "/althea/solidity/"];
     // the default unmoved locations for Github Actions
@@ -107,18 +103,33 @@ pub async fn deploy_erc20_contracts(contact: &Contact) {
         "/home/runner/work/althea-L1/althea-L1/solidity/contract-deployer.ts",
         "/home/runner/work/althea-L1/althea-L1/solidity/",
     ];
-    let output = match return_existing(vec![A, B]) {
-        Some(path) => Command::new("npx")
-            .args([
-                "ts-node",
-                path[0],
-                &format!("--eth-node={}", ETH_NODE.as_str()),
-                &format!("--eth-privkey={:#x}", *MINER_PRIVATE_KEY),
-                &format!("--artifacts-root={}", path[1]),
-            ])
-            .current_dir(path[1])
-            .output()
-            .expect("Failed to deploy contracts!"),
+
+    // the user specified contracts root
+    let contracts_root = parse_contracts_root();
+    let paths = if let Ok(root) = contracts_root {
+        Some(vec![
+            format!("{}/contract-deployer.ts", root),
+            format!("{}/", root),
+        ])
+    } else {
+        return_existing(vec![A, B]).map(|path| vec![path[0].to_string(), path[1].to_string()])
+    };
+
+    let output = match paths {
+        Some(path) => {
+            info!("Deploying contracts from {:?}", path);
+            Command::new("npx")
+                .args([
+                    "ts-node",
+                    &path[0],
+                    &format!("--eth-node={}", ETH_NODE.as_str()),
+                    &format!("--eth-privkey={:#x}", *MINER_PRIVATE_KEY),
+                    &format!("--artifacts-root={}", path[1]),
+                ])
+                .current_dir(&path[1])
+                .output()
+                .expect("Failed to deploy contracts!")
+        }
         None => {
             panic!("Could not find json contract artifacts in any known location!")
         }
@@ -147,20 +158,34 @@ pub async fn deploy_dex() {
         "/home/runner/work/althea-L1/althea-L1/solidity-dex/misc/scripts/dex-deployer.ts",
         "/home/runner/work/althea-L1/althea-L1/solidity-dex/artifacts/contracts/",
     ];
-    let output = match return_existing(vec![A, B]) {
-        Some(path) => Command::new("npx")
-            .args([
-                "ts-node",
-                path[0],
-                &format!("--eth-node={}", ETH_NODE.as_str()),
-                &format!("--eth-privkey={:#x}", *MINER_PRIVATE_KEY),
-                &format!("--artifacts-root={}", path[1]),
-            ])
-            .current_dir(path[1])
-            .output()
-            .expect("Failed to deploy contracts!"),
+    // the user specified contracts root
+    let contracts_root = parse_dex_contracts_root();
+    let paths = if let Ok(root) = contracts_root {
+        Some(vec![
+            format!("{}/misc/scripts/dex-deployer.ts", root),
+            format!("{}/artifacts/contracts/", root),
+        ])
+    } else {
+        return_existing(vec![A, B]).map(|path| vec![path[0].to_string(), path[1].to_string()])
+    };
+
+    let output = match paths {
+        Some(path) => {
+            info!("Deploying contracts from {:?}", path);
+            Command::new("npx")
+                .args([
+                    "ts-node",
+                    &path[0],
+                    &format!("--eth-node={}", ETH_NODE.as_str()),
+                    &format!("--eth-privkey={:#x}", *MINER_PRIVATE_KEY),
+                    &format!("--artifacts-root={}", path[1]),
+                ])
+                .current_dir(&path[1])
+                .output()
+                .expect("Failed to deploy contracts!")
+        }
         None => {
-            panic!("Could not find dex artifacts in any known location!")
+            panic!("Could not find json contract artifacts in any known location!")
         }
     };
     info!("stdout: {}", String::from_utf8_lossy(&output.stdout));
@@ -184,19 +209,32 @@ pub async fn deploy_multicall() {
         "/home/runner/work/althea-L1/althea-L1/solidity-dex/misc/scripts/multicall-deployer.ts",
         "/home/runner/work/althea-L1/althea-L1/solidity-dex/artifacts/contracts/periphery/",
     ];
-    let paths = vec![A, B];
-    let output = match return_existing(paths) {
-        Some(path) => Command::new("npx")
-            .args([
-                "ts-node",
-                path[0],
-                &format!("--eth-node={}", ETH_NODE.as_str()),
-                &format!("--eth-privkey={:#x}", *MINER_PRIVATE_KEY),
-                &format!("--artifacts-root={}", path[1]),
-            ])
-            .current_dir(path[1])
-            .output()
-            .expect("Failed to deploy contracts!"),
+    // the user specified contracts root
+    let contracts_root = parse_dex_contracts_root();
+    let paths = if let Ok(root) = contracts_root {
+        Some(vec![
+            format!("{}/misc/scripts/multicall-deployer.ts", root),
+            format!("{}/artifacts/contracts/periphery/", root),
+        ])
+    } else {
+        return_existing(vec![A, B]).map(|path| vec![path[0].to_string(), path[1].to_string()])
+    };
+
+    let output = match paths {
+        Some(path) => {
+            info!("Deploying contracts from {:?}", path);
+            Command::new("npx")
+                .args([
+                    "ts-node",
+                    &path[0],
+                    &format!("--eth-node={}", ETH_NODE.as_str()),
+                    &format!("--eth-privkey={:#x}", *MINER_PRIVATE_KEY),
+                    &format!("--artifacts-root={}", path[1]),
+                ])
+                .current_dir(&path[1])
+                .output()
+                .expect("Failed to deploy contracts!")
+        }
         None => {
             panic!("Could not find json contract artifacts in any known location!")
         }
diff --git a/integration_tests/test_runner/src/utils.rs b/integration_tests/test_runner/src/utils.rs
index 8c4a51ff..69dabc14 100644
--- a/integration_tests/test_runner/src/utils.rs
+++ b/integration_tests/test_runner/src/utils.rs
@@ -104,6 +104,14 @@ pub fn should_deploy_contracts() -> bool {
     }
 }
 
+pub fn parse_contracts_root() -> Result<String, env::VarError> {
+    env::var("CONTRACTS_ROOT")
+}
+
+pub fn parse_dex_contracts_root() -> Result<String, env::VarError> {
+    env::var("DEX_CONTRACTS_ROOT")
+}
+
 /// Gets the standard non-token fee for the testnet. We deploy the test chain with STAKE
 /// and FOOTOKEN balances by default, one footoken is sufficient for any Cosmos tx fee except
 /// fees for send_to_eth messages which have to be of the same bridged denom so that the relayers

From f0e389e04c659b52da5c11ef5f754b548f4f9a4a Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Thu, 21 Nov 2024 14:05:04 -0500
Subject: [PATCH 16/63] Add WETH contract to solidity folder, fix lots of DEX
 test issues

---
 integration_tests/test_runner/src/bin/main.rs |  38 +-
 .../test_runner/src/bootstrapping.rs          |   6 +
 .../test_runner/src/dex_utils.rs              | 149 +++-
 .../test_runner/src/tests/dex.rs              | 294 +++++--
 solidity/contract-deployer.ts                 |  11 +
 solidity/contracts/WETH9.sol                  | 762 ++++++++++++++++++
 6 files changed, 1122 insertions(+), 138 deletions(-)
 create mode 100644 solidity/contracts/WETH9.sol

diff --git a/integration_tests/test_runner/src/bin/main.rs b/integration_tests/test_runner/src/bin/main.rs
index e4ba8ccd..1caa81b8 100644
--- a/integration_tests/test_runner/src/bin/main.rs
+++ b/integration_tests/test_runner/src/bin/main.rs
@@ -16,6 +16,7 @@ use test_runner::bootstrapping::parse_ibc_validator_keys;
 use test_runner::bootstrapping::send_erc20s_to_evm_users;
 use test_runner::bootstrapping::start_ibc_relayer;
 use test_runner::bootstrapping::{deploy_erc20_contracts, get_keys};
+use test_runner::tests::dex::advanced_dex_test;
 use test_runner::tests::dex::basic_dex_test;
 use test_runner::tests::dex::dex_ops_proposal_test;
 use test_runner::tests::dex::dex_safe_mode_test;
@@ -33,6 +34,7 @@ use test_runner::tests::onboarding::onboarding_disabled_whitelisted;
 use test_runner::tests::upgrade::upgrade_part_1;
 use test_runner::tests::upgrade::upgrade_part_2;
 use test_runner::utils::one_atom;
+use test_runner::utils::one_eth;
 use test_runner::utils::one_hundred_eth;
 use test_runner::utils::send_funds_bulk;
 use test_runner::utils::ETH_NODE;
@@ -83,7 +85,7 @@ pub async fn main() {
         &web30,
         erc20_addresses.clone(),
         EVM_USER_KEYS.clone(),
-        one_hundred_eth(),
+        one_eth() * 60_000u32.into(),
     )
     .await
     .unwrap();
@@ -97,23 +99,6 @@ pub async fn main() {
     let (ibc_keys, _ibc_phrases) = parse_ibc_validator_keys();
 
     info!("Funding EVM users with the native coin");
-    // Send the EVM users some althea token
-    send_funds_bulk(
-        &contact,
-        keys.first().expect("No validator keys?").validator_key,
-        &EVM_USER_KEYS
-            .clone()
-            .into_iter()
-            .map(|euk| euk.ethermint_address)
-            .collect::<Vec<_>>(),
-        Coin {
-            amount: one_atom(),
-            denom: STAKING_TOKEN.to_string(),
-        },
-        Some(OPERATION_TIMEOUT),
-    )
-    .await
-    .unwrap();
 
     info!("Checking footoken balances");
     // assert that the validators have a balance of the footoken we use
@@ -234,6 +219,20 @@ pub async fn main() {
                 EVM_USER_KEYS.clone(),
                 erc20_addresses,
                 dex_contracts,
+                contracts.weth_address,
+            )
+            .await;
+            return;
+        } else if test_type == "DEX_ADVANCED" {
+            info!("Start advanced dex test");
+            advanced_dex_test(
+                &contact,
+                &web30,
+                keys,
+                EVM_USER_KEYS.clone(),
+                erc20_addresses,
+                dex_contracts,
+                contracts.weth_address,
             )
             .await;
             return;
@@ -246,6 +245,7 @@ pub async fn main() {
                 EVM_USER_KEYS.clone(),
                 erc20_addresses,
                 dex_contracts,
+                contracts.weth_address,
             )
             .await;
             return;
@@ -258,6 +258,7 @@ pub async fn main() {
                 EVM_USER_KEYS.clone(),
                 erc20_addresses,
                 dex_contracts,
+                contracts.weth_address,
             )
             .await;
             return;
@@ -270,6 +271,7 @@ pub async fn main() {
                 EVM_USER_KEYS.clone(),
                 erc20_addresses,
                 dex_contracts,
+                contracts.weth_address,
             )
             .await;
             return;
diff --git a/integration_tests/test_runner/src/bootstrapping.rs b/integration_tests/test_runner/src/bootstrapping.rs
index eb91b469..aab762ad 100644
--- a/integration_tests/test_runner/src/bootstrapping.rs
+++ b/integration_tests/test_runner/src/bootstrapping.rs
@@ -294,6 +294,7 @@ fn return_existing(paths: Vec<[&str; 2]>) -> Option<[&str; 2]> {
 pub struct BootstrapContractAddresses {
     pub erc20_addresses: Vec<EthAddress>,
     pub erc721_addresses: Vec<EthAddress>,
+    pub weth_address: EthAddress,
     pub uniswap_liquidity_address: Option<EthAddress>,
 }
 
@@ -308,6 +309,7 @@ pub fn parse_contract_addresses() -> BootstrapContractAddresses {
     file.read_to_string(&mut output).unwrap();
     let mut erc20_addresses = Vec::new();
     let mut erc721_addresses = Vec::new();
+    let mut weth_address = EthAddress::default();
     let mut uniswap_liquidity = None;
     for line in output.lines() {
         if line.contains("ERC20 deployed at Address -") {
@@ -318,6 +320,9 @@ pub fn parse_contract_addresses() -> BootstrapContractAddresses {
             let address_string = line.split('-').last().unwrap();
             erc721_addresses.push(address_string.trim().parse().unwrap());
             info!("found erc721 address it is {}", address_string);
+        } else if line.contains("WETH deployed at Address -") {
+            let address_string = line.split('-').last().unwrap();
+            weth_address = address_string.trim().parse().unwrap();
         } else if line.contains("Uniswap Liquidity test deployed at Address - ") {
             let address_string = line.split('-').last().unwrap();
             uniswap_liquidity = Some(address_string.trim().parse().unwrap());
@@ -326,6 +331,7 @@ pub fn parse_contract_addresses() -> BootstrapContractAddresses {
     BootstrapContractAddresses {
         erc20_addresses,
         erc721_addresses,
+        weth_address,
         uniswap_liquidity_address: uniswap_liquidity,
     }
 }
diff --git a/integration_tests/test_runner/src/dex_utils.rs b/integration_tests/test_runner/src/dex_utils.rs
index d56b60a8..a75d34af 100644
--- a/integration_tests/test_runner/src/dex_utils.rs
+++ b/integration_tests/test_runner/src/dex_utils.rs
@@ -442,6 +442,61 @@ pub async fn croc_query_range_tokens(
     })
 }
 
+#[derive(Debug, Clone)]
+pub struct CrocQueryKnockoutPivot {
+    pub lots: Uint256, // Multiply by 1024 to get the amount of sqrt(X*Y) liquidity at the pivot
+    pub pivot: u32,    // The pivot time used in later referring to the knockout pivot
+    pub range: u16,    // The width of the knockout range liquidity in ticks at the pivot
+}
+
+#[allow(clippy::too_many_arguments)]
+pub async fn croc_query_knockout_pivot(
+    web30: &Web3,
+    croc_query_contract: EthAddress,
+    caller: EthAddress,
+    base: EthAddress,  // The base token, must be lexically smaller than quote
+    quote: EthAddress, // The quote token, must be lexically larger than base
+    pool_idx: Uint256, // The index of the pool's template
+    is_bid: bool, // If true then the liquidity knocks out when the price moves below tick, otherwise above
+    tick: Int256, // The tick of the knockout pivot
+) -> Result<CrocQueryKnockoutPivot, Web3Error> {
+    if base.gt(&quote) {
+        return Err(Web3Error::ContractCallError(
+            "croc_query_knockout_pivot: base must be lexically smaller than quote".to_string(),
+        ));
+    }
+
+    // ABI: queryKnockoutPivot(address base, address quote, uint256 poolIdx, bool isBid, int24 tick)
+    // returns (uint96 lots, uint32 pivot, uint16 range)
+    let payload = clarity::abi::encode_call(
+        "queryKnockoutPivot(address,address,uint256,bool,int24)",
+        &[
+            base.into(),
+            quote.into(),
+            pool_idx.into(),
+            is_bid.into(),
+            tick.into(),
+        ],
+    )?;
+
+    let query_res = web30
+        .simulate_transaction(
+            TransactionRequest::quick_tx(caller, croc_query_contract, payload),
+            None,
+        )
+        .await?;
+
+    let mut i: usize = 0;
+    let lots = Uint256::from_be_bytes(&query_res[i..i + 32]);
+    i += 32;
+    let pivot = u32::from_be_bytes(query_res[i + 28..i + 32].try_into().unwrap());
+    i += 32;
+    let range = u16::from_be_bytes(query_res[i + 30..i + 32].try_into().unwrap());
+
+    Ok(CrocQueryKnockoutPivot { lots, pivot, range })
+}
+
+#[derive(Debug, Clone)]
 pub struct CrocQueryKnockoutTokens {
     pub liq: Uint256,
     pub base_qty: Uint256,
@@ -449,6 +504,7 @@ pub struct CrocQueryKnockoutTokens {
     pub knocked_out: bool,
 }
 
+#[allow(clippy::too_many_arguments)]
 pub async fn croc_query_knockout_tokens(
     web30: &Web3,
     croc_query_contract: EthAddress,
@@ -498,8 +554,7 @@ pub async fn croc_query_knockout_tokens(
     let base_qty = Uint256::from_be_bytes(&query_res[i..i + 32]);
     i += 32;
     let quote_qty = Uint256::from_be_bytes(&query_res[i..i + 32]);
-    i += 32;
-    let knocked_out = query_res[i + 32] > 0u8;
+    let knocked_out = query_res.last().unwrap() > &0u8;
 
     Ok(CrocQueryKnockoutTokens {
         liq,
@@ -939,7 +994,7 @@ pub async fn croc_policy_treasury_resolution(
 }
 
 #[allow(clippy::too_many_arguments)]
-pub async fn mint_ranged_pos(
+pub async fn dex_mint_ranged_pos(
     web3: &Web3,
     dex: EthAddress,
     query: EthAddress,
@@ -952,6 +1007,7 @@ pub async fn mint_ranged_pos(
     ask_tick: Int256,
     liq: Uint256, // The liquidity to mint (must be a multiple of 1024)
 ) {
+    assert!(base.lt(&quote), "base must be lexically smaller than quote");
     let start_pos = croc_query_range_position(
         web3,
         query,
@@ -1003,7 +1059,7 @@ pub async fn mint_ranged_pos(
 }
 
 #[allow(clippy::too_many_arguments)]
-pub async fn burn_ranged_pos(
+pub async fn dex_burn_ranged_pos(
     web3: &Web3,
     dex: EthAddress,
     query: EthAddress,
@@ -1016,6 +1072,7 @@ pub async fn burn_ranged_pos(
     ask_tick: Int256,
     liq: Uint256, // The liquidity to burn (must be a multiple of 1024)
 ) {
+    assert!(base.lt(&quote), "base must be lexically smaller than quote");
     let pos_start = croc_query_range_position(
         web3,
         query,
@@ -1068,12 +1125,12 @@ pub async fn burn_ranged_pos(
 }
 
 #[allow(clippy::too_many_arguments)]
-pub async fn mint_knockout_pos(
+pub async fn dex_mint_knockout_pos(
     web3: &Web3,
     dex: EthAddress,
-    query: EthAddress,
+    _query: EthAddress,
     evm_privkey: PrivateKey,
-    evm_user: &EthermintUserKey,
+    _evm_user: &EthermintUserKey,
     base: EthAddress,
     quote: EthAddress,
     pool_idx: Uint256,
@@ -1084,6 +1141,7 @@ pub async fn mint_knockout_pos(
     qty: Uint256,
     inside_mid: bool,
 ) {
+    assert!(base.lt(&quote), "base must be lexically smaller than quote");
     let arg_bytes = encode_tokens(&[qty.into(), inside_mid.into()]);
     let mint_ko_pos_args = UserCmdArgs {
         callpath: KNOCKOUT_LIQ_PATH, // KnockoutLiqPath index
@@ -1091,7 +1149,7 @@ pub async fn mint_knockout_pos(
             Uint256::from(91u8).into(), // Mint Knockout code
             base.into(),                // base
             quote.into(),               // quote
-            (pool_idx).into(),          // poolIdx
+            pool_idx.into(),            // poolIdx
             bid_tick.into(),            // bid (lower) tick
             ask_tick.into(),            // ask (upper) tick
             is_bid.into(),
@@ -1100,38 +1158,52 @@ pub async fn mint_knockout_pos(
         ],
     };
     info!("Minting knockout position: {mint_ko_pos_args:?}");
-    let res = dex_user_cmd(web3, dex, evm_privkey, mint_ko_pos_args, None, None)
+    dex_user_cmd(web3, dex, evm_privkey, mint_ko_pos_args, None, None)
         .await
         .expect("Failed to mint knockout position in pool");
 
-    // Querying the knockout position requires the "pivotTime", which is the timestamp of the block that the position was minted in
-    let pivot = web3
-        .eth_get_block_by_number(res.get_block_number().unwrap())
-        .await
-        .unwrap()
-        .timestamp;
-    let pivot: u32 = u32::from_be_bytes(pivot.to_be_bytes()[28..32].try_into().unwrap());
-
-    let ko_pos = croc_query_knockout_tokens(
-        web3,
-        query,
-        None,
-        evm_user.eth_address,
-        base,
-        quote,
-        pool_idx,
-        pivot,
-        bid_tick,
-        ask_tick,
-        is_bid,
-    )
-    .await
-    .expect("Could not query position");
-    assert!(ko_pos.base_qty > 0u8.into() || ko_pos.quote_qty > 0u8.into());
+    // let pivot_tick = if is_bid { bid_tick } else { ask_tick };
+    // info!(
+    //     "Querying knockout position: {} {} {} {} {}",
+    //     base, quote, pool_idx, is_bid, pivot_tick
+    // );
+    // // Querying the knockout position requires the "pivotTime", which is the timestamp of the block that the position was minted in
+    // let pivot = croc_query_knockout_pivot(
+    //     web3,
+    //     dex,
+    //     evm_user.eth_address,
+    //     base,
+    //     quote,
+    //     pool_idx,
+    //     is_bid,
+    //     pivot_tick,
+    // )
+    // .await
+    // .expect("Could not query pivot");
+    // info!("Queried pivot: {pivot:?}");
+    // let pivot = pivot.pivot;
+
+    // let ko_pos = croc_query_knockout_tokens(
+    //     web3,
+    //     query,
+    //     None,
+    //     evm_user.eth_address,
+    //     base,
+    //     quote,
+    //     pool_idx,
+    //     pivot,
+    //     bid_tick,
+    //     ask_tick,
+    //     is_bid,
+    // )
+    // .await
+    // .expect("Could not query position");
+    // info!("Minted knockout position: {ko_pos:?}");
+    // assert!(ko_pos.base_qty > 0u8.into() || ko_pos.quote_qty > 0u8.into());
 }
 
 #[allow(clippy::too_many_arguments)]
-pub async fn burn_knockout_pos(
+pub async fn dex_burn_knockout_pos(
     web3: &Web3,
     dex: EthAddress,
     query: EthAddress,
@@ -1149,6 +1221,7 @@ pub async fn burn_knockout_pos(
     inside_mid: bool,
     pivot: Option<u32>,
 ) {
+    assert!(base.lt(&quote), "base must be lexically smaller than quote");
     let start_pos = if let Some(pivot) = pivot {
         Some(
             croc_query_knockout_tokens(
@@ -1215,7 +1288,7 @@ pub async fn burn_knockout_pos(
 }
 
 #[allow(clippy::too_many_arguments)]
-pub async fn mint_ambient_pos(
+pub async fn dex_mint_ambient_pos(
     web3: &Web3,
     dex: EthAddress,
     query: EthAddress,
@@ -1226,6 +1299,7 @@ pub async fn mint_ambient_pos(
     pool_idx: Uint256,
     liq: Uint256, // The liquidity to mint (must be a multiple of 1024)
 ) {
+    assert!(base.lt(&quote), "base must be lexically smaller than quote");
     let start_pos = croc_query_ambient_tokens(
         web3,
         query,
@@ -1269,11 +1343,11 @@ pub async fn mint_ambient_pos(
     )
     .await
     .expect("Could not query position");
-    assert_eq!(amb_pos.liq - start_pos.liq, liq);
+    assert!(liq - (amb_pos.liq - start_pos.liq) < 1000u32.into());
 }
 
 #[allow(clippy::too_many_arguments)]
-pub async fn burn_ambient_pos(
+pub async fn dex_burn_ambient_pos(
     web3: &Web3,
     dex: EthAddress,
     query: EthAddress,
@@ -1284,6 +1358,7 @@ pub async fn burn_ambient_pos(
     pool_idx: Uint256,
     liq: Uint256, // The liquidity to mint (must be a multiple of 1024)
 ) {
+    assert!(base.lt(&quote), "base must be lexically smaller than quote");
     let start_pos = croc_query_ambient_tokens(
         web3,
         query,
diff --git a/integration_tests/test_runner/src/tests/dex.rs b/integration_tests/test_runner/src/tests/dex.rs
index b55ce520..123db2e6 100644
--- a/integration_tests/test_runner/src/tests/dex.rs
+++ b/integration_tests/test_runner/src/tests/dex.rs
@@ -4,12 +4,12 @@ use std::time::Duration;
 
 use crate::bootstrapping::DexAddresses;
 use crate::dex_utils::{
-    burn_ambient_pos, burn_knockout_pos, burn_ranged_pos, croc_policy_ops_resolution,
-    croc_policy_treasury_resolution, croc_query_curve_tick, croc_query_dex, croc_query_pool_params,
-    croc_query_pool_template, croc_query_range_position, dex_authority_transfer,
-    dex_direct_protocol_cmd, dex_query_authority, dex_query_safe_mode, dex_swap, dex_user_cmd,
-    mint_ambient_pos, mint_knockout_pos, mint_ranged_pos, OpsResolutionArgs, ProtocolCmdArgs,
-    SwapArgs, UserCmdArgs, BOOT_PATH, COLD_PATH, MAX_PRICE, MIN_PRICE, WARM_PATH,
+    croc_policy_ops_resolution, croc_policy_treasury_resolution, croc_query_curve_tick,
+    croc_query_dex, croc_query_pool_params, croc_query_pool_template, croc_query_range_position,
+    dex_authority_transfer, dex_burn_ambient_pos, dex_burn_knockout_pos, dex_burn_ranged_pos,
+    dex_direct_protocol_cmd, dex_mint_ambient_pos, dex_mint_knockout_pos, dex_mint_ranged_pos,
+    dex_query_authority, dex_query_safe_mode, dex_swap, dex_user_cmd, OpsResolutionArgs,
+    ProtocolCmdArgs, SwapArgs, UserCmdArgs, BOOT_PATH, COLD_PATH, MAX_PRICE, MIN_PRICE, WARM_PATH,
 };
 use crate::type_urls::{
     COLLECT_TREASURY_PROPOSAL_TYPE_URL, HOT_PATH_OPEN_PROPOSAL_TYPE_URL, OPS_PROPOSAL_TYPE_URL,
@@ -34,6 +34,7 @@ use clarity::{Address as EthAddress, PrivateKey, Uint256};
 use deep_space::{Coin, Contact};
 use num_traits::ToPrimitive;
 use rand::Rng;
+use tokio::time::sleep;
 use web30::client::Web3;
 use web30::jsonrpc::error::Web3Error;
 use web30::types::TransactionResponse;
@@ -49,11 +50,11 @@ pub async fn basic_dex_test(
     evm_user_keys: Vec<EthermintUserKey>,
     erc20_contracts: Vec<EthAddress>,
     dex_contracts: DexAddresses,
+    walthea: EthAddress,
 ) {
     let DexTestParams {
         evm_user,
-        evm_privkey,
-        caller,
+        caller: _,
         query,
         dex,
         pool_base,
@@ -69,22 +70,72 @@ pub async fn basic_dex_test(
     basic_dex_setup(
         contact,
         web3,
-        dex_contracts.dex,
-        dex_contracts.query,
+        dex,
+        query,
         dex_contracts.policy,
         &evm_user,
         &validator_keys,
         pool_base,
         pool_quote,
+        walthea,
     )
     .await;
+    let (a, b) = if walthea < pool_base {
+        (walthea, pool_base)
+    } else {
+        (pool_base, walthea)
+    };
+    populate_pool_basic(web3, &dex_contracts, &evm_user, a, b).await;
+
+    populate_pool_basic(web3, &dex_contracts, &evm_user, pool_base, pool_quote).await;
 
+    info!("Successfully tested DEX");
+}
+
+pub async fn populate_pool_basic(
+    web3: &Web3,
+    dex_contracts: &DexAddresses,
+    evm_user: &EthermintUserKey,
+    base: EthAddress,
+    quote: EthAddress,
+) {
+    if base != EthAddress::default()
+        && !web3
+            .check_erc20_approved(base, evm_user.eth_address, dex_contracts.dex)
+            .await
+            .expect("Unable to check erc20 approval")
+    {
+        web3.approve_erc20_transfers(
+            base,
+            evm_user.eth_privkey,
+            dex_contracts.dex,
+            Some(OPERATION_TIMEOUT),
+            vec![],
+        )
+        .await
+        .expect("Unable to approve erc20");
+    }
+    if !web3
+        .check_erc20_approved(quote, evm_user.eth_address, dex_contracts.dex)
+        .await
+        .expect("Unable to check erc20 approval")
+    {
+        web3.approve_erc20_transfers(
+            quote,
+            evm_user.eth_privkey,
+            dex_contracts.dex,
+            Some(OPERATION_TIMEOUT),
+            vec![],
+        )
+        .await
+        .expect("Unable to approve erc20");
+    }
     let tick = croc_query_curve_tick(
         web3,
         dex_contracts.query,
         Some(evm_user.eth_address),
-        pool_base,
-        pool_quote,
+        base,
+        quote,
         *POOL_IDX,
     )
     .await
@@ -100,8 +151,8 @@ pub async fn basic_dex_test(
         dex_contracts.query,
         None,
         evm_user.eth_address,
-        pool_base,
-        pool_quote,
+        base,
+        quote,
         *POOL_IDX,
         bid_tick,
         ask_tick,
@@ -111,15 +162,32 @@ pub async fn basic_dex_test(
     if range_pos.liq > 0u8.into() {
         info!("Range position already exists: {:?}", range_pos);
     } else {
-        let liq = one_eth() * 10240u32.into();
-        mint_ranged_pos(
+        let liq: Uint256 = one_atom() * 1024000000u32.into();
+        let bb = web3
+            .get_erc20_balance(base, evm_user.eth_address)
+            .await
+            .unwrap();
+        let qb = web3
+            .get_erc20_balance(quote, evm_user.eth_address)
+            .await
+            .unwrap();
+        let ba = web3
+            .check_erc20_approved(base, evm_user.eth_address, dex_contracts.dex)
+            .await
+            .unwrap();
+        let qa = web3
+            .check_erc20_approved(quote, evm_user.eth_address, dex_contracts.dex)
+            .await
+            .unwrap();
+        info!("Before minting ranged position: base balance: {}, quote balance: {}, base approved: {}, quote approved: {}", bb, qb, ba, qa);
+        dex_mint_ranged_pos(
             web3,
             dex_contracts.dex,
             dex_contracts.query,
-            evm_privkey,
-            &evm_user,
-            pool_base,
-            pool_quote,
+            evm_user.eth_privkey,
+            evm_user,
+            base,
+            quote,
             *POOL_IDX,
             bid_tick,
             ask_tick,
@@ -129,9 +197,7 @@ pub async fn basic_dex_test(
     }
 
     // Finally, perform many smaller swaps to ensure the pool is working as expected
-    swap_many(web3, dex_contracts, pool_base, pool_quote, &evm_user, 30).await;
-
-    info!("Successfully tested DEX");
+    swap_many(web3, dex_contracts, base, quote, evm_user, 30).await;
 }
 
 // Generates some additional positions, minting and burning ambient and ranged and knockout positions
@@ -142,10 +208,10 @@ pub async fn advanced_dex_test(
     evm_user_keys: Vec<EthermintUserKey>,
     erc20_contracts: Vec<EthAddress>,
     dex_contracts: DexAddresses,
+    walthea: EthAddress,
 ) {
     let DexTestParams {
         evm_user,
-        evm_privkey,
         caller: _,
         query: _,
         dex: _,
@@ -169,6 +235,7 @@ pub async fn advanced_dex_test(
         &validator_keys,
         pool_base,
         pool_quote,
+        walthea,
     )
     .await;
 
@@ -183,95 +250,113 @@ pub async fn advanced_dex_test(
     .await
     .expect("Could not get curve tick for pool");
 
-    let bid_tick = tick - 75u8.into();
-    let ask_tick = tick + 75u8.into();
+    let ranged_bid_tick = tick - 32u8.into();
+    let ranged_ask_tick = tick + 32u8.into();
     let liq = one_eth() * 10240u32.into();
-    mint_ranged_pos(
+
+    dex_mint_ambient_pos(
         web3,
         dex_contracts.dex,
         dex_contracts.query,
-        evm_privkey,
+        evm_user.eth_privkey,
         &evm_user,
         pool_base,
         pool_quote,
         *POOL_IDX,
-        bid_tick,
-        ask_tick,
-        liq,
+        (1024u32 * 10000u32).into(),
     )
     .await;
-    mint_knockout_pos(
+    dex_mint_ranged_pos(
         web3,
         dex_contracts.dex,
         dex_contracts.query,
-        evm_privkey,
+        evm_user.eth_privkey,
         &evm_user,
         pool_base,
         pool_quote,
         *POOL_IDX,
-        bid_tick,
-        ask_tick,
-        true,
-        0u8,
-        liq,
-        true,
+        ranged_bid_tick,
+        ranged_ask_tick,
+        liq / 4u8.into(),
     )
     .await;
-    mint_ambient_pos(
+
+    let tick = croc_query_curve_tick(
+        web3,
+        dex_contracts.query,
+        Some(evm_user.eth_address),
+        pool_base,
+        pool_quote,
+        *POOL_IDX,
+    )
+    .await
+    .expect("Could not get curve tick for pool");
+    let ko_bid_tick = tick - 32u32.into();
+    let ko_ask_tick = tick + 32u32.into();
+
+    dex_mint_knockout_pos(
         web3,
         dex_contracts.dex,
         dex_contracts.query,
-        evm_privkey,
+        evm_user.eth_privkey,
         &evm_user,
         pool_base,
         pool_quote,
         *POOL_IDX,
-        liq,
+        ko_bid_tick,
+        ko_ask_tick,
+        true,
+        0u8,
+        (1024u32 * 10000u32).into(),
+        true,
     )
     .await;
-    burn_ranged_pos(
+
+    sleep(Duration::from_secs(15)).await;
+
+    dex_burn_ranged_pos(
         web3,
         dex_contracts.dex,
         dex_contracts.query,
-        evm_privkey,
+        evm_user.eth_privkey,
         &evm_user,
         pool_base,
         pool_quote,
         *POOL_IDX,
-        bid_tick,
-        ask_tick,
-        liq,
+        ranged_bid_tick,
+        ranged_ask_tick,
+        liq / 4u8.into(),
     )
     .await;
-    burn_knockout_pos(
+    dex_burn_knockout_pos(
         web3,
         dex_contracts.dex,
         dex_contracts.query,
-        evm_privkey,
+        evm_user.eth_privkey,
         &evm_user,
         pool_base,
         pool_quote,
         *POOL_IDX,
-        bid_tick,
-        ask_tick,
+        ko_bid_tick,
+        ko_ask_tick,
         true,
         0u8,
-        liq,
+        (1024u32 * 10000u32).into(),
         true,
         true,
         None,
     )
     .await;
-    burn_ambient_pos(
+    dex_burn_ambient_pos(
         web3,
         dex_contracts.dex,
         dex_contracts.query,
-        evm_privkey,
+        evm_user.eth_privkey,
         &evm_user,
         pool_base,
         pool_quote,
         *POOL_IDX,
-        liq,
+        (1024u32 * 10000u32).into(),
     )
     .await;
     info!("Successfully tested DEX");
@@ -279,7 +364,6 @@ pub async fn advanced_dex_test(
 
 pub struct DexTestParams {
     pub evm_user: EthermintUserKey,
-    pub evm_privkey: PrivateKey,
     pub caller: Option<EthAddress>,
     pub query: EthAddress,
     pub dex: EthAddress,
@@ -294,7 +378,6 @@ pub async fn setup_params(
     erc20_contracts: Vec<EthAddress>,
 ) -> DexTestParams {
     let evm_user = *evm_user_keys.first().unwrap();
-    let evm_privkey = evm_user.eth_privkey;
     let optional_caller = Some(evm_user.eth_address);
     let croc_query_contract = dex_contracts.query;
     let dex_result = croc_query_dex(web3, croc_query_contract, optional_caller).await;
@@ -306,7 +389,6 @@ pub async fn setup_params(
 
     DexTestParams {
         evm_user,
-        evm_privkey,
         caller: optional_caller,
         query: croc_query_contract,
         dex,
@@ -322,6 +404,7 @@ pub async fn dex_upgrade_test(
     evm_user_keys: Vec<EthermintUserKey>,
     erc20_contracts: Vec<EthAddress>,
     dex_contracts: DexAddresses,
+    walthea: EthAddress,
 ) {
     let evm_user = evm_user_keys.first().unwrap();
     let emergency_user = evm_user_keys.last().unwrap();
@@ -339,6 +422,7 @@ pub async fn dex_upgrade_test(
         &validator_keys,
         pool_base,
         pool_quote,
+        walthea,
     )
     .await;
 
@@ -477,6 +561,7 @@ pub async fn dex_safe_mode_test(
     evm_user_keys: Vec<EthermintUserKey>,
     erc20_contracts: Vec<EthAddress>,
     dex_contracts: DexAddresses,
+    walthea: EthAddress,
 ) {
     let emergency_user = evm_user_keys.last().unwrap();
     let evm_user = evm_user_keys.first().unwrap();
@@ -492,6 +577,7 @@ pub async fn dex_safe_mode_test(
         &validator_keys,
         pool_base,
         pool_quote,
+        walthea,
     )
     .await;
     let tick = croc_query_curve_tick(
@@ -592,6 +678,7 @@ pub async fn dex_ops_proposal_test(
     evm_user_keys: Vec<EthermintUserKey>,
     erc20_contracts: Vec<EthAddress>,
     dex_contracts: DexAddresses,
+    walthea: EthAddress,
 ) {
     let evm_user = evm_user_keys.first().unwrap();
     let (pool_base, pool_quote) = pool_tokens(erc20_contracts.clone());
@@ -606,6 +693,7 @@ pub async fn dex_ops_proposal_test(
         &validator_keys,
         pool_base,
         pool_quote,
+        walthea,
     )
     .await;
     let callpath = COLD_PATH;
@@ -674,7 +762,7 @@ pub fn pool_tokens(erc20_contracts: Vec<EthAddress>) -> (EthAddress, EthAddress)
 
 async fn swap_many(
     web3: &Web3,
-    dex_contracts: DexAddresses,
+    dex_contracts: &DexAddresses,
     pool_base: EthAddress,
     pool_quote: EthAddress,
     evm_user: &EthermintUserKey,
@@ -684,8 +772,8 @@ async fn swap_many(
     let mut rng = rand::thread_rng();
 
     for _ in 0..swaps {
-        let qty_multi: u32 = rng.gen();
-        let qty = one_atom() * qty_multi.into();
+        let qty_multi: u16 = rng.gen();
+        let qty = (1024u32 * u32::from(qty_multi)).into();
         let pre_swap_base = web3
             .get_erc20_balance(pool_base, evm_user.eth_address)
             .await
@@ -826,6 +914,7 @@ pub async fn basic_dex_setup(
     validator_keys: &[ValidatorKeys],
     pool_base: EthAddress,
     pool_quote: EthAddress,
+    walthea: EthAddress,
 ) {
     let current_auth = dex_query_authority(web3, dex, Some(evm_user.eth_address))
         .await
@@ -853,28 +942,46 @@ pub async fn basic_dex_setup(
         info!("Dex is in safe mode, disabling safe mode");
         submit_and_pass_safe_mode_proposal(contact, validator_keys, false, true).await;
     }
-    let pool = croc_query_pool_params(
-        web3,
-        query,
-        Some(evm_user.eth_address),
-        pool_base,
-        pool_quote,
-        *POOL_IDX,
-    )
-    .await;
-    if pool.is_ok() && pool.unwrap().tick_size != 0 {
-        info!("Pool already created, approving use of base and quote tokens");
-        web3.approve_erc20_transfers(
-            pool_base,
+
+    if web3
+        .get_erc20_balance(walthea, evm_user.eth_address)
+        .await
+        .unwrap()
+        < one_eth() * 60_000u32.into()
+    {
+        web3.wrap_eth(
+            one_eth() * 60_000u32.into(),
             evm_user.eth_privkey,
-            dex,
-            Some(OPERATION_TIMEOUT),
-            vec![],
+            Some(walthea),
+            Some(Duration::from_secs(15)),
         )
         .await
-        .expect("Could not approve base token");
+        .expect("Unable to wrap native token!");
+    }
+
+    // Create or prepare the pool with Base and wAlthea tokens
+    let (a, b) = if walthea < pool_base {
+        (walthea, pool_base)
+    } else {
+        (pool_base, walthea)
+    };
+    create_or_prepare_pool(web3, dex, query, evm_user, a, b, *POOL_IDX).await;
+    // Create or prepare the pool with Base and Quote tokens
+    create_or_prepare_pool(web3, dex, query, evm_user, pool_base, pool_quote, *POOL_IDX).await;
+}
+
+pub async fn create_or_prepare_pool(
+    web3: &Web3,
+    dex: EthAddress,
+    query: EthAddress,
+    evm_user: &EthermintUserKey,
+    base: EthAddress,
+    quote: EthAddress,
+    pool_idx: Uint256,
+) {
+    if base != EthAddress::default() {
         web3.approve_erc20_transfers(
-            pool_quote,
+            base,
             evm_user.eth_privkey,
             dex,
             Some(OPERATION_TIMEOUT),
@@ -882,15 +989,36 @@ pub async fn basic_dex_setup(
         )
         .await
         .expect("Could not approve base token");
+    }
+    web3.approve_erc20_transfers(
+        quote,
+        evm_user.eth_privkey,
+        dex,
+        Some(OPERATION_TIMEOUT),
+        vec![],
+    )
+    .await
+    .expect("Could not approve quote token");
+    let pool = croc_query_pool_params(
+        web3,
+        query,
+        Some(evm_user.eth_address),
+        base,
+        quote,
+        pool_idx,
+    )
+    .await;
+    if pool.is_ok() && pool.unwrap().tick_size != 0 {
+        info!("Pool already created");
     } else {
-        info!("Creating pool");
+        info!("Creating {base}/{quote} pool");
         init_pool(
             web3,
             evm_user.eth_privkey,
             dex,
-            pool_base,
-            pool_quote,
-            None,
+            base,
+            quote,
+            Some(pool_idx),
             None,
         )
         .await
diff --git a/solidity/contract-deployer.ts b/solidity/contract-deployer.ts
index ccb4621a..efbd446a 100644
--- a/solidity/contract-deployer.ts
+++ b/solidity/contract-deployer.ts
@@ -1,3 +1,4 @@
+import { WETH9 } from "./typechain/WETH9";
 import { TestERC20A } from "./typechain/TestERC20A";
 import { TestERC20B } from "./typechain/TestERC20B";
 import { TestERC20C } from "./typechain/TestERC20C";
@@ -74,6 +75,7 @@ async function deploy() {
   var erc20_b_path: string = artifacts + "/artifacts/contracts/TestERC20B.sol/TestERC20B.json"
   var erc20_c_path: string = artifacts + "/artifacts/contracts/TestERC20C.sol/TestERC20C.json"
   var erc721_a_path: string = artifacts + "/artifacts/contracts/TestERC721A.sol/TestERC721A.json"
+  var weth9_path: string = artifacts + "/artifacts/contracts/WETH9.sol/WETH9.json"
 
   if (!fs.existsSync(artifacts)) {
     console.log("Artifacts folder not found, please specify the correct path using the --artifacts-root flag")
@@ -107,6 +109,15 @@ async function deploy() {
   await testERC721.deployed();
   const erc721TestAddress = testERC721.address;
   console.log("ERC721 deployed at Address - ", erc721TestAddress);
+
+  const { abi: abi4, bytecode: bytecode4 } = getContractArtifacts(weth9_path);
+  const weth9Factory = new ethers.ContractFactory(abi4, bytecode4, wallet);
+  const weth9 = (await weth9Factory.deploy(overrides)) as WETH9;
+  await weth9.deployed();
+  const weth9Address = weth9.address;
+  console.log("WETH deployed at Address - ", weth9Address);
+
+
 }
 
 function getContractArtifacts(path: string): { bytecode: string; abi: string } {
diff --git a/solidity/contracts/WETH9.sol b/solidity/contracts/WETH9.sol
new file mode 100644
index 00000000..6cf6e169
--- /dev/null
+++ b/solidity/contracts/WETH9.sol
@@ -0,0 +1,762 @@
+// Copyright (C) 2015, 2016, 2017 Dapphub
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+pragma solidity >=0.4.22;
+
+/// This contract is a copy of the WETH9 contract found at https://github.com/gnosis/canonical-weth/blob/master/contracts/WETH9.sol
+/// with the only notable changes being a solidity compiler version relaxation and minimal syntactic changes to ensure
+/// the contract compiles and produces no warnings.
+contract WETH9 {
+    string public name     = "Wrapped Ether";
+    string public symbol   = "WETH";
+    uint8  public decimals = 18;
+
+    event  Approval(address indexed src, address indexed guy, uint wad);
+    event  Transfer(address indexed src, address indexed dst, uint wad);
+    event  Deposit(address indexed dst, uint wad);
+    event  Withdrawal(address indexed src, uint wad);
+
+    mapping (address => uint)                       public  balanceOf;
+    mapping (address => mapping (address => uint))  public  allowance;
+
+    fallback() external payable {
+        deposit();
+    }
+    receive() external payable {
+        deposit();
+    }
+    function deposit() public payable {
+        balanceOf[msg.sender] += msg.value;
+        emit Deposit(msg.sender, msg.value);
+    }
+    function withdraw(uint wad) public {
+        require(balanceOf[msg.sender] >= wad);
+        balanceOf[msg.sender] -= wad;
+        payable(msg.sender).transfer(wad);
+        emit Withdrawal(msg.sender, wad);
+    }
+
+    function totalSupply() public view returns (uint) {
+        return address(this).balance;
+    }
+
+    function approve(address guy, uint wad) public returns (bool) {
+        allowance[msg.sender][guy] = wad;
+        emit Approval(msg.sender, guy, wad);
+        return true;
+    }
+
+    function transfer(address dst, uint wad) public returns (bool) {
+        return transferFrom(msg.sender, dst, wad);
+    }
+
+    function transferFrom(address src, address dst, uint wad)
+        public
+        returns (bool)
+    {
+        require(balanceOf[src] >= wad);
+
+        if (src != msg.sender && allowance[src][msg.sender] != type(uint).max) {
+            require(allowance[src][msg.sender] >= wad);
+            allowance[src][msg.sender] -= wad;
+        }
+
+        balanceOf[src] -= wad;
+        balanceOf[dst] += wad;
+
+        emit Transfer(src, dst, wad);
+
+        return true;
+    }
+}
+
+
+/*
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
+*/
\ No newline at end of file

From 6ca9dc530668d031d4724d37385a5c5bc51fee31 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Mon, 9 Dec 2024 11:15:31 -0500
Subject: [PATCH 17/63] Update solidity-dex

---
 solidity-dex | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/solidity-dex b/solidity-dex
index dd37563b..4cd0b972 160000
--- a/solidity-dex
+++ b/solidity-dex
@@ -1 +1 @@
-Subproject commit dd37563b69ef401d04e9f116cb3d1930e001350a
+Subproject commit 4cd0b972df204e93c87b9dd52e0ee1c5a9466aef

From a2220e7d295c081712da5c10e4aec59e78d0c8c0 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Mon, 9 Dec 2024 11:36:43 -0500
Subject: [PATCH 18/63] Fix gasfree module tests

---
 x/gasfree/module_test.go | 169 +++++++++------------------------------
 1 file changed, 40 insertions(+), 129 deletions(-)

diff --git a/x/gasfree/module_test.go b/x/gasfree/module_test.go
index ade306e2..e72fbf46 100644
--- a/x/gasfree/module_test.go
+++ b/x/gasfree/module_test.go
@@ -1,7 +1,6 @@
 package gasfree_test
 
 import (
-	"encoding/json"
 	"testing"
 	"time"
 
@@ -14,6 +13,7 @@ import (
 
 	"github.com/ethereum/go-ethereum/common"
 	ethtypes "github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/params"
 	"github.com/stretchr/testify/require"
 	"github.com/stretchr/testify/suite"
 
@@ -25,24 +25,24 @@ import (
 
 	"github.com/evmos/ethermint/crypto/ethsecp256k1"
 	"github.com/evmos/ethermint/tests"
-	"github.com/evmos/ethermint/x/evm"
 	"github.com/evmos/ethermint/x/evm/statedb"
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
+	feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"
 
 	althea "github.com/AltheaFoundation/althea-L1/app"
-	altheaconfig "github.com/AltheaFoundation/althea-L1/config"
+	altheacfg "github.com/AltheaFoundation/althea-L1/config"
 )
 
 type GasfreeTestSuite struct {
 	suite.Suite
 
-	ctx     sdk.Context
-	handler sdk.Handler
-	app     *althea.AltheaApp
+	ctx sdk.Context
+	app *althea.AltheaApp
 
-	signer    keyring.Signer
-	ethSigner ethtypes.Signer
-	from      common.Address
+	signer           keyring.Signer
+	ethSigner        ethtypes.Signer
+	from             common.Address
+	mintFeeCollector bool
 }
 
 // / DoSetupTest setup test environment, it uses `require.TestingT` to support both `testing.T` and `testing.B`.
@@ -52,83 +52,48 @@ func (suite *GasfreeTestSuite) DoSetupTest(t require.TestingT) {
 	// account key
 	priv, err := ethsecp256k1.GenerateKey()
 	require.NoError(t, err)
-	address := common.BytesToAddress(priv.PubKey().Address().Bytes())
 	suite.signer = tests.NewSigner(priv)
-	suite.from = address
-	// consensus key
-	priv, err = ethsecp256k1.GenerateKey()
-	require.NoError(t, err)
-	consAddress := sdk.ConsAddress(priv.PubKey().Address())
-
-	suite.app = Setup(checkTx, func(app *althea.AltheaApp, genesis simapp.GenesisState) simapp.GenesisState {
-		evmGenesis := evmtypes.DefaultGenesisState()
-		evmGenesis.Params.EvmDenom = altheaconfig.BaseDenom
-		evmGenesis.Params.AllowUnprotectedTxs = false
-
-		genesis[evmtypes.ModuleName] = app.AppCodec().MustMarshalJSON(evmGenesis)
-
-		coins := sdk.NewCoins(sdk.NewCoin(altheaconfig.BaseDenom, sdk.NewInt(100000000000000)))
-		genesisState := althea.ModuleBasics.DefaultGenesis(app.AppCodec())
-		b32address := sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), priv.PubKey().Address().Bytes())
-		balances := []banktypes.Balance{
-			{
-				Address: b32address,
-				Coins:   coins,
-			},
-			{
-				Address: app.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName).String(),
-				Coins:   coins,
-			},
-		}
-		// Update total supply
-		bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, sdk.NewCoins(sdk.NewCoin(altheaconfig.BaseDenom, sdk.NewInt(200000000000000))), []banktypes.Metadata{})
-		genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis)
 
-		return genesis
-	})
-
-	coins := sdk.NewCoins(sdk.NewCoin(altheaconfig.BaseDenom, sdk.NewInt(100000000000000)))
-	genesisState := althea.ModuleBasics.DefaultGenesis(suite.app.AppCodec())
-	b32address := sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), priv.PubKey().Address().Bytes())
-	balances := []banktypes.Balance{
-		{
-			Address: b32address,
-			Coins:   coins,
-		},
-		{
-			Address: suite.app.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName).String(),
-			Coins:   coins,
-		},
-	}
-	// Update total supply
-	bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, sdk.NewCoins(sdk.NewCoin(altheaconfig.BaseDenom, sdk.NewInt(200000000000000))), []banktypes.Metadata{})
-	genesisState[banktypes.ModuleName] = suite.app.AppCodec().MustMarshalJSON(bankGenesis)
+	// init app
+	suite.app = althea.NewSetup(checkTx, func(aa *althea.AltheaApp, gs simapp.GenesisState) simapp.GenesisState {
+		// setup feemarketGenesis params
+		feemarketGenesis := feemarkettypes.DefaultGenesisState()
+		feemarketGenesis.Params.EnableHeight = 1
+		feemarketGenesis.Params.NoBaseFee = false
+		feemarketGenesis.Params.BaseFee = sdk.NewInt(1)
+
+		gs[feemarkettypes.ModuleName] = aa.AppCodec().MustMarshalJSON(feemarketGenesis)
+
+		if suite.mintFeeCollector {
+			// mint some coin to fee collector
+			coins := sdk.NewCoins(sdk.NewCoin(altheacfg.BaseDenom, sdk.NewInt(int64(params.TxGas)-1)))
+			balances := []banktypes.Balance{
+				{
+					Address: suite.app.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName).String(),
+					Coins:   coins,
+				},
+			}
+			// update total supply
+			var bankGenesis banktypes.GenesisState
+			aa.AppCodec().MustUnmarshalJSON(gs[banktypes.ModuleName], &bankGenesis)
+			bankGenesis.Balances = append(bankGenesis.Balances, balances...)
+			bankGenesis.Supply = bankGenesis.Supply.Add(coins...)
+			gs[banktypes.ModuleName] = suite.app.AppCodec().MustMarshalJSON(&bankGenesis)
 
-	stateBytes, err := tmjson.MarshalIndent(genesisState, "", " ")
-	require.NoError(t, err)
+		}
 
-	// Initialize the chain
-	suite.app.InitChain(
-		// nolint: exhaustruct
-		abci.RequestInitChain{
-			ChainId:         "althea_6633438-1",
-			Validators:      []abci.ValidatorUpdate{},
-			ConsensusParams: DefaultConsensusParams,
-			AppStateBytes:   stateBytes,
-		},
-	)
+		return gs
+	})
 
-	// nolint: exhaustruct
 	suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{
 		Height:          1,
-		ChainID:         "althea_6633438-1",
+		ChainID:         "althea_7357-1",
 		Time:            time.Now().UTC(),
-		ProposerAddress: consAddress.Bytes(),
-		// nolint: exhaustruct
+		ProposerAddress: althea.ValidatorPubKey.Address().Bytes(),
+
 		Version: tmversion.Consensus{
 			Block: version.BlockProtocol,
 		},
-		// nolint: exhaustruct
 		LastBlockId: tmproto.BlockID{
 			Hash: tmhash.Sum([]byte("block_id")),
 			PartSetHeader: tmproto.PartSetHeader{
@@ -145,60 +110,6 @@ func (suite *GasfreeTestSuite) DoSetupTest(t require.TestingT) {
 		LastResultsHash:    tmhash.Sum([]byte("last_result")),
 	})
 
-	// queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry())
-	// evmtypes.RegisterQueryServer(queryHelper, suite.app.EvmKeeper)
-
-	// acc := &ethermint.EthAccount{
-	// 	BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(address.Bytes()), nil, 0, 0),
-	// 	CodeHash:    common.BytesToHash(crypto.Keccak256(nil)).String(),
-	// }
-
-	// suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
-
-	// valAddr := sdk.ValAddress(address.Bytes())
-	// // nolint: exhaustruct
-	// validator, err := stakingtypes.NewValidator(valAddr, priv.PubKey(), stakingtypes.Description{})
-	// require.NoError(t, err)
-
-	// err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator)
-	// require.NoError(t, err)
-	// err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator)
-	// require.NoError(t, err)
-	// suite.app.StakingKeeper.SetValidator(suite.ctx, validator)
-
-	suite.ethSigner = ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
-	suite.handler = evm.NewHandler(suite.app.EvmKeeper)
-}
-
-// Setup initializes a new Althea app. A Nop logger is set in AltheaApp.
-func Setup(isCheckTx bool, patchGenesis func(*althea.AltheaApp, althea.GenesisState) althea.GenesisState) *althea.AltheaApp {
-	db := dbm.NewMemDB()
-	app := althea.NewAltheaApp(tmlog.NewNopLogger(), db, nil, true, map[int64]bool{}, althea.DefaultNodeHome, 5, althea.MakeEncodingConfig(), simapp.EmptyAppOptions{})
-	if !isCheckTx {
-		// init chain must be called to stop deliverState from being nil
-		genesisState := althea.NewDefaultGenesisState()
-		if patchGenesis != nil {
-			genesisState = patchGenesis(app, genesisState)
-		}
-
-		stateBytes, err := json.MarshalIndent(genesisState, "", " ")
-		if err != nil {
-			panic(err)
-		}
-
-		// Initialize the chain
-		app.InitChain(
-			// nolint: exhaustruct
-			abci.RequestInitChain{
-				ChainId:         "althea_6633438-1",
-				Validators:      []abci.ValidatorUpdate{},
-				ConsensusParams: DefaultConsensusParams,
-				AppStateBytes:   stateBytes,
-			},
-		)
-	}
-
-	return app
 }
 
 // DefaultConsensusParams defines the default Tendermint consensus params used in

From c2877af3d0487e5d8c15e6c7450ff9600ddc8f16 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Tue, 10 Dec 2024 15:43:05 -0500
Subject: [PATCH 19/63] Mint DEX positions in base and quote quantities

---
 .../test_runner/src/dex_utils.rs              | 42 +++++++++++++++++++
 .../test_runner/src/tests/dex.rs              | 31 ++++++++++----
 2 files changed, 65 insertions(+), 8 deletions(-)

diff --git a/integration_tests/test_runner/src/dex_utils.rs b/integration_tests/test_runner/src/dex_utils.rs
index a75d34af..d6d25b39 100644
--- a/integration_tests/test_runner/src/dex_utils.rs
+++ b/integration_tests/test_runner/src/dex_utils.rs
@@ -1058,6 +1058,48 @@ pub async fn dex_mint_ranged_pos(
     assert_eq!(range_pos.liq - start_pos.liq, liq);
 }
 
+#[allow(clippy::too_many_arguments)]
+pub async fn dex_mint_ranged_in_amount(
+    web3: &Web3,
+    dex: EthAddress,
+    evm_privkey: PrivateKey,
+    base: EthAddress,
+    quote: EthAddress,
+    pool_idx: Uint256,
+    bid_tick: Int256,
+    ask_tick: Int256,
+    qty: Uint256,  // The amount of a token to mint a position with
+    in_base: bool, // Whether to mint in the base token or the quote token
+) {
+    assert!(base.lt(&quote), "base must be lexically smaller than quote");
+
+    let code = if in_base {
+        Uint256::from(11u8) // Mint in base amount code
+    } else {
+        Uint256::from(12u8) // Mint in quote amount code
+    };
+    let mint_ranged_pos_args = UserCmdArgs {
+        callpath: WARM_PATH, // Warm Path index
+        cmd: vec![
+            code.into(),
+            base.into(),                  // base
+            quote.into(),                 // quote
+            pool_idx.into(),              // poolIdx
+            bid_tick.into(),              // bid (lower) tick
+            ask_tick.into(),              // ask (upper) tick
+            qty.into(), // liq (in liquidity units, which must be a multiple of 1024)
+            (*MIN_PRICE).into(), // limitLower
+            (*MAX_PRICE).into(), // limitHigher
+            Uint256::from(0u8).into(), // reserveFlags
+            EthAddress::default().into(), // lpConduit
+        ],
+    };
+    info!("Minting position in single token: {mint_ranged_pos_args:?}");
+    dex_user_cmd(web3, dex, evm_privkey, mint_ranged_pos_args, None, None)
+        .await
+        .expect("Failed to mint position in pool");
+}
+
 #[allow(clippy::too_many_arguments)]
 pub async fn dex_burn_ranged_pos(
     web3: &Web3,
diff --git a/integration_tests/test_runner/src/tests/dex.rs b/integration_tests/test_runner/src/tests/dex.rs
index 123db2e6..361c1389 100644
--- a/integration_tests/test_runner/src/tests/dex.rs
+++ b/integration_tests/test_runner/src/tests/dex.rs
@@ -7,9 +7,10 @@ use crate::dex_utils::{
     croc_policy_ops_resolution, croc_policy_treasury_resolution, croc_query_curve_tick,
     croc_query_dex, croc_query_pool_params, croc_query_pool_template, croc_query_range_position,
     dex_authority_transfer, dex_burn_ambient_pos, dex_burn_knockout_pos, dex_burn_ranged_pos,
-    dex_direct_protocol_cmd, dex_mint_ambient_pos, dex_mint_knockout_pos, dex_mint_ranged_pos,
-    dex_query_authority, dex_query_safe_mode, dex_swap, dex_user_cmd, OpsResolutionArgs,
-    ProtocolCmdArgs, SwapArgs, UserCmdArgs, BOOT_PATH, COLD_PATH, MAX_PRICE, MIN_PRICE, WARM_PATH,
+    dex_direct_protocol_cmd, dex_mint_ambient_pos, dex_mint_knockout_pos,
+    dex_mint_ranged_in_amount, dex_mint_ranged_pos, dex_query_authority, dex_query_safe_mode,
+    dex_swap, dex_user_cmd, OpsResolutionArgs, ProtocolCmdArgs, SwapArgs, UserCmdArgs, BOOT_PATH,
+    COLD_PATH, MAX_PRICE, MIN_PRICE, WARM_PATH,
 };
 use crate::type_urls::{
     COLLECT_TREASURY_PROPOSAL_TYPE_URL, HOT_PATH_OPEN_PROPOSAL_TYPE_URL, OPS_PROPOSAL_TYPE_URL,
@@ -162,7 +163,7 @@ pub async fn populate_pool_basic(
     if range_pos.liq > 0u8.into() {
         info!("Range position already exists: {:?}", range_pos);
     } else {
-        let liq: Uint256 = one_atom() * 1024000000u32.into();
+        let qty: Uint256 = one_eth() * 1024u32.into();
         let bb = web3
             .get_erc20_balance(base, evm_user.eth_address)
             .await
@@ -180,18 +181,32 @@ pub async fn populate_pool_basic(
             .await
             .unwrap();
         info!("Before minting ranged position: base balance: {}, quote balance: {}, base approved: {}, quote approved: {}", bb, qb, ba, qa);
-        dex_mint_ranged_pos(
+        // Mint using the base token
+        dex_mint_ranged_in_amount(
             web3,
             dex_contracts.dex,
-            dex_contracts.query,
             evm_user.eth_privkey,
-            evm_user,
             base,
             quote,
             *POOL_IDX,
             bid_tick,
             ask_tick,
-            liq,
+            qty,
+            true,
+        )
+        .await;
+        // Mint using the quote token
+        dex_mint_ranged_in_amount(
+            web3,
+            dex_contracts.dex,
+            evm_user.eth_privkey,
+            base,
+            quote,
+            *POOL_IDX,
+            bid_tick,
+            ask_tick,
+            qty,
+            false,
         )
         .await;
     }

From 6c326a5590de8c79ea4093e4c775c4c405913692 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Fri, 13 Dec 2024 17:50:49 -0500
Subject: [PATCH 20/63] Improve DEX_ADVANCED test, calculate liq -> flows, add
 DEX_SWAP_MANY test

---
 integration_tests/test_runner/src/bin/main.rs |  15 +-
 .../test_runner/src/bootstrapping.rs          |   1 +
 .../test_runner/src/dex_utils.rs              | 267 ++++++++++++++----
 .../test_runner/src/tests/dex.rs              | 219 +++++++-------
 4 files changed, 316 insertions(+), 186 deletions(-)

diff --git a/integration_tests/test_runner/src/bin/main.rs b/integration_tests/test_runner/src/bin/main.rs
index 1caa81b8..1be37c7f 100644
--- a/integration_tests/test_runner/src/bin/main.rs
+++ b/integration_tests/test_runner/src/bin/main.rs
@@ -4,7 +4,6 @@
 #[macro_use]
 extern crate log;
 
-use deep_space::Coin;
 use deep_space::Contact;
 use deep_space::PrivateKey;
 use std::env;
@@ -20,6 +19,7 @@ use test_runner::tests::dex::advanced_dex_test;
 use test_runner::tests::dex::basic_dex_test;
 use test_runner::tests::dex::dex_ops_proposal_test;
 use test_runner::tests::dex::dex_safe_mode_test;
+use test_runner::tests::dex::dex_swap_many;
 use test_runner::tests::dex::dex_upgrade_test;
 use test_runner::tests::erc20_conversion::erc20_conversion_test;
 use test_runner::tests::ica_host::ica_host_happy_path;
@@ -33,15 +33,11 @@ use test_runner::tests::onboarding::onboarding_disable_after;
 use test_runner::tests::onboarding::onboarding_disabled_whitelisted;
 use test_runner::tests::upgrade::upgrade_part_1;
 use test_runner::tests::upgrade::upgrade_part_2;
-use test_runner::utils::one_atom;
 use test_runner::utils::one_eth;
-use test_runner::utils::one_hundred_eth;
-use test_runner::utils::send_funds_bulk;
 use test_runner::utils::ETH_NODE;
 use test_runner::utils::EVM_USER_KEYS;
 use test_runner::utils::IBC_ADDRESS_PREFIX;
 use test_runner::utils::IBC_NODE_GRPC;
-use test_runner::utils::STAKING_TOKEN;
 use test_runner::utils::{
     get_test_token_name, should_deploy_contracts, wait_for_cosmos_online, ADDRESS_PREFIX,
     COSMOS_NODE_GRPC, OPERATION_TIMEOUT, TOTAL_TIMEOUT,
@@ -236,6 +232,15 @@ pub async fn main() {
             )
             .await;
             return;
+        } else if test_type == "DEX_SWAP_MANY" {
+            dex_swap_many(
+                &web30,
+                EVM_USER_KEYS.clone(),
+                erc20_addresses,
+                dex_contracts,
+            )
+            .await;
+            return;
         } else if test_type == "DEX_UPGRADE" {
             info!("Start dex upgrade test");
             dex_upgrade_test(
diff --git a/integration_tests/test_runner/src/bootstrapping.rs b/integration_tests/test_runner/src/bootstrapping.rs
index aab762ad..2f75350f 100644
--- a/integration_tests/test_runner/src/bootstrapping.rs
+++ b/integration_tests/test_runner/src/bootstrapping.rs
@@ -323,6 +323,7 @@ pub fn parse_contract_addresses() -> BootstrapContractAddresses {
         } else if line.contains("WETH deployed at Address -") {
             let address_string = line.split('-').last().unwrap();
             weth_address = address_string.trim().parse().unwrap();
+            info!("found weth address it is {}", address_string);
         } else if line.contains("Uniswap Liquidity test deployed at Address - ") {
             let address_string = line.split('-').last().unwrap();
             uniswap_liquidity = Some(address_string.trim().parse().unwrap());
diff --git a/integration_tests/test_runner/src/dex_utils.rs b/integration_tests/test_runner/src/dex_utils.rs
index d6d25b39..5848bb6e 100644
--- a/integration_tests/test_runner/src/dex_utils.rs
+++ b/integration_tests/test_runner/src/dex_utils.rs
@@ -1,4 +1,4 @@
-use std::{convert::TryInto, time::Duration};
+use std::{convert::TryInto, str::FromStr, time::Duration};
 
 use clarity::{
     abi::{encode_tokens, AbiToken},
@@ -13,7 +13,7 @@ use web30::{
     types::{TransactionRequest, TransactionResponse},
 };
 
-use crate::utils::{EthermintUserKey, OPERATION_TIMEOUT};
+use crate::utils::OPERATION_TIMEOUT;
 
 // Callpath Indices
 pub const BOOT_PATH: u16 = 0;
@@ -999,7 +999,7 @@ pub async fn dex_mint_ranged_pos(
     dex: EthAddress,
     query: EthAddress,
     evm_privkey: PrivateKey,
-    evm_user: &EthermintUserKey,
+    evm_address: EthAddress,
     base: EthAddress,
     quote: EthAddress,
     pool_idx: Uint256,
@@ -1012,7 +1012,7 @@ pub async fn dex_mint_ranged_pos(
         web3,
         query,
         None,
-        evm_user.eth_address,
+        evm_address,
         base,
         quote,
         pool_idx,
@@ -1046,7 +1046,7 @@ pub async fn dex_mint_ranged_pos(
         web3,
         query,
         None,
-        evm_user.eth_address,
+        evm_address,
         base,
         quote,
         pool_idx,
@@ -1106,7 +1106,7 @@ pub async fn dex_burn_ranged_pos(
     dex: EthAddress,
     query: EthAddress,
     evm_privkey: PrivateKey,
-    evm_user: &EthermintUserKey,
+    evm_address: EthAddress,
     base: EthAddress,
     quote: EthAddress,
     pool_idx: Uint256,
@@ -1119,7 +1119,7 @@ pub async fn dex_burn_ranged_pos(
         web3,
         query,
         None,
-        evm_user.eth_address,
+        evm_address,
         base,
         quote,
         pool_idx,
@@ -1153,7 +1153,7 @@ pub async fn dex_burn_ranged_pos(
         web3,
         query,
         None,
-        evm_user.eth_address,
+        evm_address,
         base,
         quote,
         pool_idx,
@@ -1172,7 +1172,7 @@ pub async fn dex_mint_knockout_pos(
     dex: EthAddress,
     _query: EthAddress,
     evm_privkey: PrivateKey,
-    _evm_user: &EthermintUserKey,
+    _evm_address: EthAddress,
     base: EthAddress,
     quote: EthAddress,
     pool_idx: Uint256,
@@ -1213,7 +1213,7 @@ pub async fn dex_mint_knockout_pos(
     // let pivot = croc_query_knockout_pivot(
     //     web3,
     //     dex,
-    //     evm_user.eth_address,
+    //     evm_address,
     //     base,
     //     quote,
     //     pool_idx,
@@ -1229,7 +1229,7 @@ pub async fn dex_mint_knockout_pos(
     //     web3,
     //     query,
     //     None,
-    //     evm_user.eth_address,
+    //     evm_address,
     //     base,
     //     quote,
     //     pool_idx,
@@ -1250,7 +1250,7 @@ pub async fn dex_burn_knockout_pos(
     dex: EthAddress,
     query: EthAddress,
     evm_privkey: PrivateKey,
-    evm_user: &EthermintUserKey,
+    evm_address: EthAddress,
     base: EthAddress,
     quote: EthAddress,
     pool_idx: Uint256,
@@ -1270,7 +1270,7 @@ pub async fn dex_burn_knockout_pos(
                 web3,
                 query,
                 None,
-                evm_user.eth_address,
+                evm_address,
                 base,
                 quote,
                 pool_idx,
@@ -1314,7 +1314,7 @@ pub async fn dex_burn_knockout_pos(
             web3,
             query,
             None,
-            evm_user.eth_address,
+            evm_address,
             base,
             quote,
             pool_idx,
@@ -1335,24 +1335,17 @@ pub async fn dex_mint_ambient_pos(
     dex: EthAddress,
     query: EthAddress,
     evm_privkey: PrivateKey,
-    evm_user: &EthermintUserKey,
+    evm_address: EthAddress,
     base: EthAddress,
     quote: EthAddress,
     pool_idx: Uint256,
     liq: Uint256, // The liquidity to mint (must be a multiple of 1024)
 ) {
     assert!(base.lt(&quote), "base must be lexically smaller than quote");
-    let start_pos = croc_query_ambient_tokens(
-        web3,
-        query,
-        None,
-        evm_user.eth_address,
-        base,
-        quote,
-        pool_idx,
-    )
-    .await
-    .expect("Could not query position");
+    let start_pos =
+        croc_query_ambient_tokens(web3, query, None, evm_address, base, quote, pool_idx)
+            .await
+            .expect("Could not query position");
 
     let mint_ambient_pos_args = UserCmdArgs {
         callpath: WARM_PATH, // Warm Path index
@@ -1374,44 +1367,81 @@ pub async fn dex_mint_ambient_pos(
     dex_user_cmd(web3, dex, evm_privkey, mint_ambient_pos_args, None, None)
         .await
         .expect("Failed to mint position in pool");
-    let amb_pos = croc_query_ambient_tokens(
-        web3,
-        query,
-        None,
-        evm_user.eth_address,
-        base,
-        quote,
-        pool_idx,
-    )
-    .await
-    .expect("Could not query position");
+    let amb_pos = croc_query_ambient_tokens(web3, query, None, evm_address, base, quote, pool_idx)
+        .await
+        .expect("Could not query position");
     assert!(liq - (amb_pos.liq - start_pos.liq) < 1000u32.into());
 }
 
+#[allow(clippy::too_many_arguments)]
+pub async fn dex_mint_ambient_in_amount(
+    web3: &Web3,
+    dex: EthAddress,
+    query: EthAddress,
+    evm_privkey: PrivateKey,
+    evm_address: EthAddress,
+    base: EthAddress,
+    quote: EthAddress,
+    pool_idx: Uint256,
+    qty: Uint256,  // The amount of a token to mint a position with
+    in_base: bool, // Whether to mint in the base token or the quote token
+) {
+    assert!(base.lt(&quote), "base must be lexically smaller than quote");
+
+    let code = if in_base {
+        Uint256::from(31u8) // Mint in base amount code
+    } else {
+        Uint256::from(32u8) // Mint in quote amount code
+    };
+
+    let start_pos =
+        croc_query_ambient_tokens(web3, query, None, evm_address, base, quote, pool_idx)
+            .await
+            .expect("Could not query position");
+
+    let mint_ambient_pos_args = UserCmdArgs {
+        callpath: WARM_PATH, // Warm Path index
+        cmd: vec![
+            code.into(),
+            base.into(),                  // base
+            quote.into(),                 // quote
+            (pool_idx).into(),            // poolIdx
+            Uint256::zero().into(),       // bid (lower) tick
+            Uint256::zero().into(),       // ask (upper) tick
+            qty.into(),                   // qty of tokens to supply
+            (*MIN_PRICE).into(),          // limitLower
+            (*MAX_PRICE).into(),          // limitHigher
+            Uint256::zero().into(),       // reserveFlags
+            EthAddress::default().into(), // lpConduit
+        ],
+    };
+    info!("Minting ambient position: {mint_ambient_pos_args:?}");
+    dex_user_cmd(web3, dex, evm_privkey, mint_ambient_pos_args, None, None)
+        .await
+        .expect("Failed to mint position in pool");
+    let amb_pos = croc_query_ambient_tokens(web3, query, None, evm_address, base, quote, pool_idx)
+        .await
+        .expect("Could not query position");
+    assert!(amb_pos.liq > start_pos.liq);
+}
+
 #[allow(clippy::too_many_arguments)]
 pub async fn dex_burn_ambient_pos(
     web3: &Web3,
     dex: EthAddress,
     query: EthAddress,
     evm_privkey: PrivateKey,
-    evm_user: &EthermintUserKey,
+    evm_address: EthAddress,
     base: EthAddress,
     quote: EthAddress,
     pool_idx: Uint256,
     liq: Uint256, // The liquidity to mint (must be a multiple of 1024)
 ) {
     assert!(base.lt(&quote), "base must be lexically smaller than quote");
-    let start_pos = croc_query_ambient_tokens(
-        web3,
-        query,
-        None,
-        evm_user.eth_address,
-        base,
-        quote,
-        pool_idx,
-    )
-    .await
-    .expect("Could not query position");
+    let start_pos =
+        croc_query_ambient_tokens(web3, query, None, evm_address, base, quote, pool_idx)
+            .await
+            .expect("Could not query position");
 
     let burn_ambient_pos_args = UserCmdArgs {
         callpath: WARM_PATH, // Warm Path index
@@ -1433,16 +1463,133 @@ pub async fn dex_burn_ambient_pos(
     dex_user_cmd(web3, dex, evm_privkey, burn_ambient_pos_args, None, None)
         .await
         .expect("Failed to burn position in pool");
-    let amb_pos = croc_query_ambient_tokens(
-        web3,
-        query,
-        None,
-        evm_user.eth_address,
-        base,
-        quote,
-        pool_idx,
-    )
-    .await
-    .expect("Could not query position");
+    let amb_pos = croc_query_ambient_tokens(web3, query, None, evm_address, base, quote, pool_idx)
+        .await
+        .expect("Could not query position");
     assert_eq!(start_pos.liq - amb_pos.liq, liq);
 }
+
+// Mimics the logic in the Ambient sizeAmbientLiq() function
+pub fn size_ambient_liq(collateral: u128, is_add: bool, price_root: u128, in_base: bool) -> u128 {
+    let buffered = buffer_collateral(collateral, is_add);
+    let liq = liquidity_supported(buffered, price_root, in_base);
+
+    if is_add {
+        liq
+    } else {
+        liq + 1
+    }
+}
+
+// Mimics the logic in the Ambient sizeConcentratedLiq() function
+pub fn size_concentrated_liq(
+    collateral: u128,
+    is_add: bool,
+    price_root: u128,
+    lower_tick: i32,
+    upper_tick: i32,
+    in_base: bool,
+) -> u128 {
+    let (mut bid_price, mut ask_price) =
+        determine_price_range(price_root, lower_tick, upper_tick, in_base).unwrap();
+    let buffered = buffer_collateral(collateral, is_add);
+    if !in_base {
+        bid_price = recip_q64(bid_price);
+        ask_price = recip_q64(ask_price);
+    }
+    let price_delta = if bid_price > ask_price {
+        bid_price - ask_price
+    } else {
+        ask_price - bid_price
+    };
+    let liq = liquidity_supported(buffered, price_delta, in_base);
+
+    if is_add {
+        shave_round_lots(liq)
+    } else {
+        shave_round_lots_up(liq)
+    }
+}
+
+pub fn buffer_collateral(collateral: u128, is_add: bool) -> u128 {
+    const BUFFER: u128 = 4;
+
+    if is_add {
+        if collateral < BUFFER {
+            0
+        } else {
+            collateral - BUFFER
+        }
+    } else {
+        #[allow(clippy::collapsible_if)]
+        if collateral > u128::MAX - BUFFER {
+            u128::MAX
+        } else {
+            collateral + BUFFER
+        }
+    }
+}
+
+pub fn liquidity_supported(buffered_collateral: u128, price_root: u128, in_base: bool) -> u128 {
+    let bc_f = buffered_collateral.to_f64().unwrap();
+    let pr_f = price_root.to_f64().unwrap();
+
+    if in_base {
+        (bc_f * 2f64.powf(64f64) / pr_f).to_u128()
+    } else {
+        (bc_f * pr_f / 2f64.powf(64f64)).to_u128()
+    }
+    .unwrap()
+}
+
+pub fn determine_price_range(
+    curve_price: u128,
+    lower_tick: i32,
+    upper_tick: i32,
+    in_base: bool,
+) -> Result<(u128, u128), String> {
+    let mut bid_price = tick_to_sqrt_ratio(lower_tick);
+    let mut ask_price = tick_to_sqrt_ratio(upper_tick);
+
+    if curve_price <= bid_price {
+        if in_base {
+            return Err("Price is below the bid price".to_string());
+        }
+    } else if curve_price >= ask_price {
+        if !in_base {
+            return Err("Price is above the ask price".to_string());
+        }
+    } else if in_base {
+        ask_price = curve_price;
+    } else {
+        bid_price = curve_price;
+    }
+
+    Ok((bid_price, ask_price))
+}
+
+pub fn tick_to_sqrt_ratio(tick: i32) -> u128 {
+    (1.0001f64.powf(tick as f64).sqrt() * 2f64.powf(64f64))
+        .to_u128()
+        .unwrap()
+}
+
+fn recip_q64(x: u128) -> u128 {
+    let q128 = Uint256::from_str("340282366920938463463374607431768211456").unwrap();
+    (q128 / x.into()).to_u128().unwrap()
+}
+
+fn shave_round_lots(liq: u128) -> u128 {
+    (liq >> 11) << 11
+}
+
+fn shave_round_lots_up(liq: u128) -> u128 {
+    ((liq >> 11) + 1) << 11
+}
+
+pub fn ambient_liq_to_flows(liq: u128, price_root: u128) -> (i128, i128) {
+    let base = (price_root * liq) >> 64;
+    let quote = (price_root << 64) / liq;
+
+    (base as i128, quote as i128)
+}
diff --git a/integration_tests/test_runner/src/tests/dex.rs b/integration_tests/test_runner/src/tests/dex.rs
index 361c1389..8c52ed12 100644
--- a/integration_tests/test_runner/src/tests/dex.rs
+++ b/integration_tests/test_runner/src/tests/dex.rs
@@ -4,13 +4,13 @@ use std::time::Duration;
 
 use crate::bootstrapping::DexAddresses;
 use crate::dex_utils::{
-    croc_policy_ops_resolution, croc_policy_treasury_resolution, croc_query_curve_tick,
-    croc_query_dex, croc_query_pool_params, croc_query_pool_template, croc_query_range_position,
-    dex_authority_transfer, dex_burn_ambient_pos, dex_burn_knockout_pos, dex_burn_ranged_pos,
-    dex_direct_protocol_cmd, dex_mint_ambient_pos, dex_mint_knockout_pos,
+    ambient_liq_to_flows, croc_policy_ops_resolution, croc_policy_treasury_resolution,
+    croc_query_curve_tick, croc_query_dex, croc_query_pool_params, croc_query_pool_template,
+    croc_query_price, croc_query_range_position, dex_authority_transfer, dex_direct_protocol_cmd,
+    dex_mint_ambient_in_amount, dex_mint_ambient_pos, dex_mint_knockout_pos,
     dex_mint_ranged_in_amount, dex_mint_ranged_pos, dex_query_authority, dex_query_safe_mode,
-    dex_swap, dex_user_cmd, OpsResolutionArgs, ProtocolCmdArgs, SwapArgs, UserCmdArgs, BOOT_PATH,
-    COLD_PATH, MAX_PRICE, MIN_PRICE, WARM_PATH,
+    dex_swap, dex_user_cmd, size_ambient_liq, OpsResolutionArgs, ProtocolCmdArgs, SwapArgs,
+    UserCmdArgs, BOOT_PATH, COLD_PATH, MAX_PRICE, MIN_PRICE, WARM_PATH,
 };
 use crate::type_urls::{
     COLLECT_TREASURY_PROPOSAL_TYPE_URL, HOT_PATH_OPEN_PROPOSAL_TYPE_URL, OPS_PROPOSAL_TYPE_URL,
@@ -33,9 +33,9 @@ use althea_proto::cosmos_sdk_proto::cosmos::params::v1beta1::{
 };
 use clarity::{Address as EthAddress, PrivateKey, Uint256};
 use deep_space::{Coin, Contact};
+use num256::Int256;
 use num_traits::ToPrimitive;
 use rand::Rng;
-use tokio::time::sleep;
 use web30::client::Web3;
 use web30::jsonrpc::error::Web3Error;
 use web30::types::TransactionResponse;
@@ -163,7 +163,7 @@ pub async fn populate_pool_basic(
     if range_pos.liq > 0u8.into() {
         info!("Range position already exists: {:?}", range_pos);
     } else {
-        let qty: Uint256 = one_eth() * 1024u32.into();
+        let qty: Uint256 = 1024000000000000000u128.into(); // 1.024 eth
         let bb = web3
             .get_erc20_balance(base, evm_user.eth_address)
             .await
@@ -180,39 +180,29 @@ pub async fn populate_pool_basic(
             .check_erc20_approved(quote, evm_user.eth_address, dex_contracts.dex)
             .await
             .unwrap();
-        info!("Before minting ranged position: base balance: {}, quote balance: {}, base approved: {}, quote approved: {}", bb, qb, ba, qa);
+        let one_eth_f = one_eth().to_f64().unwrap();
+        let bb_f = bb.to_i128().unwrap().to_f64().unwrap();
+        let qb_f = qb.to_i128().unwrap().to_f64().unwrap();
+        info!("Before minting ranged position: base balance: {}, quote balance: {}, base approved: {}, quote approved: {}", (bb_f) / one_eth_f, (qb_f) / one_eth_f, ba, qa);
         // Mint using the base token
-        dex_mint_ranged_in_amount(
-            web3,
-            dex_contracts.dex,
-            evm_user.eth_privkey,
-            base,
-            quote,
-            *POOL_IDX,
-            bid_tick,
-            ask_tick,
-            qty,
-            true,
-        )
-        .await;
-        // Mint using the quote token
-        dex_mint_ranged_in_amount(
+        dex_mint_ranged_pos(
             web3,
             dex_contracts.dex,
+            dex_contracts.query,
             evm_user.eth_privkey,
+            evm_user.eth_address,
             base,
             quote,
             *POOL_IDX,
             bid_tick,
             ask_tick,
             qty,
-            false,
         )
         .await;
     }
 
     // Finally, perform many smaller swaps to ensure the pool is working as expected
-    swap_many(web3, dex_contracts, base, quote, evm_user, 30).await;
+    swap_many(web3, dex_contracts, base, quote, evm_user, 30, None).await;
 }
 
 // Generates some additional positions, minting and burning ambient and ranged and knockout positions
@@ -230,8 +220,8 @@ pub async fn advanced_dex_test(
         caller: _,
         query: _,
         dex: _,
-        pool_base,
-        pool_quote,
+        pool_base: base,
+        pool_quote: quote,
     } = setup_params(
         web3,
         evm_user_keys,
@@ -248,51 +238,42 @@ pub async fn advanced_dex_test(
         dex_contracts.policy,
         &evm_user,
         &validator_keys,
-        pool_base,
-        pool_quote,
+        base,
+        quote,
         walthea,
     )
     .await;
-
-    let tick = croc_query_curve_tick(
+    let bb = web3
+        .get_erc20_balance(base, evm_user.eth_address)
+        .await
+        .unwrap();
+    let qb = web3
+        .get_erc20_balance(quote, evm_user.eth_address)
+        .await
+        .unwrap();
+    let ambient_qty = one_eth();
+    let price_root = croc_query_price(
         web3,
         dex_contracts.query,
-        Some(evm_user.eth_address),
-        pool_base,
-        pool_quote,
+        Some(*MINER_ETH_ADDRESS),
+        base,
+        quote,
         *POOL_IDX,
     )
     .await
-    .expect("Could not get curve tick for pool");
-
-    let ranged_bid_tick = tick - 32u8.into();
-    let ranged_ask_tick = tick + 32u8.into();
-    let liq = one_eth() * 10240u32.into();
-
+    .unwrap();
+    let pr_u128 = price_root.to_u128().unwrap();
+    let liq = size_ambient_liq(ambient_qty.to_u128().unwrap(), true, pr_u128, false);
     dex_mint_ambient_pos(
         web3,
         dex_contracts.dex,
         dex_contracts.query,
         evm_user.eth_privkey,
-        &evm_user,
-        pool_base,
-        pool_quote,
-        *POOL_IDX,
-        (1024u32 * 10000u32).into(),
-    )
-    .await;
-    dex_mint_ranged_pos(
-        web3,
-        dex_contracts.dex,
-        dex_contracts.query,
-        evm_user.eth_privkey,
-        &evm_user,
-        pool_base,
-        pool_quote,
+        evm_user.eth_address,
+        base,
+        quote,
         *POOL_IDX,
-        ranged_bid_tick,
-        ranged_ask_tick,
-        liq / 4u8.into(),
+        liq.into(),
     )
     .await;
 
@@ -300,81 +281,71 @@ pub async fn advanced_dex_test(
         web3,
         dex_contracts.query,
         Some(evm_user.eth_address),
-        pool_base,
-        pool_quote,
+        base,
+        quote,
         *POOL_IDX,
     )
     .await
     .expect("Could not get curve tick for pool");
-    let ko_bid_tick = tick - 32u32.into();
-    let ko_ask_tick = tick + 32u32.into();
 
-    dex_mint_knockout_pos(
-        web3,
-        dex_contracts.dex,
-        dex_contracts.query,
-        evm_user.eth_privkey,
-        &evm_user,
-        pool_base,
-        pool_quote,
-        *POOL_IDX,
-        ko_bid_tick,
-        ko_ask_tick,
-        true,
-        0u8,
-        (1024u32 * 10000u32).into(),
-        true,
-    )
-    .await;
+    let liq = one_eth() * 1024u32.into() / 100u32.into();
+    let tick_range = 10240;
+    for i in 0..10 {
+        let position_width = tick_range / 10; // Make the positions 1/10th of the total range
+        let tick_offset = tick_range / 10 * i - (tick_range / 2); // Divide into 10 portions, center around the current tick
+        let bid_tick = tick + tick_offset.into() - (Int256::from(position_width) / 2i8.into());
+        let ask_tick = tick + tick_offset.into() + (Int256::from(position_width) / 2i8.into());
 
-    sleep(Duration::from_secs(15)).await;
+        info!("Minting position between ticks {bid_tick} and {ask_tick}");
 
-    dex_burn_ranged_pos(
-        web3,
-        dex_contracts.dex,
-        dex_contracts.query,
-        evm_user.eth_privkey,
-        &evm_user,
+        // Mint two ranged positions (in each token)
+        dex_mint_ranged_in_amount(
+            web3,
+            dex_contracts.dex,
+            evm_user.eth_privkey,
+            base,
+            quote,
+            *POOL_IDX,
+            bid_tick,
+            ask_tick,
+            liq,
+            true,
+        )
+        .await;
+    }
+    info!("Successfully tested DEX");
+}
+pub async fn dex_swap_many(
+    web3: &Web3,
+    evm_user_keys: Vec<EthermintUserKey>,
+    erc20_contracts: Vec<EthAddress>,
+    dex_contracts: DexAddresses,
+) {
+    let DexTestParams {
+        evm_user,
+        caller: _,
+        query: _,
+        dex: _,
         pool_base,
         pool_quote,
-        *POOL_IDX,
-        ranged_bid_tick,
-        ranged_ask_tick,
-        liq / 4u8.into(),
-    )
-    .await;
-    dex_burn_knockout_pos(
+    } = setup_params(
         web3,
-        dex_contracts.dex,
-        dex_contracts.query,
-        evm_user.eth_privkey,
-        &evm_user,
-        pool_base,
-        pool_quote,
-        *POOL_IDX,
-        ko_bid_tick,
-        ko_ask_tick,
-        true,
-        0u8,
-        (1024u32 * 10000u32).into(),
-        true,
-        true,
-        None,
+        evm_user_keys,
+        dex_contracts.clone(),
+        erc20_contracts.clone(),
     )
     .await;
-    dex_burn_ambient_pos(
+    swap_many(
         web3,
-        dex_contracts.dex,
-        dex_contracts.query,
-        evm_user.eth_privkey,
-        &evm_user,
+        &dex_contracts,
         pool_base,
         pool_quote,
-        *POOL_IDX,
-        (1024u32 * 10000u32).into(),
+        &evm_user,
+        40,
+        Some(true),
     )
     .await;
-    info!("Successfully tested DEX");
+    info!("Successfully swapped many times");
 }
 
 pub struct DexTestParams {
@@ -782,13 +753,17 @@ async fn swap_many(
     pool_quote: EthAddress,
     evm_user: &EthermintUserKey,
     swaps: usize,
+    direction: Option<bool>,
 ) {
-    let mut is_buy = true; // Switch direction frequently, starting with base for quote
+    let mut is_buy = if let Some(d) = direction { d } else { true };
     let mut rng = rand::thread_rng();
 
     for _ in 0..swaps {
-        let qty_multi: u16 = rng.gen();
-        let qty = (1024u32 * u32::from(qty_multi)).into();
+        let mut qty_multi: u8 = rng.gen(); // 0 to 255
+        if qty_multi == 0 {
+            qty_multi = 1;
+        }
+        let qty = Uint256::from(1000000000000000u128) * qty_multi.into(); // .001 eth * qty_multi
         let pre_swap_base = web3
             .get_erc20_balance(pool_base, evm_user.eth_address)
             .await
@@ -855,7 +830,9 @@ async fn swap_many(
             );
         }
 
-        is_buy = !is_buy;
+        if direction.is_none() {
+            is_buy = !is_buy;
+        }
     }
 }
 

From 7c69ee2042e9aeae15a6b99ab2ab2e877b5733e4 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Mon, 16 Dec 2024 18:20:41 -0500
Subject: [PATCH 21/63] Implement evm fee burning test

---
 .github/workflows/integration-tests.yml       |  12 ++
 integration_tests/test_runner/src/bin/main.rs |  16 +++
 .../test_runner/src/tests/evm_fee_burning.rs  | 135 ++++++++++++++++++
 .../test_runner/src/tests/mod.rs              |   1 +
 4 files changed, 164 insertions(+)
 create mode 100644 integration_tests/test_runner/src/tests/evm_fee_burning.rs

diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
index 7fb20a8b..ccd68f4a 100644
--- a/.github/workflows/integration-tests.yml
+++ b/.github/workflows/integration-tests.yml
@@ -188,6 +188,18 @@ jobs:
           cache-on-failure: true
       - name: Tests the nativedex OpsProposal function
         run: tests/all-up-test-ci.sh DEX_OPS_PROPOSAL
+  EVM_FEE_BURNING:
+    needs: native_token
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions/setup-go@v5
+      - uses: Swatinem/rust-cache@v2
+        with:
+          workspaces: integration_tests/
+          cache-on-failure: true
+      - name: Checks that evm fees are truly burned
+        run: tests/all-up-test-ci.sh EVM_FEE_BURNING
   UPGRADE:
     needs: native_token
     runs-on: ubuntu-latest
diff --git a/integration_tests/test_runner/src/bin/main.rs b/integration_tests/test_runner/src/bin/main.rs
index 1be37c7f..9993b773 100644
--- a/integration_tests/test_runner/src/bin/main.rs
+++ b/integration_tests/test_runner/src/bin/main.rs
@@ -4,6 +4,7 @@
 #[macro_use]
 extern crate log;
 
+use althea_proto::canto::erc20;
 use deep_space::Contact;
 use deep_space::PrivateKey;
 use std::env;
@@ -22,6 +23,7 @@ use test_runner::tests::dex::dex_safe_mode_test;
 use test_runner::tests::dex::dex_swap_many;
 use test_runner::tests::dex::dex_upgrade_test;
 use test_runner::tests::erc20_conversion::erc20_conversion_test;
+use test_runner::tests::evm_fee_burning::evm_fee_burning_test;
 use test_runner::tests::ica_host::ica_host_happy_path;
 use test_runner::tests::liquid_accounts::liquid_accounts_test;
 use test_runner::tests::lockup::lockup_test;
@@ -280,6 +282,20 @@ pub async fn main() {
             )
             .await;
             return;
+        } else if test_type == "EVM_FEES"
+            || test_type == "EVM_FEE_BURNING"
+            || test_type == "EVM_FEE_BURN"
+        {
+            info!("Start evm fee burning test");
+            evm_fee_burning_test(
+                &contact,
+                &web30,
+                keys,
+                EVM_USER_KEYS.clone(),
+                erc20_addresses,
+            )
+            .await;
+            return;
         } else if test_type == "UPGRADE_PART_1" {
             upgrade_part_1(
                 &web30,
diff --git a/integration_tests/test_runner/src/tests/evm_fee_burning.rs b/integration_tests/test_runner/src/tests/evm_fee_burning.rs
new file mode 100644
index 00000000..55357d14
--- /dev/null
+++ b/integration_tests/test_runner/src/tests/evm_fee_burning.rs
@@ -0,0 +1,135 @@
+use std::time::Duration;
+
+use crate::utils::{
+    create_parameter_change_proposal, one_atom, vote_yes_on_proposals,
+    wait_for_proposals_to_execute, EthermintUserKey, ValidatorKeys, OPERATION_TIMEOUT,
+    STAKING_TOKEN,
+};
+use althea_proto::cosmos_sdk_proto::cosmos::base::v1beta1::DecCoin;
+use althea_proto::cosmos_sdk_proto::cosmos::distribution::v1beta1::{
+    query_client::QueryClient as DistributionQueryClient, QueryValidatorOutstandingRewardsRequest,
+};
+use althea_proto::cosmos_sdk_proto::cosmos::params::v1beta1::ParamChange;
+use clarity::Address as EthAddress;
+use deep_space::{Coin, Contact, PrivateKey};
+use futures::future::join_all;
+use num256::Uint256;
+use tokio::time::sleep;
+use web30::client::Web3;
+
+pub async fn evm_fee_burning_test(
+    contact: &Contact,
+    web3: &Web3,
+    validator_keys: Vec<ValidatorKeys>,
+    evm_user_keys: Vec<EthermintUserKey>,
+    erc20_contracts: Vec<EthAddress>,
+) {
+    info!("Set inflation to 0");
+    set_inflation_to_zero(contact, &validator_keys).await;
+
+    let pre_balances = snapshot_validator_rewards(contact, &validator_keys).await;
+
+    info!("Generating some fees");
+    let gas_multiplier = web30::types::SendTxOption::GasPriceMultiplier(10.0);
+    for _ in 0..35 {
+        let mut fs = vec![];
+        for user in &evm_user_keys {
+            fs.push(web3.approve_erc20_transfers(
+                erc20_contracts[0],
+                user.eth_privkey,
+                erc20_contracts[1],
+                Some(Duration::from_secs(15)),
+                vec![gas_multiplier.clone()],
+            ));
+        }
+
+        join_all(fs).await;
+    }
+
+    sleep(Duration::from_secs(10)).await;
+    let post_balances = snapshot_validator_rewards(contact, &validator_keys).await;
+    assert_eq!(pre_balances, post_balances);
+
+    // Check that other fees are not burned, and wind up in validator accounts
+    let pre_balances = snapshot_validator_rewards(contact, &validator_keys).await;
+    let mut fs = vec![];
+    for user in &evm_user_keys {
+        fs.push(contact.send_coins(
+            Coin {
+                amount: one_atom(),
+                denom: STAKING_TOKEN.clone(),
+            },
+            Some(Coin {
+                amount: one_atom() * 100u8.into(),
+                denom: STAKING_TOKEN.clone(),
+            }),
+            evm_user_keys[0].ethermint_address,
+            Some(Duration::from_secs(20)),
+            user.ethermint_key,
+        ));
+    }
+    join_all(fs).await;
+    // info!("Results: {:?}", results);
+    sleep(Duration::from_secs(10)).await;
+    let post_balances = snapshot_validator_rewards(contact, &validator_keys).await;
+    info!("Pre: {:?}, Post: {:?}", pre_balances, post_balances);
+    for (pre, post) in pre_balances.iter().zip(post_balances.iter()) {
+        for (pr, po) in pre.iter().zip(post.iter()) {
+            assert_eq!(pr.denom, po.denom);
+            let pree: Uint256 = pr.amount.parse().expect("Invalid integer?");
+            let postt: Uint256 = po.amount.parse().expect("Invalid integer?");
+            assert!(pree < postt);
+        }
+    }
+
+    info!("Successfully tested EVM fee burning");
+}
+
+pub async fn set_inflation_to_zero(contact: &Contact, validator_keys: &[ValidatorKeys]) {
+    let to_change = vec![
+        ParamChange {
+            subspace: "mint".to_string(),
+            key: "InflationMax".to_string(),
+            value: "\"0.0\"".to_string(),
+        },
+        ParamChange {
+            subspace: "mint".to_string(),
+            key: "InflationMin".to_string(),
+            value: "\"0.0\"".to_string(),
+        },
+    ];
+
+    let proposer = validator_keys.first().unwrap();
+    let zero_fee = Coin {
+        denom: STAKING_TOKEN.clone(),
+        amount: 0u8.into(),
+    };
+    create_parameter_change_proposal(contact, proposer.validator_key, to_change, zero_fee).await;
+
+    vote_yes_on_proposals(contact, validator_keys, Some(OPERATION_TIMEOUT)).await;
+    wait_for_proposals_to_execute(contact).await;
+}
+
+pub async fn snapshot_validator_rewards(
+    contact: &Contact,
+    validator_keys: &[ValidatorKeys],
+) -> Vec<Vec<DecCoin>> {
+    let mut grpc = DistributionQueryClient::connect(contact.get_url())
+        .await
+        .unwrap();
+    let mut rewards = Vec::new();
+    for key in validator_keys {
+        let reward = grpc
+            .validator_outstanding_rewards(QueryValidatorOutstandingRewardsRequest {
+                validator_address: key
+                    .validator_key
+                    .to_address("altheavaloper")
+                    .unwrap()
+                    .to_string(),
+            })
+            .await
+            .expect("Unable to get rewards");
+        rewards.push(reward.into_inner().rewards.expect("No rewards").rewards);
+    }
+    rewards
+}
diff --git a/integration_tests/test_runner/src/tests/mod.rs b/integration_tests/test_runner/src/tests/mod.rs
index 191584e3..b043aa35 100644
--- a/integration_tests/test_runner/src/tests/mod.rs
+++ b/integration_tests/test_runner/src/tests/mod.rs
@@ -1,5 +1,6 @@
 pub mod dex;
 pub mod erc20_conversion;
+pub mod evm_fee_burning;
 pub mod ica_host;
 pub mod liquid_accounts;
 pub mod lockup;

From 9bce867d4416cff55ed659cb2577628da526f159 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Mon, 16 Dec 2024 20:53:10 -0500
Subject: [PATCH 22/63] Fix MsgEthereumTx support in lockup module

---
 integration_tests/test_runner/src/bin/main.rs |  9 ++-
 .../test_runner/src/tests/lockup.rs           | 64 +++++++++++++++++--
 x/lockup/ante.go                              | 24 ++++++-
 x/lockup/types/genesis.go                     | 13 +++-
 4 files changed, 98 insertions(+), 12 deletions(-)

diff --git a/integration_tests/test_runner/src/bin/main.rs b/integration_tests/test_runner/src/bin/main.rs
index 9993b773..cff343bc 100644
--- a/integration_tests/test_runner/src/bin/main.rs
+++ b/integration_tests/test_runner/src/bin/main.rs
@@ -119,7 +119,14 @@ pub async fn main() {
     if let Ok(test_type) = test_type {
         if test_type == "LOCKUP" {
             info!("Starting Lockup test");
-            lockup_test(&contact, keys).await;
+            lockup_test(
+                &contact,
+                keys,
+                &web30,
+                EVM_USER_KEYS.clone(),
+                erc20_addresses,
+            )
+            .await;
             return;
         } else if test_type == "MICROTX_FEES" {
             info!("Starting microtx fees test");
diff --git a/integration_tests/test_runner/src/tests/lockup.rs b/integration_tests/test_runner/src/tests/lockup.rs
index 5cab3093..b0122a5c 100644
--- a/integration_tests/test_runner/src/tests/lockup.rs
+++ b/integration_tests/test_runner/src/tests/lockup.rs
@@ -1,4 +1,4 @@
-use std::time::SystemTime;
+use std::time::{Duration, SystemTime};
 
 use crate::type_urls::{
     GENERIC_AUTHORIZATION_TYPE_URL, MSG_EXEC_TYPE_URL, MSG_GRANT_TYPE_URL, MSG_MICROTX_TYPE_URL,
@@ -18,9 +18,11 @@ use althea_proto::cosmos_sdk_proto::cosmos::bank::v1beta1::{Input, MsgMultiSend,
 use althea_proto::cosmos_sdk_proto::cosmos::base::v1beta1::Coin as ProtoCoin;
 use althea_proto::cosmos_sdk_proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress;
 use althea_proto::cosmos_sdk_proto::cosmos::params::v1beta1::ParamChange;
+use clarity::Address as EthAddress;
 use clarity::Uint256;
 use deep_space::error::CosmosGrpcError;
 use deep_space::{Address, Coin, Contact, Msg, PrivateKey};
+use web30::client::Web3;
 
 /// These *_PARAM_KEY constants are defined in x/lockup/types/types.go and must match those values exactly
 pub const LOCKED_PARAM_KEY: &str = "locked";
@@ -31,7 +33,13 @@ pub const LOCKED_TOKEN_DENOMS_PARAM_KEY: &str = "lockedTokenDenoms";
 /// Simulates the launch lockup process by setting the lockup module params via governance,
 /// attempting to transfer tokens a variety of ways, and finally clearing the lockup module params
 /// and asserting that balances can successfully be transferred
-pub async fn lockup_test(contact: &Contact, validator_keys: Vec<ValidatorKeys>) {
+pub async fn lockup_test(
+    contact: &Contact,
+    validator_keys: Vec<ValidatorKeys>,
+    web3: &Web3,
+    evm_user_keys: Vec<EthermintUserKey>,
+    erc20_addresses: Vec<EthAddress>,
+) {
     let lock_exempt = get_user_key(None);
     let msg_send_authorized = get_user_key(None);
     let msg_multi_send_authorized = get_user_key(None);
@@ -44,7 +52,14 @@ pub async fn lockup_test(contact: &Contact, validator_keys: Vec<ValidatorKeys>)
         msg_multi_send_authorized,
     )
     .await;
-    lockup_the_chain(contact, &validator_keys, &lock_exempt).await;
+    lockup_the_chain(
+        contact,
+        &validator_keys,
+        vec![&lock_exempt, &evm_user_keys[0]],
+    )
+    .await;
+    send_evm_tx(evm_user_keys[1], web3, &erc20_addresses, false).await;
+    send_evm_tx(evm_user_keys[0], web3, &erc20_addresses, true).await;
 
     // TODO: Add ibc transfer and check that transfers are blocked outbound for aalthea
     fail_to_send(
@@ -62,6 +77,8 @@ pub async fn lockup_test(contact: &Contact, validator_keys: Vec<ValidatorKeys>)
 
     unlock_the_chain(contact, &validator_keys).await;
     successfully_send(contact, &validator_keys, lock_exempt).await;
+    send_evm_tx(evm_user_keys[1], web3, &erc20_addresses, true).await;
+    send_evm_tx(evm_user_keys[0], web3, &erc20_addresses, true).await;
 }
 
 async fn fund_lock_exempt_user(
@@ -126,9 +143,9 @@ async fn fund_authorized_users(
 pub async fn lockup_the_chain(
     contact: &Contact,
     validator_keys: &[ValidatorKeys],
-    lock_exempt: &EthermintUserKey,
+    lock_exempt: Vec<&EthermintUserKey>,
 ) {
-    let to_change = create_lockup_param_changes(lock_exempt.ethermint_address);
+    let to_change = create_lockup_param_changes(lock_exempt);
     let proposer = validator_keys.first().unwrap();
     let zero_fee = Coin {
         denom: STAKING_TOKEN.clone(),
@@ -140,7 +157,7 @@ pub async fn lockup_the_chain(
     wait_for_proposals_to_execute(contact).await;
 }
 
-pub fn create_lockup_param_changes(exempt_user: Address) -> Vec<ParamChange> {
+pub fn create_lockup_param_changes(exempt_users: Vec<&EthermintUserKey>) -> Vec<ParamChange> {
     // Params{lock_exempt:, locked: false, locked_message_types: Vec::new() };
     let lockup_param = ParamChange {
         subspace: "lockup".to_string(),
@@ -153,7 +170,13 @@ pub fn create_lockup_param_changes(exempt_user: Address) -> Vec<ParamChange> {
 
     let mut lock_exempt = lockup_param.clone();
     lock_exempt.key = LOCK_EXEMPT_PARAM_KEY.to_string();
-    lock_exempt.value = serde_json::to_string(&vec![exempt_user.to_string()]).unwrap();
+    lock_exempt.value = serde_json::to_string(
+        &exempt_users
+            .into_iter()
+            .map(|v| v.ethermint_address)
+            .collect::<Vec<_>>(),
+    )
+    .unwrap();
 
     let locked_msgs = vec![
         MSG_SEND_TYPE_URL.to_string(),
@@ -680,3 +703,30 @@ async fn send_unlocked_token(contact: &Contact, validator_keys: &[ValidatorKeys]
     };
     send_from_and_assert_balance_changes(contact, val0, amount.clone()).await;
 }
+
+async fn send_evm_tx(
+    user: EthermintUserKey,
+    web3: &Web3,
+    erc20_addresses: &[EthAddress],
+    expect_success: bool,
+) {
+    let tx = web3
+        .approve_erc20_transfers(
+            erc20_addresses[0],
+            user.eth_privkey,
+            erc20_addresses[1],
+            Some(Duration::from_secs(15)),
+            vec![],
+        )
+        .await;
+    info!(
+        "Submitted evm tx ({} expecting an error): {:?}",
+        if expect_success { "not" } else { "" },
+        tx
+    );
+    if expect_success {
+        tx.expect("Failed to submit evm tx!");
+    } else {
+        tx.expect_err("Successfully submitted evm tx? Should not be possible!");
+    }
+}
diff --git a/x/lockup/ante.go b/x/lockup/ante.go
index 5f869855..e1dcaeb7 100644
--- a/x/lockup/ante.go
+++ b/x/lockup/ante.go
@@ -12,9 +12,12 @@ import (
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	authz "github.com/cosmos/cosmos-sdk/x/authz"
 	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+	"github.com/ethereum/go-ethereum/common"
 
 	ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
 
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+
 	microtxtypes "github.com/AltheaFoundation/althea-L1/x/microtx/types"
 
 	"github.com/AltheaFoundation/althea-L1/x/lockup/keeper"
@@ -125,6 +128,9 @@ func (lad LockAnteDecorator) isAcceptable(ctx sdk.Context, msg sdk.Msg) error {
 		// Check that any locked msg is permissible on a type-case basis
 		if allow, err := allowMessage(msg, exemptSet, lockedTokenDenomsSet); !allow {
 			return sdkerrors.Wrap(err, fmt.Sprintf("Transaction blocked because of message %v", msg))
+		} else {
+			// The user is exempt, allow it to pass
+			return nil
 		}
 	}
 	if msgType == "/cosmos.distribution.v1beta1.MsgSetWithdrawAddress" {
@@ -134,7 +140,11 @@ func (lad LockAnteDecorator) isAcceptable(ctx sdk.Context, msg sdk.Msg) error {
 		return sdkerrors.Wrap(types.ErrLocked, "The chain is locked, recursively MsgExec-wrapped Msgs are not allowed")
 	}
 	if msgType == "/ethermint.evm.v1.MsgEthereumTx" {
-		return sdkerrors.Wrap(types.ErrLocked, "The chain is locked, only exempt addresses may submit this Msg type")
+		if allow, err := allowMessage(msg, exemptSet, lockedTokenDenomsSet); !allow {
+			return sdkerrors.Wrap(err, "The chain is locked, only exempt addresses may submit this Msg type")
+		} else {
+			return nil
+		}
 	}
 
 	return nil
@@ -213,6 +223,18 @@ func allowMessage(msg sdk.Msg, exemptSet map[string]struct{}, lockedTokenDenomsS
 		}
 		return true, nil
 
+	// ^v^v^v^v^v^v^v^v^v^v^v^v EVM MODULE MESSAGES ^v^v^v^v^v^v^v^v^v^v^v^v
+	// nolint: exhaustruct
+	case sdk.MsgTypeURL(&evmtypes.MsgEthereumTx{}):
+		msgEvmTx := msg.(*evmtypes.MsgEthereumTx)
+		addressBytes := common.HexToAddress(msgEvmTx.From).Bytes()
+		ethermintAddr := sdk.AccAddress(addressBytes)
+		if _, present := exemptSet[ethermintAddr.String()]; !present {
+			return false, sdkerrors.Wrap(types.ErrLocked,
+				"The chain is locked, only exempt addresses may send a MsgEthereumTx")
+		}
+		return true, nil
+
 	default:
 		return false, sdkerrors.Wrap(types.ErrUnhandled,
 			fmt.Sprintf("Message type %v does not have a case in allowMessage, unable to handle messages like this",
diff --git a/x/lockup/types/genesis.go b/x/lockup/types/genesis.go
index 8b7c5d16..61010084 100644
--- a/x/lockup/types/genesis.go
+++ b/x/lockup/types/genesis.go
@@ -9,6 +9,8 @@ import (
 	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
 	ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
 
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+
 	"github.com/AltheaFoundation/althea-L1/config"
 	microtxtypes "github.com/AltheaFoundation/althea-L1/x/microtx/types"
 )
@@ -23,7 +25,7 @@ func DefaultGenesisState() *GenesisState {
 func DefaultParams() *Params {
 	return &Params{
 		Locked:     false,
-		LockExempt: []string{"0x0000000000000000000000000000000000000000"},
+		LockExempt: []string{},
 		LockedMessageTypes: []string{
 			// nolint: exhaustruct
 			sdk.MsgTypeURL(&banktypes.MsgSend{}),
@@ -33,6 +35,8 @@ func DefaultParams() *Params {
 			sdk.MsgTypeURL(&ibctransfertypes.MsgTransfer{}),
 			// nolint: exhaustruct
 			sdk.MsgTypeURL(&microtxtypes.MsgMicrotx{}),
+			// nolint: exhaustruct
+			sdk.MsgTypeURL(&evmtypes.MsgEthereumTx{}),
 		},
 		/* Note: The authoritative way to get the native token of the chain is by calling
 		   mintKeeper.GetParams(ctx).MintDenom, but the context is not available yet
@@ -71,9 +75,12 @@ func ValidateLockExempt(i interface{}) error {
 	if !ok {
 		return fmt.Errorf("invalid lock exempt type: %T", i)
 	}
-	if len(v) == 0 {
-		return fmt.Errorf("no lock exempt addresses %v", v)
+	for i, address := range v {
+		if _, err := sdk.AccAddressFromBech32(address); err != nil {
+			return fmt.Errorf("invalid lock exempt address %d: %s", i, address)
+		}
 	}
+
 	return nil
 }
 

From 4acdb8c45f8d8dec2a7d105406b1d643bf0df14b Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Thu, 19 Dec 2024 16:54:57 -0500
Subject: [PATCH 23/63] Update dex contracts for events

---
 solidity-dex | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/solidity-dex b/solidity-dex
index 4cd0b972..1f970635 160000
--- a/solidity-dex
+++ b/solidity-dex
@@ -1 +1 @@
-Subproject commit 4cd0b972df204e93c87b9dd52e0ee1c5a9466aef
+Subproject commit 1f970635f0de00ba133b201fafb1f28ee03bcd89

From 1e6bcf2a110945b44b3b6131657c71194c51d892 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Mon, 23 Dec 2024 20:36:22 -0500
Subject: [PATCH 24/63] Make two-script tests easier to run

---
 tests/container-scripts/all-up-test-internal.sh         | 5 +++++
 tests/container-scripts/integration-tests.sh            | 8 +++++---
 tests/container-scripts/manual-upgrade-test-internal.sh | 5 +++++
 tests/container-scripts/reload-code.sh                  | 8 ++++++++
 tests/container-scripts/upgrade-test-internal.sh        | 4 ++++
 5 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/tests/container-scripts/all-up-test-internal.sh b/tests/container-scripts/all-up-test-internal.sh
index e0ab7242..29417c70 100755
--- a/tests/container-scripts/all-up-test-internal.sh
+++ b/tests/container-scripts/all-up-test-internal.sh
@@ -2,6 +2,9 @@
 # the script run inside the container for all-up-test.sh
 NODES=$1
 TEST_TYPE=$2
+
+rm /althea/test-ready-to-run
+
 set -eux
 
 bash /althea/tests/container-scripts/setup-validators.sh $NODES
@@ -14,4 +17,6 @@ sleep 30
 pushd /althea/integration_tests/test_runner
 DEPLOY_CONTRACTS=1 RUST_BACKTRACE=full TEST_TYPE=$TEST_TYPE NO_GAS_OPT=1 RUST_LOG="INFO" PATH=$PATH:$HOME/.cargo/bin cargo run --release --bin test-runner
 
+touch /althea/test-ready-to-run
+
 bash /althea/tests/container-scripts/integration-tests.sh $NODES $TEST_TYPE
\ No newline at end of file
diff --git a/tests/container-scripts/integration-tests.sh b/tests/container-scripts/integration-tests.sh
index fa68e55a..d0340a1d 100755
--- a/tests/container-scripts/integration-tests.sh
+++ b/tests/container-scripts/integration-tests.sh
@@ -3,9 +3,11 @@ NODES=$1
 TEST_TYPE=$2
 set -eu
 
-set +e
-killall -9 test-runner
-set -e
+echo "Waiting for /althea/test-ready-to-run to exist before starting the test"
+while [ ! -f /althea/test-ready-to-run ];
+do
+    sleep 1
+done
 
 pushd /althea/integration_tests/test_runner
 RUST_BACKTRACE=full TEST_TYPE=$TEST_TYPE RUST_LOG=INFO PATH=$PATH:$HOME/.cargo/bin cargo run --release --bin test-runner
diff --git a/tests/container-scripts/manual-upgrade-test-internal.sh b/tests/container-scripts/manual-upgrade-test-internal.sh
index 7b80df4e..6d012bee 100755
--- a/tests/container-scripts/manual-upgrade-test-internal.sh
+++ b/tests/container-scripts/manual-upgrade-test-internal.sh
@@ -1,4 +1,7 @@
 #!/bin/bash
+
+rm /althea/test-ready-to-run
+
 set -eux
 # Number of validators to start
 NODES=$1
@@ -41,6 +44,8 @@ pushd integration_tests/test_runner
 DEPLOY_CONTRACTS=1 RUST_BACKTRACE=full NO_GAS_OPT=1 RUST_LOG="INFO" PATH=$PATH:$HOME/.cargo/bin cargo run --release --bin test-runner
 popd
 
+touch /althea/test-ready-to-run
+
 # This allows the tester to run the first part of the test
 # immediately if the nodes are killed by a different process
 
diff --git a/tests/container-scripts/reload-code.sh b/tests/container-scripts/reload-code.sh
index 24faa753..ea129d9a 100755
--- a/tests/container-scripts/reload-code.sh
+++ b/tests/container-scripts/reload-code.sh
@@ -15,6 +15,11 @@ do
 done
 
 
+# Remove the setup complete flag file
+set +e
+rm -fr /althea/test-ready-to-run
+set -e
+
 cd /althea/
 export PATH=$PATH:/usr/local/go/bin
 make install-core
@@ -35,6 +40,9 @@ sleep 10
 pushd /althea/integration_tests/test_runner
 DEPLOY_CONTRACTS=1 RUST_BACKTRACE=full TEST_TYPE=$TEST_TYPE NO_GAS_OPT=1 RUST_LOG="INFO" PATH=$PATH:$HOME/.cargo/bin cargo run --release --bin test-runner
 
+# Create a setup complete flag file used by the integration tests
+touch /althea/test-ready-to-run
+
 # This keeps the script open to prevent Docker from stopping the container
 # immediately if the nodes are killed by a different process
 read -p "Press Return to Close..."
\ No newline at end of file
diff --git a/tests/container-scripts/upgrade-test-internal.sh b/tests/container-scripts/upgrade-test-internal.sh
index da3fc95b..103fab27 100755
--- a/tests/container-scripts/upgrade-test-internal.sh
+++ b/tests/container-scripts/upgrade-test-internal.sh
@@ -1,4 +1,6 @@
 #!/bin/bash
+
+rm /althea/test-ready-to-run
 set -eux
 # Number of validators to start
 NODES=$1
@@ -39,6 +41,8 @@ pushd /althea/integration_tests/test_runner
 DEPLOY_CONTRACTS=1 RUST_BACKTRACE=full NO_GAS_OPT=1 RUST_LOG="INFO" PATH=$PATH:$HOME/.cargo/bin cargo run --release --bin test-runner
 popd
 
+touch /althea/test-ready-to-run
+
 # Run the pre-upgrade tests
 pushd /althea/
 tests/container-scripts/integration-tests.sh $NODES UPGRADE_PART_1

From 5a9267dbd10011df24f33aca835bee3d0c140266 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Mon, 23 Dec 2024 21:07:06 -0500
Subject: [PATCH 25/63] Add supply check to evm fee burning test

---
 .gitignore                                    |  1 +
 integration_tests/test_runner/src/bin/main.rs |  1 -
 .../test_runner/src/tests/dex.rs              | 23 ++++++-------------
 .../test_runner/src/tests/evm_fee_burning.rs  | 15 ++++++++++++
 4 files changed, 23 insertions(+), 17 deletions(-)

diff --git a/.gitignore b/.gitignore
index 43babe7d..dea6936c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,6 +33,7 @@ mytestnet
 coverage.txt
 profile.out
 tests/dockerfile/althea.tar.gz
+test-ready-to-run
 
 # Vagrant
 .vagrant/
diff --git a/integration_tests/test_runner/src/bin/main.rs b/integration_tests/test_runner/src/bin/main.rs
index cff343bc..8b189a4b 100644
--- a/integration_tests/test_runner/src/bin/main.rs
+++ b/integration_tests/test_runner/src/bin/main.rs
@@ -4,7 +4,6 @@
 #[macro_use]
 extern crate log;
 
-use althea_proto::canto::erc20;
 use deep_space::Contact;
 use deep_space::PrivateKey;
 use std::env;
diff --git a/integration_tests/test_runner/src/tests/dex.rs b/integration_tests/test_runner/src/tests/dex.rs
index 8c52ed12..113a43dc 100644
--- a/integration_tests/test_runner/src/tests/dex.rs
+++ b/integration_tests/test_runner/src/tests/dex.rs
@@ -4,13 +4,12 @@ use std::time::Duration;
 
 use crate::bootstrapping::DexAddresses;
 use crate::dex_utils::{
-    ambient_liq_to_flows, croc_policy_ops_resolution, croc_policy_treasury_resolution,
-    croc_query_curve_tick, croc_query_dex, croc_query_pool_params, croc_query_pool_template,
-    croc_query_price, croc_query_range_position, dex_authority_transfer, dex_direct_protocol_cmd,
-    dex_mint_ambient_in_amount, dex_mint_ambient_pos, dex_mint_knockout_pos,
-    dex_mint_ranged_in_amount, dex_mint_ranged_pos, dex_query_authority, dex_query_safe_mode,
-    dex_swap, dex_user_cmd, size_ambient_liq, OpsResolutionArgs, ProtocolCmdArgs, SwapArgs,
-    UserCmdArgs, BOOT_PATH, COLD_PATH, MAX_PRICE, MIN_PRICE, WARM_PATH,
+    croc_policy_ops_resolution, croc_policy_treasury_resolution, croc_query_curve_tick,
+    croc_query_dex, croc_query_pool_params, croc_query_pool_template, croc_query_price,
+    croc_query_range_position, dex_authority_transfer, dex_direct_protocol_cmd,
+    dex_mint_ambient_pos, dex_mint_ranged_in_amount, dex_mint_ranged_pos, dex_query_authority,
+    dex_query_safe_mode, dex_swap, dex_user_cmd, size_ambient_liq, OpsResolutionArgs,
+    ProtocolCmdArgs, SwapArgs, UserCmdArgs, BOOT_PATH, COLD_PATH, MAX_PRICE, MIN_PRICE, WARM_PATH,
 };
 use crate::type_urls::{
     COLLECT_TREASURY_PROPOSAL_TYPE_URL, HOT_PATH_OPEN_PROPOSAL_TYPE_URL, OPS_PROPOSAL_TYPE_URL,
@@ -243,14 +242,6 @@ pub async fn advanced_dex_test(
         walthea,
     )
     .await;
-    let bb = web3
-        .get_erc20_balance(base, evm_user.eth_address)
-        .await
-        .unwrap();
-    let qb = web3
-        .get_erc20_balance(quote, evm_user.eth_address)
-        .await
-        .unwrap();
     let ambient_qty = one_eth();
     let price_root = croc_query_price(
         web3,
@@ -755,7 +746,7 @@ async fn swap_many(
     swaps: usize,
     direction: Option<bool>,
 ) {
-    let mut is_buy = if let Some(d) = direction { d } else { true };
+    let mut is_buy = direction.unwrap_or(true);
     let mut rng = rand::thread_rng();
 
     for _ in 0..swaps {
diff --git a/integration_tests/test_runner/src/tests/evm_fee_burning.rs b/integration_tests/test_runner/src/tests/evm_fee_burning.rs
index 55357d14..91eabf06 100644
--- a/integration_tests/test_runner/src/tests/evm_fee_burning.rs
+++ b/integration_tests/test_runner/src/tests/evm_fee_burning.rs
@@ -27,6 +27,11 @@ pub async fn evm_fee_burning_test(
     info!("Set inflation to 0");
     set_inflation_to_zero(contact, &validator_keys).await;
 
+    let pre_supply = contact
+        .query_supply_of(STAKING_TOKEN.clone())
+        .await
+        .expect("Unable to get aalthea supply")
+        .expect("No supply of aalthea?");
     let pre_balances = snapshot_validator_rewards(contact, &validator_keys).await;
 
     info!("Generating some fees");
@@ -47,6 +52,16 @@ pub async fn evm_fee_burning_test(
     }
 
     sleep(Duration::from_secs(10)).await;
+    let post_supply = contact
+        .query_supply_of(STAKING_TOKEN.clone())
+        .await
+        .expect("Unable to get aalthea supply")
+        .expect("No supply of aalthea?");
+    assert!(pre_supply.amount > post_supply.amount);
+    info!(
+        "Supply decreased by: {}",
+        pre_supply.amount - post_supply.amount
+    );
     let post_balances = snapshot_validator_rewards(contact, &validator_keys).await;
     assert_eq!(pre_balances, post_balances);
 

From 8ad29a8ab610b20cc0bbca7838c1a90083724927 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Fri, 27 Dec 2024 12:37:57 -0500
Subject: [PATCH 26/63] Migrate go contracts to solidity repo, fix compilation

---
 contracts/ERC20MinterBurnerDecimals.sol       | 125 -------
 contracts/Port.sol                            |  83 ----
 contracts/ProposalStore.go                    |  23 --
 contracts/callee.go                           |  23 --
 contracts/callee.sol                          |  11 -
 contracts/caller.go                           |  22 --
 contracts/caller.sol                          |  27 --
 .../compiled_contracts/ERC20Burnable.json     |   5 -
 .../ERC20DirectBalanceManipulation.json       |   5 -
 .../ERC20MaliciousDelayed.json                |   5 -
 .../ERC20MinterBurnerDecimals.json            |   5 -
 .../compiled_contracts/ProposalStore.json     |   4 -
 contracts/compiled_contracts/Turnstile.json   |   6 -
 contracts/compiled_contracts/callee.json      |   6 -
 contracts/compiled_contracts/caller.json      |   5 -
 contracts/csr.go                              |  25 --
 contracts/erc20.go                            |   2 +-
 contracts/erc20DirectBalanceManipulation.go   |   2 +-
 contracts/erc20burnable.go                    |   2 +-
 contracts/erc20maliciousdelayed.go            |   2 +-
 contracts/package-lock.json                   |  13 -
 contracts/package.json                        |  26 --
 contracts/turnstile.sol                       | 155 --------
 scripts/compile-contracts-for-go.sh           |  12 +-
 .../contracts}/ERC20Burnable.sol              |   6 +-
 .../ERC20DirectBalanceManipulation.sol        |   4 +-
 .../contracts}/ERC20MaliciousDelayed.sol      |   4 +-
 .../contracts/ERC20MinterBurnerDecimals.sol   | 145 +++++++
 .../contracts/LiquidInfrastructureERC20.sol   | 354 ++++++++++++++++++
 29 files changed, 518 insertions(+), 589 deletions(-)
 delete mode 100644 contracts/ERC20MinterBurnerDecimals.sol
 delete mode 100644 contracts/Port.sol
 delete mode 100644 contracts/ProposalStore.go
 delete mode 100644 contracts/callee.go
 delete mode 100644 contracts/callee.sol
 delete mode 100644 contracts/caller.go
 delete mode 100644 contracts/caller.sol
 delete mode 100644 contracts/compiled_contracts/ERC20Burnable.json
 delete mode 100644 contracts/compiled_contracts/ERC20DirectBalanceManipulation.json
 delete mode 100644 contracts/compiled_contracts/ERC20MaliciousDelayed.json
 delete mode 100644 contracts/compiled_contracts/ERC20MinterBurnerDecimals.json
 delete mode 100644 contracts/compiled_contracts/ProposalStore.json
 delete mode 100644 contracts/compiled_contracts/Turnstile.json
 delete mode 100644 contracts/compiled_contracts/callee.json
 delete mode 100644 contracts/compiled_contracts/caller.json
 delete mode 100644 contracts/csr.go
 delete mode 100644 contracts/package-lock.json
 delete mode 100644 contracts/package.json
 delete mode 100644 contracts/turnstile.sol
 rename {contracts => solidity/contracts}/ERC20Burnable.sol (90%)
 rename {contracts => solidity/contracts}/ERC20DirectBalanceManipulation.sol (88%)
 rename {contracts => solidity/contracts}/ERC20MaliciousDelayed.sol (89%)
 create mode 100644 solidity/contracts/ERC20MinterBurnerDecimals.sol
 create mode 100644 solidity/contracts/LiquidInfrastructureERC20.sol

diff --git a/contracts/ERC20MinterBurnerDecimals.sol b/contracts/ERC20MinterBurnerDecimals.sol
deleted file mode 100644
index f36b9eff..00000000
--- a/contracts/ERC20MinterBurnerDecimals.sol
+++ /dev/null
@@ -1,125 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts v4.3.2 (token/ERC20/presets/ERC20PresetMinterPauser.sol)
-
-pragma solidity ^0.8.0;
-
-import "./@openzeppelin/contracts/token/ERC20/ERC20.sol";
-import "./@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
-import "./@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
-import "./@openzeppelin/contracts/access/AccessControlEnumerable.sol";
-import "./@openzeppelin/contracts/utils/Context.sol";
-
-/**
- * @dev {ERC20} token, including:
- *
- *  - ability for holders to burn (destroy) their tokens
- *  - a minter role that allows for token minting (creation)
- *  - a pauser role that allows to stop all token transfers
- *
- * This contract uses {AccessControl} to lock permissioned functions using the
- * different roles - head to its documentation for details.
- *
- * The account that deploys the contract will be granted the minter and pauser
- * roles, as well as the default admin role, which will let it grant both minter
- * and pauser roles to other accounts.
- */
-contract ERC20MinterBurnerDecimals is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable {
-  bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
-  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
-  bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
-  uint8 private _decimals;
-
-  /**
-    * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
-    * account that deploys the contract and customizes tokens decimals
-    *
-    * See {ERC20-constructor}.
-    */
-  constructor(string memory name, string memory symbol, uint8 decimals_)
-    ERC20(name, symbol) {
-      _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
-
-      _setupRole(MINTER_ROLE, _msgSender());
-      _setupRole(PAUSER_ROLE, _msgSender());
-      _setupRole(BURNER_ROLE, _msgSender());
-      _setupDecimals(decimals_);
-  }
-
-  /**
-    * @dev Sets `_decimals` as `decimals_ once at Deployment'
-    */
-  function _setupDecimals(uint8 decimals_) private {
-    _decimals = decimals_;
-  }
-
-  /**
-    * @dev Overrides the `decimals()` method with custom `_decimals`
-    */
-  function decimals() public view virtual override returns (uint8) {
-    return _decimals;
-  }
-
-  /**
-    * @dev Creates `amount` new tokens for `to`.
-    *
-    * See {ERC20-_mint}.
-    *
-    * Requirements:
-    *
-    * - the caller must have the `MINTER_ROLE`.
-    */
-  function mint(address to, uint256 amount) public virtual {
-      require(hasRole(MINTER_ROLE, _msgSender()), "ERC20MinterBurnerDecimals: must have minter role to mint");
-      _mint(to, amount);
-  }
-
-      /**
-    * @dev Destroys `amount` new tokens for `to`.
-    *
-    * See {ERC20-_burn}.
-    *
-    * Requirements:
-    *
-    * - the caller must have the `BURNER_ROLE`.
-    */
-  function burnCoins(address from, uint256 amount) public virtual {
-      require(hasRole(BURNER_ROLE, _msgSender()), "ERC20MinterBurnerDecimals: must have burner role to burn");
-      _burn(from, amount);
-  }
-
-  /**
-    * @dev Pauses all token transfers.
-    *
-    * See {ERC20Pausable} and {Pausable-_pause}.
-    *
-    * Requirements:
-    *
-    * - the caller must have the `PAUSER_ROLE`.
-    */
-  function pause() public virtual {
-      require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20MinterBurnerDecimals: must have pauser role to pause");
-      _pause();
-  }
-
-  /**
-    * @dev Unpauses all token transfers.
-    *
-    * See {ERC20Pausable} and {Pausable-_unpause}.
-    *
-    * Requirements:
-    *
-    * - the caller must have the `PAUSER_ROLE`.
-    */
-  function unpause() public virtual {
-      require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20MinterBurnerDecimals: must have pauser role to unpause");
-      _unpause();
-  }
-
-  function _beforeTokenTransfer(
-      address from,
-      address to,
-      uint256 amount
-  ) internal virtual override(ERC20, ERC20Pausable) {
-      super._beforeTokenTransfer(from, to, amount);
-  }
-}
\ No newline at end of file
diff --git a/contracts/Port.sol b/contracts/Port.sol
deleted file mode 100644
index 2c2a0562..00000000
--- a/contracts/Port.sol
+++ /dev/null
@@ -1,83 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-pragma solidity ^0.8.0;
-
-contract ProposalStore {
-    struct Proposal {
-        // @notice Unique id for looking up a proposal
-        uint256 id;
-        string title;
-        string desc;
-        // @notice the ordered list of target addresses for calls to be made
-        address[] targets;
-        uint256[] values;
-        // @notice The ordered list of function signatures to be called
-        string[] signatures;
-        // @notice The ordered list of calldata to be passed to each call
-        bytes[] calldatas;
-    }
-
-    address immutable govshuttleModAcct;
-
-    mapping(uint256 => Proposal) private proposals;
-
-    constructor(
-        uint256 propId,
-        string memory title,
-        string memory desc,
-        address[] memory targets,
-        uint256[] memory values,
-        string[] memory signatures,
-        bytes[] memory calldatas
-    ) {
-        govshuttleModAcct = msg.sender;
-        Proposal memory prop = Proposal(
-            propId,
-            title,
-            desc,
-            targets,
-            values,
-            signatures,
-            calldatas
-        );
-        proposals[propId] = prop;
-    }
-
-    function AddProposal(
-        uint256 propId,
-        string memory title,
-        string memory desc,
-        address[] memory targets,
-        uint256[] memory values,
-        string[] memory signatures,
-        bytes[] memory calldatas
-    ) public {
-        require(msg.sender == govshuttleModAcct); // only govshuttle account can add proposals to store
-        Proposal memory newProp = Proposal(
-            propId,
-            title,
-            desc,
-            targets,
-            values,
-            signatures,
-            calldatas
-        );
-        proposals[propId] = newProp;
-    }
-
-    function QueryProp(uint256 propId) public view returns (Proposal memory) {
-        if (proposals[propId].id == propId) {
-            return proposals[propId];
-        }
-        return
-            Proposal(
-                0,
-                "",
-                "",
-                new address[](0),
-                new uint256[](0),
-                new string[](0),
-                new bytes[](0)
-            );
-    }
-}
diff --git a/contracts/ProposalStore.go b/contracts/ProposalStore.go
deleted file mode 100644
index 97e241e5..00000000
--- a/contracts/ProposalStore.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package contracts
-
-import (
-	_ "embed" // embed compiled smart contract
-	"encoding/json"
-
-	evmtypes "github.com/evmos/ethermint/x/evm/types"
-)
-
-var (
-	//go:embed compiled_contracts/ProposalStore.json
-	ProposalStoreJSON []byte
-
-	// ERC20BurnableContract is the compiled ERC20Burnable contract
-	ProposalStoreContract evmtypes.CompiledContract
-)
-
-func init() {
-	err := json.Unmarshal(ProposalStoreJSON, &ProposalStoreContract)
-	if err != nil {
-		panic(err)
-	}
-}
diff --git a/contracts/callee.go b/contracts/callee.go
deleted file mode 100644
index 426961a5..00000000
--- a/contracts/callee.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package contracts
-
-import (
-	_ "embed" // embed compiled smart contract
-
-	evmtypes "github.com/evmos/ethermint/x/evm/types"
-)
-
-var (
-	//go:embed compiled_contracts/callee.json
-	calleeJSON []byte
-
-	// ERC20BurnableContract is the compiled ERC20Burnable contract
-	CalleeContract evmtypes.CompiledContract
-)
-
-func init() {
-	// err := json.Unmarshal(calleeJSON, &CalleeContract)
-	// if err != nil {
-	// 	// panic(err)
-	// 	fmt.Println("ERROR HERE")
-	// }
-}
diff --git a/contracts/callee.sol b/contracts/callee.sol
deleted file mode 100644
index ac9574c2..00000000
--- a/contracts/callee.sol
+++ /dev/null
@@ -1,11 +0,0 @@
-pragma solidity ^0.8.10;
-
-//contract that is called from the caller
-
-contract callee {
-    uint public Int; //public variable initially set to 0
-
-    function setInt(uint val) external {
-        Int = val;
-    }
-}
\ No newline at end of file
diff --git a/contracts/caller.go b/contracts/caller.go
deleted file mode 100644
index 0bacf559..00000000
--- a/contracts/caller.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package contracts
-
-import (
-	_ "embed" // embed compiled smart contract
-
-	evmtypes "github.com/evmos/ethermint/x/evm/types"
-)
-
-var (
-	//go:embed compiled_contracts/caller.json
-	callerJSON []byte
-
-	// ERC20BurnableContract is the compiled ERC20Burnable contract
-	CallerContract evmtypes.CompiledContract
-)
-
-func init() {
-	// err := json.Unmarshal(callerJSON, &CallerContract)
-	// if err != nil {
-	// 	panic(err)
-	// }
-}
diff --git a/contracts/caller.sol b/contracts/caller.sol
deleted file mode 100644
index eccdecb1..00000000
--- a/contracts/caller.sol
+++ /dev/null
@@ -1,27 +0,0 @@
-pragma solidity ^0.8.10;
-
-import "./Port.sol";
-
-// caller contract, passed a reference of the Proposal-Store contract
-contract caller {
-    address public govshuttleContract;
-
-    function setPropContract(address propContract) external {
-        require(govshuttleContract == address(0));
-        govshuttleContract = propContract;
-    }
-
-    function queryProp(uint256 id) external {
-        ProposalStore propStore = ProposalStore(govshuttleContract);
-        ProposalStore.Proposal memory x = propStore.QueryProp(id);
-        //Query this proposal and ping the callee contract
-        bytes memory calldatas = abi.encodePacked(
-            bytes4(keccak256(bytes(x.signatures[0]))),
-            x.calldatas[0]
-        );
-        (bool success, bytes memory data) = x.targets[0].call{
-            value: x.values[0]
-        }(calldatas);
-        //in this case we are pinging the setInt of the callee ...
-    }
-}
diff --git a/contracts/compiled_contracts/ERC20Burnable.json b/contracts/compiled_contracts/ERC20Burnable.json
deleted file mode 100644
index 65f033fd..00000000
--- a/contracts/compiled_contracts/ERC20Burnable.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "abi": "[{\"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\":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\"},{\"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\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"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\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"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\":\"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\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
-  "bin": "",
-  "contractName": "ERC20Burnable"
-}
diff --git a/contracts/compiled_contracts/ERC20DirectBalanceManipulation.json b/contracts/compiled_contracts/ERC20DirectBalanceManipulation.json
deleted file mode 100644
index 06657b43..00000000
--- a/contracts/compiled_contracts/ERC20DirectBalanceManipulation.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "abi": "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"initialSupply\",\"type\":\"uint256\"}],\"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\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"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\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"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\":[],\"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\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"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\"}]",
-  "bin": "6080604052734dc6ac40af078661fc43823086e1513635eeab14600760016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503480156200006657600080fd5b5060405162003e4038038062003e4083398181016040528101906200008c9190620007cd565b6040518060400160405280601e81526020017f455243323044697265637442616c616e63654d616e6970756c6174696f6e00008152506040518060400160405280601e81526020017f455243323044697265637442616c616e63654d616e6970756c6174696f6e00008152508181816005908051906020019062000112929190620006dd565b5080600690805190602001906200012b929190620006dd565b5050506000600760006101000a81548160ff0219169083151502179055506200016d6000801b620001616200021f60201b60201c565b6200022760201b60201c565b620001ae7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6620001a26200021f60201b60201c565b6200022760201b60201c565b620001ef7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a620001e36200021f60201b60201c565b6200022760201b60201c565b5050620002066000801b336200022760201b60201c565b6200021833826200023d60201b60201c565b5062000a39565b600033905090565b620002398282620003b760201b60201c565b5050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415620002b0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620002a79062000860565b60405180910390fd5b620002c460008383620003ff60201b60201c565b8060046000828254620002d89190620008b1565b9250508190555080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254620003309190620008b1565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516200039791906200091f565b60405180910390a3620003b3600083836200041c60201b60201c565b5050565b620003ce82826200042160201b62000f581760201c565b620003fa81600160008581526020019081526020016000206200051260201b620010381790919060201c565b505050565b620004178383836200054a60201b620010681760201c565b505050565b505050565b620004338282620005ba60201b60201c565b6200050e57600160008084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550620004b36200021f60201b60201c565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b600062000542836000018373ffffffffffffffffffffffffffffffffffffffff1660001b6200062460201b60201c565b905092915050565b620005628383836200069e60201b620010c01760201c565b62000572620006a360201b60201c565b15620005b5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620005ac90620009b2565b60405180910390fd5b505050565b600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b6000620006388383620006ba60201b60201c565b6200069357826000018290806001815401808255809150506001900390600052602060002001600090919091909150558260000180549050836001016000848152602001908152602001600020819055506001905062000698565b600090505b92915050565b505050565b6000600760009054906101000a900460ff16905090565b600080836001016000848152602001908152602001600020541415905092915050565b828054620006eb9062000a03565b90600052602060002090601f0160209004810192826200070f57600085556200075b565b82601f106200072a57805160ff19168380011785556200075b565b828001600101855582156200075b579182015b828111156200075a5782518255916020019190600101906200073d565b5b5090506200076a91906200076e565b5090565b5b80821115620007895760008160009055506001016200076f565b5090565b600080fd5b6000819050919050565b620007a78162000792565b8114620007b357600080fd5b50565b600081519050620007c7816200079c565b92915050565b600060208284031215620007e657620007e56200078d565b5b6000620007f684828501620007b6565b91505092915050565b600082825260208201905092915050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b600062000848601f83620007ff565b9150620008558262000810565b602082019050919050565b600060208201905081810360008301526200087b8162000839565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000620008be8262000792565b9150620008cb8362000792565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111562000903576200090262000882565b5b828201905092915050565b620009198162000792565b82525050565b60006020820190506200093660008301846200090e565b92915050565b7f45524332305061757361626c653a20746f6b656e207472616e7366657220776860008201527f696c652070617573656400000000000000000000000000000000000000000000602082015250565b60006200099a602a83620007ff565b9150620009a7826200093c565b604082019050919050565b60006020820190508181036000830152620009cd816200098b565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168062000a1c57607f821691505b6020821081141562000a335762000a32620009d4565b5b50919050565b6133f78062000a496000396000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c806370a08231116100f9578063a457c2d711610097578063d539139311610071578063d53913931461052d578063d547741f1461054b578063dd62ed3e14610567578063e63ab1e914610597576101c4565b8063a457c2d71461049d578063a9059cbb146104cd578063ca15c873146104fd576101c4565b80639010d07c116100d35780639010d07c1461040157806391d148541461043157806395d89b4114610461578063a217fddf1461047f576101c4565b806370a08231146103ab57806379cc6790146103db5780638456cb59146103f7576101c4565b8063313ce567116101665780633f4ba83a116101405780633f4ba83a1461034b57806340c10f191461035557806342966c68146103715780635c975abb1461038d576101c4565b8063313ce567146102e157806336568abe146102ff578063395093511461031b576101c4565b806318160ddd116101a257806318160ddd1461024757806323b872dd14610265578063248a9ca3146102955780632f2ff15d146102c5576101c4565b806301ffc9a7146101c957806306fdde03146101f9578063095ea7b314610217575b600080fd5b6101e360048036038101906101de9190612173565b6105b5565b6040516101f091906121bb565b60405180910390f35b61020161062f565b60405161020e919061226f565b60405180910390f35b610231600480360381019061022c9190612325565b6106c1565b60405161023e91906121bb565b60405180910390f35b61024f6106df565b60405161025c9190612374565b60405180910390f35b61027f600480360381019061027a919061238f565b6106e9565b60405161028c91906121bb565b60405180910390f35b6102af60048036038101906102aa9190612418565b6107e1565b6040516102bc9190612454565b60405180910390f35b6102df60048036038101906102da919061246f565b610800565b005b6102e9610829565b6040516102f691906124cb565b60405180910390f35b6103196004803603810190610314919061246f565b610832565b005b61033560048036038101906103309190612325565b6108b5565b60405161034291906121bb565b60405180910390f35b610353610961565b005b61036f600480360381019061036a9190612325565b6109db565b005b61038b600480360381019061038691906124e6565b610a59565b005b610395610a6d565b6040516103a291906121bb565b60405180910390f35b6103c560048036038101906103c09190612513565b610a84565b6040516103d29190612374565b60405180910390f35b6103f560048036038101906103f09190612325565b610acd565b005b6103ff610b48565b005b61041b60048036038101906104169190612540565b610bc2565b604051610428919061258f565b60405180910390f35b61044b6004803603810190610446919061246f565b610bf1565b60405161045891906121bb565b60405180910390f35b610469610c5b565b604051610476919061226f565b60405180910390f35b610487610ced565b6040516104949190612454565b60405180910390f35b6104b760048036038101906104b29190612325565b610cf4565b6040516104c491906121bb565b60405180910390f35b6104e760048036038101906104e29190612325565b610ddf565b6040516104f491906121bb565b60405180910390f35b61051760048036038101906105129190612418565b610e3c565b6040516105249190612374565b60405180910390f35b610535610e60565b6040516105429190612454565b60405180910390f35b6105656004803603810190610560919061246f565b610e84565b005b610581600480360381019061057c91906125aa565b610ead565b60405161058e9190612374565b60405180910390f35b61059f610f34565b6040516105ac9190612454565b60405180910390f35b60007f5a05180f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806106285750610627826110c5565b5b9050919050565b60606005805461063e90612619565b80601f016020809104026020016040519081016040528092919081815260200182805461066a90612619565b80156106b75780601f1061068c576101008083540402835291602001916106b7565b820191906000526020600020905b81548152906001019060200180831161069a57829003601f168201915b5050505050905090565b60006106d56106ce61113f565b8484611147565b6001905092915050565b6000600454905090565b60006106f6848484611312565b6000600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600061074161113f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050828110156107c1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b8906126bd565b60405180910390fd5b6107d5856107cd61113f565b858403611147565b60019150509392505050565b6000806000838152602001908152602001600020600101549050919050565b610809826107e1565b61081a8161081561113f565b611596565b6108248383611633565b505050565b60006012905090565b61083a61113f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146108a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161089e9061274f565b60405180910390fd5b6108b18282611667565b5050565b60006109576108c261113f565b8484600360006108d061113f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610952919061279e565b611147565b6001905092915050565b6109927f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a61098d61113f565b610bf1565b6109d1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109c890612866565b60405180910390fd5b6109d961169b565b565b610a0c7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6610a0761113f565b610bf1565b610a4b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a42906128f8565b60405180910390fd5b610a55828261173d565b5050565b610a6a610a6461113f565b8261189e565b50565b6000600760009054906101000a900460ff16905090565b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6000610ae083610adb61113f565b610ead565b905081811015610b25576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b1c9061298a565b60405180910390fd5b610b3983610b3161113f565b848403611147565b610b43838361189e565b505050565b610b797f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610b7461113f565b610bf1565b610bb8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610baf90612a1c565b60405180910390fd5b610bc0611a77565b565b6000610be98260016000868152602001908152602001600020611b1a90919063ffffffff16565b905092915050565b600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b606060068054610c6a90612619565b80601f0160208091040260200160405190810160405280929190818152602001828054610c9690612619565b8015610ce35780601f10610cb857610100808354040283529160200191610ce3565b820191906000526020600020905b815481529060010190602001808311610cc657829003601f168201915b5050505050905090565b6000801b81565b60008060036000610d0361113f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082811015610dc0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610db790612aae565b60405180910390fd5b610dd4610dcb61113f565b85858403611147565b600191505092915050565b600080600283610def9190612afd565b9050610e28600760019054906101000a900473ffffffffffffffffffffffffffffffffffffffff168285610e239190612b2e565b611b34565b50610e338482611b34565b91505092915050565b6000610e5960016000848152602001908152602001600020611b52565b9050919050565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b610e8d826107e1565b610e9e81610e9961113f565b611596565b610ea88383611667565b505050565b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b610f628282610bf1565b61103457600160008084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550610fd961113f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b6000611060836000018373ffffffffffffffffffffffffffffffffffffffff1660001b611b67565b905092915050565b6110738383836110c0565b61107b610a6d565b156110bb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110b290612bd4565b60405180910390fd5b505050565b505050565b60007f7965db0b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480611138575061113782611bd7565b5b9050919050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156111b7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ae90612c66565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611227576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161121e90612cf8565b60405180910390fd5b80600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516113059190612374565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611382576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161137990612d8a565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156113f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113e990612e1c565b60405180910390fd5b6113fd838383611c41565b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015611484576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161147b90612eae565b60405180910390fd5b818103600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611519919061279e565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161157d9190612374565b60405180910390a3611590848484611c51565b50505050565b6115a08282610bf1565b61162f576115c58173ffffffffffffffffffffffffffffffffffffffff166014611c56565b6115d38360001c6020611c56565b6040516020016115e4929190612fa2565b6040516020818303038152906040526040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611626919061226f565b60405180910390fd5b5050565b61163d8282610f58565b611662816001600085815260200190815260200160002061103890919063ffffffff16565b505050565b6116718282611e92565b6116968160016000858152602001908152602001600020611f7390919063ffffffff16565b505050565b6116a3610a6d565b6116e2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116d990613028565b60405180910390fd5b6000600760006101000a81548160ff0219169083151502179055507f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa61172661113f565b604051611733919061258f565b60405180910390a1565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156117ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117a490613094565b60405180910390fd5b6117b960008383611c41565b80600460008282546117cb919061279e565b9250508190555080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611821919061279e565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516118869190612374565b60405180910390a361189a60008383611c51565b5050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561190e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161190590613126565b60405180910390fd5b61191a82600083611c41565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050818110156119a1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611998906131b8565b60405180910390fd5b818103600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600460008282546119f99190612b2e565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051611a5e9190612374565b60405180910390a3611a7283600084611c51565b505050565b611a7f610a6d565b15611abf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611ab690613224565b60405180910390fd5b6001600760006101000a81548160ff0219169083151502179055507f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258611b0361113f565b604051611b10919061258f565b60405180910390a1565b6000611b298360000183611fa3565b60001c905092915050565b6000611b48611b4161113f565b8484611312565b6001905092915050565b6000611b6082600001611fce565b9050919050565b6000611b738383611fdf565b611bcc578260000182908060018154018082558091505060019003906000526020600020016000909190919091505582600001805490508360010160008481526020019081526020016000208190555060019050611bd1565b600090505b92915050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b611c4c838383611068565b505050565b505050565b606060006002836002611c699190613244565b611c73919061279e565b67ffffffffffffffff811115611c8c57611c8b61329e565b5b6040519080825280601f01601f191660200182016040528015611cbe5781602001600182028036833780820191505090505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110611cf657611cf56132cd565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110611d5a57611d596132cd565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006001846002611d9a9190613244565b611da4919061279e565b90505b6001811115611e44577f3031323334353637383961626364656600000000000000000000000000000000600f861660108110611de657611de56132cd565b5b1a60f81b828281518110611dfd57611dfc6132cd565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600485901c945080611e3d906132fc565b9050611da7565b5060008414611e88576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e7f90613372565b60405180910390fd5b8091505092915050565b611e9c8282610bf1565b15611f6f57600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550611f1461113f565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45b5050565b6000611f9b836000018373ffffffffffffffffffffffffffffffffffffffff1660001b612002565b905092915050565b6000826000018281548110611fbb57611fba6132cd565b5b9060005260206000200154905092915050565b600081600001805490509050919050565b600080836001016000848152602001908152602001600020541415905092915050565b6000808360010160008481526020019081526020016000205490506000811461210a5760006001826120349190612b2e565b905060006001866000018054905061204c9190612b2e565b90508181146120bb57600086600001828154811061206d5761206c6132cd565b5b9060005260206000200154905080876000018481548110612091576120906132cd565b5b90600052602060002001819055508387600101600083815260200190815260200160002081905550505b856000018054806120cf576120ce613392565b5b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050612110565b60009150505b92915050565b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6121508161211b565b811461215b57600080fd5b50565b60008135905061216d81612147565b92915050565b60006020828403121561218957612188612116565b5b60006121978482850161215e565b91505092915050565b60008115159050919050565b6121b5816121a0565b82525050565b60006020820190506121d060008301846121ac565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156122105780820151818401526020810190506121f5565b8381111561221f576000848401525b50505050565b6000601f19601f8301169050919050565b6000612241826121d6565b61224b81856121e1565b935061225b8185602086016121f2565b61226481612225565b840191505092915050565b600060208201905081810360008301526122898184612236565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006122bc82612291565b9050919050565b6122cc816122b1565b81146122d757600080fd5b50565b6000813590506122e9816122c3565b92915050565b6000819050919050565b612302816122ef565b811461230d57600080fd5b50565b60008135905061231f816122f9565b92915050565b6000806040838503121561233c5761233b612116565b5b600061234a858286016122da565b925050602061235b85828601612310565b9150509250929050565b61236e816122ef565b82525050565b60006020820190506123896000830184612365565b92915050565b6000806000606084860312156123a8576123a7612116565b5b60006123b6868287016122da565b93505060206123c7868287016122da565b92505060406123d886828701612310565b9150509250925092565b6000819050919050565b6123f5816123e2565b811461240057600080fd5b50565b600081359050612412816123ec565b92915050565b60006020828403121561242e5761242d612116565b5b600061243c84828501612403565b91505092915050565b61244e816123e2565b82525050565b60006020820190506124696000830184612445565b92915050565b6000806040838503121561248657612485612116565b5b600061249485828601612403565b92505060206124a5858286016122da565b9150509250929050565b600060ff82169050919050565b6124c5816124af565b82525050565b60006020820190506124e060008301846124bc565b92915050565b6000602082840312156124fc576124fb612116565b5b600061250a84828501612310565b91505092915050565b60006020828403121561252957612528612116565b5b6000612537848285016122da565b91505092915050565b6000806040838503121561255757612556612116565b5b600061256585828601612403565b925050602061257685828601612310565b9150509250929050565b612589816122b1565b82525050565b60006020820190506125a46000830184612580565b92915050565b600080604083850312156125c1576125c0612116565b5b60006125cf858286016122da565b92505060206125e0858286016122da565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061263157607f821691505b60208210811415612645576126446125ea565b5b50919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206160008201527f6c6c6f77616e6365000000000000000000000000000000000000000000000000602082015250565b60006126a76028836121e1565b91506126b28261264b565b604082019050919050565b600060208201905081810360008301526126d68161269a565b9050919050565b7f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560008201527f20726f6c657320666f722073656c660000000000000000000000000000000000602082015250565b6000612739602f836121e1565b9150612744826126dd565b604082019050919050565b600060208201905081810360008301526127688161272c565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006127a9826122ef565b91506127b4836122ef565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156127e9576127e861276f565b5b828201905092915050565b7f45524332305072657365744d696e7465725061757365723a206d75737420686160008201527f76652070617573657220726f6c6520746f20756e706175736500000000000000602082015250565b60006128506039836121e1565b915061285b826127f4565b604082019050919050565b6000602082019050818103600083015261287f81612843565b9050919050565b7f45524332305072657365744d696e7465725061757365723a206d75737420686160008201527f7665206d696e74657220726f6c6520746f206d696e7400000000000000000000602082015250565b60006128e26036836121e1565b91506128ed82612886565b604082019050919050565b60006020820190508181036000830152612911816128d5565b9050919050565b7f45524332303a206275726e20616d6f756e74206578636565647320616c6c6f7760008201527f616e636500000000000000000000000000000000000000000000000000000000602082015250565b60006129746024836121e1565b915061297f82612918565b604082019050919050565b600060208201905081810360008301526129a381612967565b9050919050565b7f45524332305072657365744d696e7465725061757365723a206d75737420686160008201527f76652070617573657220726f6c6520746f207061757365000000000000000000602082015250565b6000612a066037836121e1565b9150612a11826129aa565b604082019050919050565b60006020820190508181036000830152612a35816129f9565b9050919050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000612a986025836121e1565b9150612aa382612a3c565b604082019050919050565b60006020820190508181036000830152612ac781612a8b565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000612b08826122ef565b9150612b13836122ef565b925082612b2357612b22612ace565b5b828204905092915050565b6000612b39826122ef565b9150612b44836122ef565b925082821015612b5757612b5661276f565b5b828203905092915050565b7f45524332305061757361626c653a20746f6b656e207472616e7366657220776860008201527f696c652070617573656400000000000000000000000000000000000000000000602082015250565b6000612bbe602a836121e1565b9150612bc982612b62565b604082019050919050565b60006020820190508181036000830152612bed81612bb1565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000612c506024836121e1565b9150612c5b82612bf4565b604082019050919050565b60006020820190508181036000830152612c7f81612c43565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000612ce26022836121e1565b9150612ced82612c86565b604082019050919050565b60006020820190508181036000830152612d1181612cd5565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b6000612d746025836121e1565b9150612d7f82612d18565b604082019050919050565b60006020820190508181036000830152612da381612d67565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000612e066023836121e1565b9150612e1182612daa565b604082019050919050565b60006020820190508181036000830152612e3581612df9565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b6000612e986026836121e1565b9150612ea382612e3c565b604082019050919050565b60006020820190508181036000830152612ec781612e8b565b9050919050565b600081905092915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000600082015250565b6000612f0f601783612ece565b9150612f1a82612ed9565b601782019050919050565b6000612f30826121d6565b612f3a8185612ece565b9350612f4a8185602086016121f2565b80840191505092915050565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000600082015250565b6000612f8c601183612ece565b9150612f9782612f56565b601182019050919050565b6000612fad82612f02565b9150612fb98285612f25565b9150612fc482612f7f565b9150612fd08284612f25565b91508190509392505050565b7f5061757361626c653a206e6f7420706175736564000000000000000000000000600082015250565b60006130126014836121e1565b915061301d82612fdc565b602082019050919050565b6000602082019050818103600083015261304181613005565b9050919050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b600061307e601f836121e1565b915061308982613048565b602082019050919050565b600060208201905081810360008301526130ad81613071565b9050919050565b7f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360008201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b60006131106021836121e1565b915061311b826130b4565b604082019050919050565b6000602082019050818103600083015261313f81613103565b9050919050565b7f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60008201527f6365000000000000000000000000000000000000000000000000000000000000602082015250565b60006131a26022836121e1565b91506131ad82613146565b604082019050919050565b600060208201905081810360008301526131d181613195565b9050919050565b7f5061757361626c653a2070617573656400000000000000000000000000000000600082015250565b600061320e6010836121e1565b9150613219826131d8565b602082019050919050565b6000602082019050818103600083015261323d81613201565b9050919050565b600061324f826122ef565b915061325a836122ef565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156132935761329261276f565b5b828202905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000613307826122ef565b9150600082141561331b5761331a61276f565b5b600182039050919050565b7f537472696e67733a20686578206c656e67746820696e73756666696369656e74600082015250565b600061335c6020836121e1565b915061336782613326565b602082019050919050565b6000602082019050818103600083015261338b8161334f565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea26469706673582212202d12e01c719900c4212b57e8299d1f85099bba3e312f68981cb985a87e786ccf64736f6c63430008090033",
-  "contractName": "ERC20DirectBalanceManipulation"
-}
diff --git a/contracts/compiled_contracts/ERC20MaliciousDelayed.json b/contracts/compiled_contracts/ERC20MaliciousDelayed.json
deleted file mode 100644
index a4407e65..00000000
--- a/contracts/compiled_contracts/ERC20MaliciousDelayed.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "abi": "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"initialSupply\",\"type\":\"uint256\"}],\"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\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"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\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"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\":[],\"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\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"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\"}]",
-  "bin": "6080604052734dc6ac40af078661fc43823086e1513635eeab14600760016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550670de0b6b3a76400006008553480156200007257600080fd5b5060405162003dd238038062003dd28339818101604052810190620000989190620007d9565b6040518060400160405280601581526020017f45524332304d616c6963696f757344656c6179656400000000000000000000008152506040518060400160405280601581526020017f45524332304d414c4943494f555344454c415945440000000000000000000000815250818181600590805190602001906200011e929190620006e9565b50806006908051906020019062000137929190620006e9565b5050506000600760006101000a81548160ff021916908315150217905550620001796000801b6200016d6200022b60201b60201c565b6200023360201b60201c565b620001ba7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6620001ae6200022b60201b60201c565b6200023360201b60201c565b620001fb7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a620001ef6200022b60201b60201c565b6200023360201b60201c565b5050620002126000801b336200023360201b60201c565b6200022433826200024960201b60201c565b5062000a45565b600033905090565b620002458282620003c360201b60201c565b5050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415620002bc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620002b3906200086c565b60405180910390fd5b620002d0600083836200040b60201b60201c565b8060046000828254620002e49190620008bd565b9250508190555080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546200033c9190620008bd565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051620003a391906200092b565b60405180910390a3620003bf600083836200042860201b60201c565b5050565b620003da82826200042d60201b62000f3e1760201c565b6200040681600160008581526020019081526020016000206200051e60201b6200101e1790919060201c565b505050565b620004238383836200055660201b6200104e1760201c565b505050565b505050565b6200043f8282620005c660201b60201c565b6200051a57600160008084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550620004bf6200022b60201b60201c565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b60006200054e836000018373ffffffffffffffffffffffffffffffffffffffff1660001b6200063060201b60201c565b905092915050565b6200056e838383620006aa60201b620010a61760201c565b6200057e620006af60201b60201c565b15620005c1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620005b890620009be565b60405180910390fd5b505050565b600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b6000620006448383620006c660201b60201c565b6200069f578260000182908060018154018082558091505060019003906000526020600020016000909190919091505582600001805490508360010160008481526020019081526020016000208190555060019050620006a4565b600090505b92915050565b505050565b6000600760009054906101000a900460ff16905090565b600080836001016000848152602001908152602001600020541415905092915050565b828054620006f79062000a0f565b90600052602060002090601f0160209004810192826200071b576000855562000767565b82601f106200073657805160ff191683800117855562000767565b8280016001018555821562000767579182015b828111156200076657825182559160200191906001019062000749565b5b5090506200077691906200077a565b5090565b5b80821115620007955760008160009055506001016200077b565b5090565b600080fd5b6000819050919050565b620007b3816200079e565b8114620007bf57600080fd5b50565b600081519050620007d381620007a8565b92915050565b600060208284031215620007f257620007f162000799565b5b60006200080284828501620007c2565b91505092915050565b600082825260208201905092915050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b600062000854601f836200080b565b915062000861826200081c565b602082019050919050565b60006020820190508181036000830152620008878162000845565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000620008ca826200079e565b9150620008d7836200079e565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156200090f576200090e6200088e565b5b828201905092915050565b62000925816200079e565b82525050565b60006020820190506200094260008301846200091a565b92915050565b7f45524332305061757361626c653a20746f6b656e207472616e7366657220776860008201527f696c652070617573656400000000000000000000000000000000000000000000602082015250565b6000620009a6602a836200080b565b9150620009b38262000948565b604082019050919050565b60006020820190508181036000830152620009d98162000997565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168062000a2857607f821691505b6020821081141562000a3f5762000a3e620009e0565b5b50919050565b61337d8062000a556000396000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c806370a08231116100f9578063a457c2d711610097578063d539139311610071578063d53913931461052d578063d547741f1461054b578063dd62ed3e14610567578063e63ab1e914610597576101c4565b8063a457c2d71461049d578063a9059cbb146104cd578063ca15c873146104fd576101c4565b80639010d07c116100d35780639010d07c1461040157806391d148541461043157806395d89b4114610461578063a217fddf1461047f576101c4565b806370a08231146103ab57806379cc6790146103db5780638456cb59146103f7576101c4565b8063313ce567116101665780633f4ba83a116101405780633f4ba83a1461034b57806340c10f191461035557806342966c68146103715780635c975abb1461038d576101c4565b8063313ce567146102e157806336568abe146102ff578063395093511461031b576101c4565b806318160ddd116101a257806318160ddd1461024757806323b872dd14610265578063248a9ca3146102955780632f2ff15d146102c5576101c4565b806301ffc9a7146101c957806306fdde03146101f9578063095ea7b314610217575b600080fd5b6101e360048036038101906101de9190612159565b6105b5565b6040516101f091906121a1565b60405180910390f35b61020161062f565b60405161020e9190612255565b60405180910390f35b610231600480360381019061022c919061230b565b6106c1565b60405161023e91906121a1565b60405180910390f35b61024f6106df565b60405161025c919061235a565b60405180910390f35b61027f600480360381019061027a9190612375565b6106e9565b60405161028c91906121a1565b60405180910390f35b6102af60048036038101906102aa91906123fe565b6107e1565b6040516102bc919061243a565b60405180910390f35b6102df60048036038101906102da9190612455565b610800565b005b6102e9610829565b6040516102f691906124b1565b60405180910390f35b61031960048036038101906103149190612455565b610832565b005b6103356004803603810190610330919061230b565b6108b5565b60405161034291906121a1565b60405180910390f35b610353610961565b005b61036f600480360381019061036a919061230b565b6109db565b005b61038b600480360381019061038691906124cc565b610a59565b005b610395610a6d565b6040516103a291906121a1565b60405180910390f35b6103c560048036038101906103c091906124f9565b610a84565b6040516103d2919061235a565b60405180910390f35b6103f560048036038101906103f0919061230b565b610acd565b005b6103ff610b48565b005b61041b60048036038101906104169190612526565b610bc2565b6040516104289190612575565b60405180910390f35b61044b60048036038101906104469190612455565b610bf1565b60405161045891906121a1565b60405180910390f35b610469610c5b565b6040516104769190612255565b60405180910390f35b610487610ced565b604051610494919061243a565b60405180910390f35b6104b760048036038101906104b2919061230b565b610cf4565b6040516104c491906121a1565b60405180910390f35b6104e760048036038101906104e2919061230b565b610ddf565b6040516104f491906121a1565b60405180910390f35b610517600480360381019061051291906123fe565b610e22565b604051610524919061235a565b60405180910390f35b610535610e46565b604051610542919061243a565b60405180910390f35b61056560048036038101906105609190612455565b610e6a565b005b610581600480360381019061057c9190612590565b610e93565b60405161058e919061235a565b60405180910390f35b61059f610f1a565b6040516105ac919061243a565b60405180910390f35b60007f5a05180f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806106285750610627826110ab565b5b9050919050565b60606005805461063e906125ff565b80601f016020809104026020016040519081016040528092919081815260200182805461066a906125ff565b80156106b75780601f1061068c576101008083540402835291602001916106b7565b820191906000526020600020905b81548152906001019060200180831161069a57829003601f168201915b5050505050905090565b60006106d56106ce611125565b848461112d565b6001905092915050565b6000600454905090565b60006106f68484846112f8565b6000600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610741611125565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050828110156107c1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b8906126a3565b60405180910390fd5b6107d5856107cd611125565b85840361112d565b60019150509392505050565b6000806000838152602001908152602001600020600101549050919050565b610809826107e1565b61081a81610815611125565b61157c565b6108248383611619565b505050565b60006012905090565b61083a611125565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146108a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161089e90612735565b60405180910390fd5b6108b1828261164d565b5050565b60006109576108c2611125565b8484600360006108d0611125565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546109529190612784565b61112d565b6001905092915050565b6109927f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a61098d611125565b610bf1565b6109d1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109c89061284c565b60405180910390fd5b6109d9611681565b565b610a0c7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6610a07611125565b610bf1565b610a4b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a42906128de565b60405180910390fd5b610a558282611723565b5050565b610a6a610a64611125565b82611884565b50565b6000600760009054906101000a900460ff16905090565b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6000610ae083610adb611125565b610e93565b905081811015610b25576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b1c90612970565b60405180910390fd5b610b3983610b31611125565b84840361112d565b610b438383611884565b505050565b610b797f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610b74611125565b610bf1565b610bb8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610baf90612a02565b60405180910390fd5b610bc0611a5d565b565b6000610be98260016000868152602001908152602001600020611b0090919063ffffffff16565b905092915050565b600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b606060068054610c6a906125ff565b80601f0160208091040260200160405190810160405280929190818152602001828054610c96906125ff565b8015610ce35780601f10610cb857610100808354040283529160200191610ce3565b820191906000526020600020905b815481529060010190602001808311610cc657829003601f168201915b5050505050905090565b6000801b81565b60008060036000610d03611125565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082811015610dc0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610db790612a94565b60405180910390fd5b610dd4610dcb611125565b8585840361112d565b600191505092915050565b6000610e1083600760019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660085461112d565b610e1a8383611b1a565b905092915050565b6000610e3f60016000848152602001908152602001600020611b38565b9050919050565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b610e73826107e1565b610e8481610e7f611125565b61157c565b610e8e838361164d565b505050565b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b610f488282610bf1565b61101a57600160008084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550610fbf611125565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b6000611046836000018373ffffffffffffffffffffffffffffffffffffffff1660001b611b4d565b905092915050565b6110598383836110a6565b611061610a6d565b156110a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161109890612b26565b60405180910390fd5b505050565b505050565b60007f7965db0b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061111e575061111d82611bbd565b5b9050919050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561119d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161119490612bb8565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561120d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161120490612c4a565b60405180910390fd5b80600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516112eb919061235a565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611368576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161135f90612cdc565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156113d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113cf90612d6e565b60405180910390fd5b6113e3838383611c27565b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561146a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161146190612e00565b60405180910390fd5b818103600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546114ff9190612784565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051611563919061235a565b60405180910390a3611576848484611c37565b50505050565b6115868282610bf1565b611615576115ab8173ffffffffffffffffffffffffffffffffffffffff166014611c3c565b6115b98360001c6020611c3c565b6040516020016115ca929190612ef4565b6040516020818303038152906040526040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161160c9190612255565b60405180910390fd5b5050565b6116238282610f3e565b611648816001600085815260200190815260200160002061101e90919063ffffffff16565b505050565b6116578282611e78565b61167c8160016000858152602001908152602001600020611f5990919063ffffffff16565b505050565b611689610a6d565b6116c8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116bf90612f7a565b60405180910390fd5b6000600760006101000a81548160ff0219169083151502179055507f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa61170c611125565b6040516117199190612575565b60405180910390a1565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611793576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161178a90612fe6565b60405180910390fd5b61179f60008383611c27565b80600460008282546117b19190612784565b9250508190555080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546118079190612784565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161186c919061235a565b60405180910390a361188060008383611c37565b5050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156118f4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118eb90613078565b60405180910390fd5b61190082600083611c27565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015611987576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161197e9061310a565b60405180910390fd5b818103600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600460008282546119df919061312a565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051611a44919061235a565b60405180910390a3611a5883600084611c37565b505050565b611a65610a6d565b15611aa5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a9c906131aa565b60405180910390fd5b6001600760006101000a81548160ff0219169083151502179055507f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258611ae9611125565b604051611af69190612575565b60405180910390a1565b6000611b0f8360000183611f89565b60001c905092915050565b6000611b2e611b27611125565b84846112f8565b6001905092915050565b6000611b4682600001611fb4565b9050919050565b6000611b598383611fc5565b611bb2578260000182908060018154018082558091505060019003906000526020600020016000909190919091505582600001805490508360010160008481526020019081526020016000208190555060019050611bb7565b600090505b92915050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b611c3283838361104e565b505050565b505050565b606060006002836002611c4f91906131ca565b611c599190612784565b67ffffffffffffffff811115611c7257611c71613224565b5b6040519080825280601f01601f191660200182016040528015611ca45781602001600182028036833780820191505090505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110611cdc57611cdb613253565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110611d4057611d3f613253565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006001846002611d8091906131ca565b611d8a9190612784565b90505b6001811115611e2a577f3031323334353637383961626364656600000000000000000000000000000000600f861660108110611dcc57611dcb613253565b5b1a60f81b828281518110611de357611de2613253565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600485901c945080611e2390613282565b9050611d8d565b5060008414611e6e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e65906132f8565b60405180910390fd5b8091505092915050565b611e828282610bf1565b15611f5557600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550611efa611125565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45b5050565b6000611f81836000018373ffffffffffffffffffffffffffffffffffffffff1660001b611fe8565b905092915050565b6000826000018281548110611fa157611fa0613253565b5b9060005260206000200154905092915050565b600081600001805490509050919050565b600080836001016000848152602001908152602001600020541415905092915050565b600080836001016000848152602001908152602001600020549050600081146120f057600060018261201a919061312a565b9050600060018660000180549050612032919061312a565b90508181146120a157600086600001828154811061205357612052613253565b5b906000526020600020015490508087600001848154811061207757612076613253565b5b90600052602060002001819055508387600101600083815260200190815260200160002081905550505b856000018054806120b5576120b4613318565b5b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506120f6565b60009150505b92915050565b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61213681612101565b811461214157600080fd5b50565b6000813590506121538161212d565b92915050565b60006020828403121561216f5761216e6120fc565b5b600061217d84828501612144565b91505092915050565b60008115159050919050565b61219b81612186565b82525050565b60006020820190506121b66000830184612192565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156121f65780820151818401526020810190506121db565b83811115612205576000848401525b50505050565b6000601f19601f8301169050919050565b6000612227826121bc565b61223181856121c7565b93506122418185602086016121d8565b61224a8161220b565b840191505092915050565b6000602082019050818103600083015261226f818461221c565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006122a282612277565b9050919050565b6122b281612297565b81146122bd57600080fd5b50565b6000813590506122cf816122a9565b92915050565b6000819050919050565b6122e8816122d5565b81146122f357600080fd5b50565b600081359050612305816122df565b92915050565b60008060408385031215612322576123216120fc565b5b6000612330858286016122c0565b9250506020612341858286016122f6565b9150509250929050565b612354816122d5565b82525050565b600060208201905061236f600083018461234b565b92915050565b60008060006060848603121561238e5761238d6120fc565b5b600061239c868287016122c0565b93505060206123ad868287016122c0565b92505060406123be868287016122f6565b9150509250925092565b6000819050919050565b6123db816123c8565b81146123e657600080fd5b50565b6000813590506123f8816123d2565b92915050565b600060208284031215612414576124136120fc565b5b6000612422848285016123e9565b91505092915050565b612434816123c8565b82525050565b600060208201905061244f600083018461242b565b92915050565b6000806040838503121561246c5761246b6120fc565b5b600061247a858286016123e9565b925050602061248b858286016122c0565b9150509250929050565b600060ff82169050919050565b6124ab81612495565b82525050565b60006020820190506124c660008301846124a2565b92915050565b6000602082840312156124e2576124e16120fc565b5b60006124f0848285016122f6565b91505092915050565b60006020828403121561250f5761250e6120fc565b5b600061251d848285016122c0565b91505092915050565b6000806040838503121561253d5761253c6120fc565b5b600061254b858286016123e9565b925050602061255c858286016122f6565b9150509250929050565b61256f81612297565b82525050565b600060208201905061258a6000830184612566565b92915050565b600080604083850312156125a7576125a66120fc565b5b60006125b5858286016122c0565b92505060206125c6858286016122c0565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061261757607f821691505b6020821081141561262b5761262a6125d0565b5b50919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206160008201527f6c6c6f77616e6365000000000000000000000000000000000000000000000000602082015250565b600061268d6028836121c7565b915061269882612631565b604082019050919050565b600060208201905081810360008301526126bc81612680565b9050919050565b7f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560008201527f20726f6c657320666f722073656c660000000000000000000000000000000000602082015250565b600061271f602f836121c7565b915061272a826126c3565b604082019050919050565b6000602082019050818103600083015261274e81612712565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061278f826122d5565b915061279a836122d5565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156127cf576127ce612755565b5b828201905092915050565b7f45524332305072657365744d696e7465725061757365723a206d75737420686160008201527f76652070617573657220726f6c6520746f20756e706175736500000000000000602082015250565b60006128366039836121c7565b9150612841826127da565b604082019050919050565b6000602082019050818103600083015261286581612829565b9050919050565b7f45524332305072657365744d696e7465725061757365723a206d75737420686160008201527f7665206d696e74657220726f6c6520746f206d696e7400000000000000000000602082015250565b60006128c86036836121c7565b91506128d38261286c565b604082019050919050565b600060208201905081810360008301526128f7816128bb565b9050919050565b7f45524332303a206275726e20616d6f756e74206578636565647320616c6c6f7760008201527f616e636500000000000000000000000000000000000000000000000000000000602082015250565b600061295a6024836121c7565b9150612965826128fe565b604082019050919050565b600060208201905081810360008301526129898161294d565b9050919050565b7f45524332305072657365744d696e7465725061757365723a206d75737420686160008201527f76652070617573657220726f6c6520746f207061757365000000000000000000602082015250565b60006129ec6037836121c7565b91506129f782612990565b604082019050919050565b60006020820190508181036000830152612a1b816129df565b9050919050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000612a7e6025836121c7565b9150612a8982612a22565b604082019050919050565b60006020820190508181036000830152612aad81612a71565b9050919050565b7f45524332305061757361626c653a20746f6b656e207472616e7366657220776860008201527f696c652070617573656400000000000000000000000000000000000000000000602082015250565b6000612b10602a836121c7565b9150612b1b82612ab4565b604082019050919050565b60006020820190508181036000830152612b3f81612b03565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000612ba26024836121c7565b9150612bad82612b46565b604082019050919050565b60006020820190508181036000830152612bd181612b95565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000612c346022836121c7565b9150612c3f82612bd8565b604082019050919050565b60006020820190508181036000830152612c6381612c27565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b6000612cc66025836121c7565b9150612cd182612c6a565b604082019050919050565b60006020820190508181036000830152612cf581612cb9565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000612d586023836121c7565b9150612d6382612cfc565b604082019050919050565b60006020820190508181036000830152612d8781612d4b565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b6000612dea6026836121c7565b9150612df582612d8e565b604082019050919050565b60006020820190508181036000830152612e1981612ddd565b9050919050565b600081905092915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000600082015250565b6000612e61601783612e20565b9150612e6c82612e2b565b601782019050919050565b6000612e82826121bc565b612e8c8185612e20565b9350612e9c8185602086016121d8565b80840191505092915050565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000600082015250565b6000612ede601183612e20565b9150612ee982612ea8565b601182019050919050565b6000612eff82612e54565b9150612f0b8285612e77565b9150612f1682612ed1565b9150612f228284612e77565b91508190509392505050565b7f5061757361626c653a206e6f7420706175736564000000000000000000000000600082015250565b6000612f646014836121c7565b9150612f6f82612f2e565b602082019050919050565b60006020820190508181036000830152612f9381612f57565b9050919050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b6000612fd0601f836121c7565b9150612fdb82612f9a565b602082019050919050565b60006020820190508181036000830152612fff81612fc3565b9050919050565b7f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360008201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b60006130626021836121c7565b915061306d82613006565b604082019050919050565b6000602082019050818103600083015261309181613055565b9050919050565b7f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60008201527f6365000000000000000000000000000000000000000000000000000000000000602082015250565b60006130f46022836121c7565b91506130ff82613098565b604082019050919050565b60006020820190508181036000830152613123816130e7565b9050919050565b6000613135826122d5565b9150613140836122d5565b92508282101561315357613152612755565b5b828203905092915050565b7f5061757361626c653a2070617573656400000000000000000000000000000000600082015250565b60006131946010836121c7565b915061319f8261315e565b602082019050919050565b600060208201905081810360008301526131c381613187565b9050919050565b60006131d5826122d5565b91506131e0836122d5565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561321957613218612755565b5b828202905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600061328d826122d5565b915060008214156132a1576132a0612755565b5b600182039050919050565b7f537472696e67733a20686578206c656e67746820696e73756666696369656e74600082015250565b60006132e26020836121c7565b91506132ed826132ac565b602082019050919050565b60006020820190508181036000830152613311816132d5565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea26469706673582212206d1717d0528164934ac2de7f8d8135302ae264585aa9725517927da5e3b2464e64736f6c63430008090033",
-  "contractName": "ERC20MaliciousDelayed"
-}
diff --git a/contracts/compiled_contracts/ERC20MinterBurnerDecimals.json b/contracts/compiled_contracts/ERC20MinterBurnerDecimals.json
deleted file mode 100644
index 84955e5c..00000000
--- a/contracts/compiled_contracts/ERC20MinterBurnerDecimals.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "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\":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\":[],\"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\":[],\"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\":[],\"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\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"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\"}]",
-  "bin": "60806040523480156200001157600080fd5b5060405162003ca338038062003ca38339818101604052810190620000379190620006c8565b82828160059080519060200190620000519291906200043d565b5080600690805190602001906200006a9291906200043d565b5050506000600760006101000a81548160ff021916908315150217905550620000ac6000801b620000a06200018960201b60201c565b6200019160201b60201c565b620000ed7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6620000e16200018960201b60201c565b6200019160201b60201c565b6200012e7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a620001226200018960201b60201c565b6200019160201b60201c565b6200016f7f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a848620001636200018960201b60201c565b6200019160201b60201c565b6200018081620001a760201b60201c565b505050620007c7565b600033905090565b620001a38282620001c560201b60201c565b5050565b80600760016101000a81548160ff021916908360ff16021790555050565b620001dc82826200020d60201b620010191760201c565b620002088160016000858152602001908152602001600020620002fe60201b620010f91790919060201c565b505050565b6200021f82826200033660201b60201c565b620002fa57600160008084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506200029f6200018960201b60201c565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b60006200032e836000018373ffffffffffffffffffffffffffffffffffffffff1660001b620003a060201b60201c565b905092915050565b600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b6000620003b483836200041a60201b60201c565b6200040f57826000018290806001815401808255809150506001900390600052602060002001600090919091909150558260000180549050836001016000848152602001908152602001600020819055506001905062000414565b600090505b92915050565b600080836001016000848152602001908152602001600020541415905092915050565b8280546200044b9062000791565b90600052602060002090601f0160209004810192826200046f5760008555620004bb565b82601f106200048a57805160ff1916838001178555620004bb565b82800160010185558215620004bb579182015b82811115620004ba5782518255916020019190600101906200049d565b5b509050620004ca9190620004ce565b5090565b5b80821115620004e9576000816000905550600101620004cf565b5090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b62000556826200050b565b810181811067ffffffffffffffff821117156200057857620005776200051c565b5b80604052505050565b60006200058d620004ed565b90506200059b82826200054b565b919050565b600067ffffffffffffffff821115620005be57620005bd6200051c565b5b620005c9826200050b565b9050602081019050919050565b60005b83811015620005f6578082015181840152602081019050620005d9565b8381111562000606576000848401525b50505050565b6000620006236200061d84620005a0565b62000581565b90508281526020810184848401111562000642576200064162000506565b5b6200064f848285620005d6565b509392505050565b600082601f8301126200066f576200066e62000501565b5b8151620006818482602086016200060c565b91505092915050565b600060ff82169050919050565b620006a2816200068a565b8114620006ae57600080fd5b50565b600081519050620006c28162000697565b92915050565b600080600060608486031215620006e457620006e3620004f7565b5b600084015167ffffffffffffffff811115620007055762000704620004fc565b5b620007138682870162000657565b935050602084015167ffffffffffffffff811115620007375762000736620004fc565b5b620007458682870162000657565b92505060406200075886828701620006b1565b9150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620007aa57607f821691505b60208210811415620007c157620007c062000762565b5b50919050565b6134cc80620007d76000396000f3fe608060405234801561001057600080fd5b50600436106101da5760003560e01c80635c975abb11610104578063a217fddf116100a2578063d539139311610071578063d53913931461057d578063d547741f1461059b578063dd62ed3e146105b7578063e63ab1e9146105e7576101da565b8063a217fddf146104cf578063a457c2d7146104ed578063a9059cbb1461051d578063ca15c8731461054d576101da565b80638456cb59116100de5780638456cb59146104475780639010d07c1461045157806391d148541461048157806395d89b41146104b1576101da565b80635c975abb146103dd57806370a08231146103fb57806379cc67901461042b576101da565b8063282c51f31161017c578063395093511161014b578063395093511461036b5780633f4ba83a1461039b57806340c10f19146103a557806342966c68146103c1576101da565b8063282c51f3146102f75780632f2ff15d14610315578063313ce5671461033157806336568abe1461034f576101da565b806318160ddd116101b857806318160ddd1461025d5780631cf2c7e21461027b57806323b872dd14610297578063248a9ca3146102c7576101da565b806301ffc9a7146101df57806306fdde031461020f578063095ea7b31461022d575b600080fd5b6101f960048036038101906101f49190612216565b610605565b604051610206919061225e565b60405180910390f35b61021761067f565b6040516102249190612312565b60405180910390f35b610247600480360381019061024291906123c8565b610711565b604051610254919061225e565b60405180910390f35b61026561072f565b6040516102729190612417565b60405180910390f35b610295600480360381019061029091906123c8565b610739565b005b6102b160048036038101906102ac9190612432565b6107b7565b6040516102be919061225e565b60405180910390f35b6102e160048036038101906102dc91906124bb565b6108af565b6040516102ee91906124f7565b60405180910390f35b6102ff6108ce565b60405161030c91906124f7565b60405180910390f35b61032f600480360381019061032a9190612512565b6108f2565b005b61033961091b565b604051610346919061256e565b60405180910390f35b61036960048036038101906103649190612512565b610932565b005b610385600480360381019061038091906123c8565b6109b5565b604051610392919061225e565b60405180910390f35b6103a3610a61565b005b6103bf60048036038101906103ba91906123c8565b610adb565b005b6103db60048036038101906103d69190612589565b610b59565b005b6103e5610b6d565b6040516103f2919061225e565b60405180910390f35b610415600480360381019061041091906125b6565b610b84565b6040516104229190612417565b60405180910390f35b610445600480360381019061044091906123c8565b610bcd565b005b61044f610c48565b005b61046b600480360381019061046691906125e3565b610cc2565b6040516104789190612632565b60405180910390f35b61049b60048036038101906104969190612512565b610cf1565b6040516104a8919061225e565b60405180910390f35b6104b9610d5b565b6040516104c69190612312565b60405180910390f35b6104d7610ded565b6040516104e491906124f7565b60405180910390f35b610507600480360381019061050291906123c8565b610df4565b604051610514919061225e565b60405180910390f35b610537600480360381019061053291906123c8565b610edf565b604051610544919061225e565b60405180910390f35b610567600480360381019061056291906124bb565b610efd565b6040516105749190612417565b60405180910390f35b610585610f21565b60405161059291906124f7565b60405180910390f35b6105b560048036038101906105b09190612512565b610f45565b005b6105d160048036038101906105cc919061264d565b610f6e565b6040516105de9190612417565b60405180910390f35b6105ef610ff5565b6040516105fc91906124f7565b60405180910390f35b60007f5a05180f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161480610678575061067782611129565b5b9050919050565b60606005805461068e906126bc565b80601f01602080910402602001604051908101604052809291908181526020018280546106ba906126bc565b80156107075780601f106106dc57610100808354040283529160200191610707565b820191906000526020600020905b8154815290600101906020018083116106ea57829003601f168201915b5050505050905090565b600061072561071e6111a3565b84846111ab565b6001905092915050565b6000600454905090565b61076a7f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a8486107656111a3565b610cf1565b6107a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107a090612760565b60405180910390fd5b6107b38282611376565b5050565b60006107c484848461154f565b6000600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600061080f6111a3565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508281101561088f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610886906127f2565b60405180910390fd5b6108a38561089b6111a3565b8584036111ab565b60019150509392505050565b6000806000838152602001908152602001600020600101549050919050565b7f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a84881565b6108fb826108af565b61090c816109076111a3565b6117d3565b6109168383611870565b505050565b6000600760019054906101000a900460ff16905090565b61093a6111a3565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146109a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161099e90612884565b60405180910390fd5b6109b182826118a4565b5050565b6000610a576109c26111a3565b8484600360006109d06111a3565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610a5291906128d3565b6111ab565b6001905092915050565b610a927f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610a8d6111a3565b610cf1565b610ad1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ac89061299b565b60405180910390fd5b610ad96118d8565b565b610b0c7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6610b076111a3565b610cf1565b610b4b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b4290612a2d565b60405180910390fd5b610b55828261197a565b5050565b610b6a610b646111a3565b82611376565b50565b6000600760009054906101000a900460ff16905090565b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6000610be083610bdb6111a3565b610f6e565b905081811015610c25576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c1c90612abf565b60405180910390fd5b610c3983610c316111a3565b8484036111ab565b610c438383611376565b505050565b610c797f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a610c746111a3565b610cf1565b610cb8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610caf90612b51565b60405180910390fd5b610cc0611adb565b565b6000610ce98260016000868152602001908152602001600020611b7e90919063ffffffff16565b905092915050565b600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b606060068054610d6a906126bc565b80601f0160208091040260200160405190810160405280929190818152602001828054610d96906126bc565b8015610de35780601f10610db857610100808354040283529160200191610de3565b820191906000526020600020905b815481529060010190602001808311610dc657829003601f168201915b5050505050905090565b6000801b81565b60008060036000610e036111a3565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082811015610ec0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610eb790612be3565b60405180910390fd5b610ed4610ecb6111a3565b858584036111ab565b600191505092915050565b6000610ef3610eec6111a3565b848461154f565b6001905092915050565b6000610f1a60016000848152602001908152602001600020611b98565b9050919050565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b610f4e826108af565b610f5f81610f5a6111a3565b6117d3565b610f6983836118a4565b505050565b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b6110238282610cf1565b6110f557600160008084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555061109a6111a3565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b6000611121836000018373ffffffffffffffffffffffffffffffffffffffff1660001b611bad565b905092915050565b60007f7965db0b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061119c575061119b82611c1d565b5b9050919050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561121b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161121290612c75565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561128b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161128290612d07565b60405180910390fd5b80600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516113699190612417565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156113e6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113dd90612d99565b60405180910390fd5b6113f282600083611c87565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015611479576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161147090612e2b565b60405180910390fd5b818103600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600460008282546114d19190612e4b565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516115369190612417565b60405180910390a361154a83600084611c97565b505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156115bf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115b690612ef1565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561162f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161162690612f83565b60405180910390fd5b61163a838383611c87565b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050818110156116c1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116b890613015565b60405180910390fd5b818103600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461175691906128d3565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516117ba9190612417565b60405180910390a36117cd848484611c97565b50505050565b6117dd8282610cf1565b61186c576118028173ffffffffffffffffffffffffffffffffffffffff166014611c9c565b6118108360001c6020611c9c565b604051602001611821929190613109565b6040516020818303038152906040526040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118639190612312565b60405180910390fd5b5050565b61187a8282611019565b61189f81600160008581526020019081526020016000206110f990919063ffffffff16565b505050565b6118ae8282611ed8565b6118d38160016000858152602001908152602001600020611fb990919063ffffffff16565b505050565b6118e0610b6d565b61191f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119169061318f565b60405180910390fd5b6000600760006101000a81548160ff0219169083151502179055507f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6119636111a3565b6040516119709190612632565b60405180910390a1565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156119ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119e1906131fb565b60405180910390fd5b6119f660008383611c87565b8060046000828254611a0891906128d3565b9250508190555080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611a5e91906128d3565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051611ac39190612417565b60405180910390a3611ad760008383611c97565b5050565b611ae3610b6d565b15611b23576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b1a90613267565b60405180910390fd5b6001600760006101000a81548160ff0219169083151502179055507f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258611b676111a3565b604051611b749190612632565b60405180910390a1565b6000611b8d8360000183611fe9565b60001c905092915050565b6000611ba682600001612014565b9050919050565b6000611bb98383612025565b611c12578260000182908060018154018082558091505060019003906000526020600020016000909190919091505582600001805490508360010160008481526020019081526020016000208190555060019050611c17565b600090505b92915050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b611c92838383612048565b505050565b505050565b606060006002836002611caf9190613287565b611cb991906128d3565b67ffffffffffffffff811115611cd257611cd16132e1565b5b6040519080825280601f01601f191660200182016040528015611d045781602001600182028036833780820191505090505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110611d3c57611d3b613310565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110611da057611d9f613310565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006001846002611de09190613287565b611dea91906128d3565b90505b6001811115611e8a577f3031323334353637383961626364656600000000000000000000000000000000600f861660108110611e2c57611e2b613310565b5b1a60f81b828281518110611e4357611e42613310565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600485901c945080611e839061333f565b9050611ded565b5060008414611ece576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611ec5906133b5565b60405180910390fd5b8091505092915050565b611ee28282610cf1565b15611fb557600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550611f5a6111a3565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45b5050565b6000611fe1836000018373ffffffffffffffffffffffffffffffffffffffff1660001b6120a0565b905092915050565b600082600001828154811061200157612000613310565b5b9060005260206000200154905092915050565b600081600001805490509050919050565b600080836001016000848152602001908152602001600020541415905092915050565b6120538383836121b4565b61205b610b6d565b1561209b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161209290613447565b60405180910390fd5b505050565b600080836001016000848152602001908152602001600020549050600081146121a85760006001826120d29190612e4b565b90506000600186600001805490506120ea9190612e4b565b905081811461215957600086600001828154811061210b5761210a613310565b5b906000526020600020015490508087600001848154811061212f5761212e613310565b5b90600052602060002001819055508387600101600083815260200190815260200160002081905550505b8560000180548061216d5761216c613467565b5b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506121ae565b60009150505b92915050565b505050565b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6121f3816121be565b81146121fe57600080fd5b50565b600081359050612210816121ea565b92915050565b60006020828403121561222c5761222b6121b9565b5b600061223a84828501612201565b91505092915050565b60008115159050919050565b61225881612243565b82525050565b6000602082019050612273600083018461224f565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156122b3578082015181840152602081019050612298565b838111156122c2576000848401525b50505050565b6000601f19601f8301169050919050565b60006122e482612279565b6122ee8185612284565b93506122fe818560208601612295565b612307816122c8565b840191505092915050565b6000602082019050818103600083015261232c81846122d9565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061235f82612334565b9050919050565b61236f81612354565b811461237a57600080fd5b50565b60008135905061238c81612366565b92915050565b6000819050919050565b6123a581612392565b81146123b057600080fd5b50565b6000813590506123c28161239c565b92915050565b600080604083850312156123df576123de6121b9565b5b60006123ed8582860161237d565b92505060206123fe858286016123b3565b9150509250929050565b61241181612392565b82525050565b600060208201905061242c6000830184612408565b92915050565b60008060006060848603121561244b5761244a6121b9565b5b60006124598682870161237d565b935050602061246a8682870161237d565b925050604061247b868287016123b3565b9150509250925092565b6000819050919050565b61249881612485565b81146124a357600080fd5b50565b6000813590506124b58161248f565b92915050565b6000602082840312156124d1576124d06121b9565b5b60006124df848285016124a6565b91505092915050565b6124f181612485565b82525050565b600060208201905061250c60008301846124e8565b92915050565b60008060408385031215612529576125286121b9565b5b6000612537858286016124a6565b92505060206125488582860161237d565b9150509250929050565b600060ff82169050919050565b61256881612552565b82525050565b6000602082019050612583600083018461255f565b92915050565b60006020828403121561259f5761259e6121b9565b5b60006125ad848285016123b3565b91505092915050565b6000602082840312156125cc576125cb6121b9565b5b60006125da8482850161237d565b91505092915050565b600080604083850312156125fa576125f96121b9565b5b6000612608858286016124a6565b9250506020612619858286016123b3565b9150509250929050565b61262c81612354565b82525050565b60006020820190506126476000830184612623565b92915050565b60008060408385031215612664576126636121b9565b5b60006126728582860161237d565b92505060206126838582860161237d565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806126d457607f821691505b602082108114156126e8576126e761268d565b5b50919050565b7f45524332304d696e7465724275726e6572446563696d616c733a206d7573742060008201527f68617665206275726e657220726f6c6520746f206275726e0000000000000000602082015250565b600061274a603883612284565b9150612755826126ee565b604082019050919050565b600060208201905081810360008301526127798161273d565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206160008201527f6c6c6f77616e6365000000000000000000000000000000000000000000000000602082015250565b60006127dc602883612284565b91506127e782612780565b604082019050919050565b6000602082019050818103600083015261280b816127cf565b9050919050565b7f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560008201527f20726f6c657320666f722073656c660000000000000000000000000000000000602082015250565b600061286e602f83612284565b915061287982612812565b604082019050919050565b6000602082019050818103600083015261289d81612861565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006128de82612392565b91506128e983612392565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561291e5761291d6128a4565b5b828201905092915050565b7f45524332304d696e7465724275726e6572446563696d616c733a206d7573742060008201527f686176652070617573657220726f6c6520746f20756e70617573650000000000602082015250565b6000612985603b83612284565b915061299082612929565b604082019050919050565b600060208201905081810360008301526129b481612978565b9050919050565b7f45524332304d696e7465724275726e6572446563696d616c733a206d7573742060008201527f68617665206d696e74657220726f6c6520746f206d696e740000000000000000602082015250565b6000612a17603883612284565b9150612a22826129bb565b604082019050919050565b60006020820190508181036000830152612a4681612a0a565b9050919050565b7f45524332303a206275726e20616d6f756e74206578636565647320616c6c6f7760008201527f616e636500000000000000000000000000000000000000000000000000000000602082015250565b6000612aa9602483612284565b9150612ab482612a4d565b604082019050919050565b60006020820190508181036000830152612ad881612a9c565b9050919050565b7f45524332304d696e7465724275726e6572446563696d616c733a206d7573742060008201527f686176652070617573657220726f6c6520746f20706175736500000000000000602082015250565b6000612b3b603983612284565b9150612b4682612adf565b604082019050919050565b60006020820190508181036000830152612b6a81612b2e565b9050919050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000612bcd602583612284565b9150612bd882612b71565b604082019050919050565b60006020820190508181036000830152612bfc81612bc0565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000612c5f602483612284565b9150612c6a82612c03565b604082019050919050565b60006020820190508181036000830152612c8e81612c52565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000612cf1602283612284565b9150612cfc82612c95565b604082019050919050565b60006020820190508181036000830152612d2081612ce4565b9050919050565b7f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360008201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b6000612d83602183612284565b9150612d8e82612d27565b604082019050919050565b60006020820190508181036000830152612db281612d76565b9050919050565b7f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60008201527f6365000000000000000000000000000000000000000000000000000000000000602082015250565b6000612e15602283612284565b9150612e2082612db9565b604082019050919050565b60006020820190508181036000830152612e4481612e08565b9050919050565b6000612e5682612392565b9150612e6183612392565b925082821015612e7457612e736128a4565b5b828203905092915050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b6000612edb602583612284565b9150612ee682612e7f565b604082019050919050565b60006020820190508181036000830152612f0a81612ece565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000612f6d602383612284565b9150612f7882612f11565b604082019050919050565b60006020820190508181036000830152612f9c81612f60565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b6000612fff602683612284565b915061300a82612fa3565b604082019050919050565b6000602082019050818103600083015261302e81612ff2565b9050919050565b600081905092915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000600082015250565b6000613076601783613035565b915061308182613040565b601782019050919050565b600061309782612279565b6130a18185613035565b93506130b1818560208601612295565b80840191505092915050565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000600082015250565b60006130f3601183613035565b91506130fe826130bd565b601182019050919050565b600061311482613069565b9150613120828561308c565b915061312b826130e6565b9150613137828461308c565b91508190509392505050565b7f5061757361626c653a206e6f7420706175736564000000000000000000000000600082015250565b6000613179601483612284565b915061318482613143565b602082019050919050565b600060208201905081810360008301526131a88161316c565b9050919050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b60006131e5601f83612284565b91506131f0826131af565b602082019050919050565b60006020820190508181036000830152613214816131d8565b9050919050565b7f5061757361626c653a2070617573656400000000000000000000000000000000600082015250565b6000613251601083612284565b915061325c8261321b565b602082019050919050565b6000602082019050818103600083015261328081613244565b9050919050565b600061329282612392565b915061329d83612392565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156132d6576132d56128a4565b5b828202905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600061334a82612392565b9150600082141561335e5761335d6128a4565b5b600182039050919050565b7f537472696e67733a20686578206c656e67746820696e73756666696369656e74600082015250565b600061339f602083612284565b91506133aa82613369565b602082019050919050565b600060208201905081810360008301526133ce81613392565b9050919050565b7f45524332305061757361626c653a20746f6b656e207472616e7366657220776860008201527f696c652070617573656400000000000000000000000000000000000000000000602082015250565b6000613431602a83612284565b915061343c826133d5565b604082019050919050565b6000602082019050818103600083015261346081613424565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea2646970667358221220c3d4a4231a6c94cfb03623ea4b77df2c9ccfa487132bebf43620219e3dc2f4cf64736f6c63430008090033",
-  "contractName": "ERC20MinterBurnerDecimals"
-}
diff --git a/contracts/compiled_contracts/ProposalStore.json b/contracts/compiled_contracts/ProposalStore.json
deleted file mode 100644
index 821090c9..00000000
--- a/contracts/compiled_contracts/ProposalStore.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-    "abi": "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"propId\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"title\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"desc\",\"type\":\"string\"},{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"propId\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"title\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"desc\",\"type\":\"string\"},{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"}],\"name\":\"AddProposal\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"propId\",\"type\":\"uint256\"}],\"name\":\"QueryProp\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"title\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"desc\",\"type\":\"string\"},{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"}],\"internalType\":\"struct ProposalStore.Proposal\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]","bin":"60a06040523480156200001157600080fd5b5060405162002b2238038062002b22833981810160405281019062000037919062000aa8565b3373ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff168152505060006040518060e00160405280898152602001888152602001878152602001868152602001858152602001848152602001838152509050806000808a8152602001908152602001600020600082015181600001556020820151816001019081620000d6919062000e56565b506040820151816002019081620000ee919062000e56565b5060608201518160030190805190602001906200010d9291906200017c565b5060808201518160040190805190602001906200012c9291906200020b565b5060a08201518160050190805190602001906200014b9291906200025d565b5060c08201518160060190805190602001906200016a929190620002bd565b50905050505050505050505062001094565b828054828255906000526020600020908101928215620001f8579160200282015b82811115620001f75782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906200019d565b5b5090506200020791906200031d565b5090565b8280548282559060005260206000209081019282156200024a579160200282015b82811115620002495782518255916020019190600101906200022c565b5b5090506200025991906200031d565b5090565b828054828255906000526020600020908101928215620002aa579160200282015b82811115620002a957825182908162000298919062000e56565b50916020019190600101906200027e565b5b509050620002b991906200033c565b5090565b8280548282559060005260206000209081019282156200030a579160200282015b8281111562000309578251829081620002f8919062000fad565b5091602001919060010190620002de565b5b50905062000319919062000364565b5090565b5b80821115620003385760008160009055506001016200031e565b5090565b5b808211156200036057600081816200035691906200038c565b506001016200033d565b5090565b5b808211156200038857600081816200037e9190620003d2565b5060010162000365565b5090565b5080546200039a9062000c4f565b6000825580601f10620003ae5750620003cf565b601f016020900490600052602060002090810190620003ce91906200031d565b5b50565b508054620003e09062000c4f565b6000825580601f10620003f4575062000415565b601f0160209004906000526020600020908101906200041491906200031d565b5b50565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b62000441816200042c565b81146200044d57600080fd5b50565b600081519050620004618162000436565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620004bc8262000471565b810181811067ffffffffffffffff82111715620004de57620004dd62000482565b5b80604052505050565b6000620004f362000418565b9050620005018282620004b1565b919050565b600067ffffffffffffffff82111562000524576200052362000482565b5b6200052f8262000471565b9050602081019050919050565b60005b838110156200055c5780820151818401526020810190506200053f565b838111156200056c576000848401525b50505050565b600062000589620005838462000506565b620004e7565b905082815260208101848484011115620005a857620005a76200046c565b5b620005b58482856200053c565b509392505050565b600082601f830112620005d557620005d462000467565b5b8151620005e784826020860162000572565b91505092915050565b600067ffffffffffffffff8211156200060e576200060d62000482565b5b602082029050602081019050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620006518262000624565b9050919050565b620006638162000644565b81146200066f57600080fd5b50565b600081519050620006838162000658565b92915050565b6000620006a06200069a84620005f0565b620004e7565b90508083825260208201905060208402830185811115620006c657620006c56200061f565b5b835b81811015620006f35780620006de888262000672565b845260208401935050602081019050620006c8565b5050509392505050565b600082601f83011262000715576200071462000467565b5b81516200072784826020860162000689565b91505092915050565b600067ffffffffffffffff8211156200074e576200074d62000482565b5b602082029050602081019050919050565b600062000776620007708462000730565b620004e7565b905080838252602082019050602084028301858111156200079c576200079b6200061f565b5b835b81811015620007c95780620007b4888262000450565b8452602084019350506020810190506200079e565b5050509392505050565b600082601f830112620007eb57620007ea62000467565b5b8151620007fd8482602086016200075f565b91505092915050565b600067ffffffffffffffff82111562000824576200082362000482565b5b602082029050602081019050919050565b60006200084c620008468462000806565b620004e7565b905080838252602082019050602084028301858111156200087257620008716200061f565b5b835b81811015620008c057805167ffffffffffffffff8111156200089b576200089a62000467565b5b808601620008aa8982620005bd565b8552602085019450505060208101905062000874565b5050509392505050565b600082601f830112620008e257620008e162000467565b5b8151620008f484826020860162000835565b91505092915050565b600067ffffffffffffffff8211156200091b576200091a62000482565b5b602082029050602081019050919050565b600067ffffffffffffffff8211156200094a576200094962000482565b5b620009558262000471565b9050602081019050919050565b60006200097962000973846200092c565b620004e7565b9050828152602081018484840111156200099857620009976200046c565b5b620009a58482856200053c565b509392505050565b600082601f830112620009c557620009c462000467565b5b8151620009d784826020860162000962565b91505092915050565b6000620009f7620009f184620008fd565b620004e7565b9050808382526020820190506020840283018581111562000a1d5762000a1c6200061f565b5b835b8181101562000a6b57805167ffffffffffffffff81111562000a465762000a4562000467565b5b80860162000a558982620009ad565b8552602085019450505060208101905062000a1f565b5050509392505050565b600082601f83011262000a8d5762000a8c62000467565b5b815162000a9f848260208601620009e0565b91505092915050565b600080600080600080600060e0888a03121562000aca5762000ac962000422565b5b600062000ada8a828b0162000450565b975050602088015167ffffffffffffffff81111562000afe5762000afd62000427565b5b62000b0c8a828b01620005bd565b965050604088015167ffffffffffffffff81111562000b305762000b2f62000427565b5b62000b3e8a828b01620005bd565b955050606088015167ffffffffffffffff81111562000b625762000b6162000427565b5b62000b708a828b01620006fd565b945050608088015167ffffffffffffffff81111562000b945762000b9362000427565b5b62000ba28a828b01620007d3565b93505060a088015167ffffffffffffffff81111562000bc65762000bc562000427565b5b62000bd48a828b01620008ca565b92505060c088015167ffffffffffffffff81111562000bf85762000bf762000427565b5b62000c068a828b0162000a75565b91505092959891949750929550565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168062000c6857607f821691505b60208210810362000c7e5762000c7d62000c20565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b60006008830262000ce87fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000ca9565b62000cf4868362000ca9565b95508019841693508086168417925050509392505050565b6000819050919050565b600062000d3762000d3162000d2b846200042c565b62000d0c565b6200042c565b9050919050565b6000819050919050565b62000d538362000d16565b62000d6b62000d628262000d3e565b84845462000cb6565b825550505050565b600090565b62000d8262000d73565b62000d8f81848462000d48565b505050565b5b8181101562000db75762000dab60008262000d78565b60018101905062000d95565b5050565b601f82111562000e065762000dd08162000c84565b62000ddb8462000c99565b8101602085101562000deb578190505b62000e0362000dfa8562000c99565b83018262000d94565b50505b505050565b600082821c905092915050565b600062000e2b6000198460080262000e0b565b1980831691505092915050565b600062000e46838362000e18565b9150826002028217905092915050565b62000e618262000c15565b67ffffffffffffffff81111562000e7d5762000e7c62000482565b5b62000e89825462000c4f565b62000e9682828562000dbb565b600060209050601f83116001811462000ece576000841562000eb9578287015190505b62000ec5858262000e38565b86555062000f35565b601f19841662000ede8662000c84565b60005b8281101562000f085784890151825560018201915060208501945060208101905062000ee1565b8683101562000f28578489015162000f24601f89168262000e18565b8355505b6001600288020188555050505b505050505050565b600081519050919050565b60008190508160005260206000209050919050565b601f82111562000fa85762000f728162000f48565b62000f7d8462000c99565b8101602085101562000f8d578190505b62000fa562000f9c8562000c99565b83018262000d94565b50505b505050565b62000fb88262000f3d565b67ffffffffffffffff81111562000fd45762000fd362000482565b5b62000fe0825462000c4f565b62000fed82828562000f5d565b600060209050601f83116001811462001025576000841562001010578287015190505b6200101c858262000e38565b8655506200108c565b601f198416620010358662000f48565b60005b828110156200105f5784890151825560018201915060208501945060208101905062001038565b868310156200107f57848901516200107b601f89168262000e18565b8355505b6001600288020188555050505b505050505050565b608051611a73620010af600039600060890152611a736000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806327bc123e1461003b578063f0e147b814610057575b600080fd5b61005560048036038101906100509190610ffd565b610087565b005b610071600480360381019061006c9190611147565b6101e0565b60405161007e9190611610565b60405180910390f35b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146100df57600080fd5b60006040518060e00160405280898152602001888152602001878152602001868152602001858152602001848152602001838152509050806000808a8152602001908152602001600020600082015181600001556020820151816001019081610148919061183e565b50604082015181600201908161015e919061183e565b50606082015181600301908051906020019061017b929190610776565b506080820151816004019080519060200190610198929190610800565b5060a08201518160050190805190602001906101b592919061084d565b5060c08201518160060190805190602001906101d29291906108a6565b509050505050505050505050565b6101e86108ff565b8160008084815260200190815260200160002060000154036105ee576000808381526020019081526020016000206040518060e00160405290816000820154815260200160018201805461023b90611661565b80601f016020809104026020016040519081016040528092919081815260200182805461026790611661565b80156102b45780601f10610289576101008083540402835291602001916102b4565b820191906000526020600020905b81548152906001019060200180831161029757829003601f168201915b505050505081526020016002820180546102cd90611661565b80601f01602080910402602001604051908101604052809291908181526020018280546102f990611661565b80156103465780601f1061031b57610100808354040283529160200191610346565b820191906000526020600020905b81548152906001019060200180831161032957829003601f168201915b50505050508152602001600382018054806020026020016040519081016040528092919081815260200182805480156103d457602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161038a575b505050505081526020016004820180548060200260200160405190810160405280929190818152602001828054801561042c57602002820191906000526020600020905b815481526020019060010190808311610418575b5050505050815260200160058201805480602002602001604051908101604052809291908181526020016000905b8282101561050657838290600052602060002001805461047990611661565b80601f01602080910402602001604051908101604052809291908181526020018280546104a590611661565b80156104f25780601f106104c7576101008083540402835291602001916104f2565b820191906000526020600020905b8154815290600101906020018083116104d557829003601f168201915b50505050508152602001906001019061045a565b50505050815260200160068201805480602002602001604051908101604052809291908181526020016000905b828210156105df57838290600052602060002001805461055290611661565b80601f016020809104026020016040519081016040528092919081815260200182805461057e90611661565b80156105cb5780601f106105a0576101008083540402835291602001916105cb565b820191906000526020600020905b8154815290600101906020018083116105ae57829003601f168201915b505050505081526020019060010190610533565b50505050815250509050610771565b6040518060e0016040528060008152602001604051806020016040528060008152508152602001604051806020016040528060008152508152602001600067ffffffffffffffff81111561064557610644610a86565b5b6040519080825280602002602001820160405280156106735781602001602082028036833780820191505090505b508152602001600067ffffffffffffffff81111561069457610693610a86565b5b6040519080825280602002602001820160405280156106c25781602001602082028036833780820191505090505b508152602001600067ffffffffffffffff8111156106e3576106e2610a86565b5b60405190808252806020026020018201604052801561071657816020015b60608152602001906001900390816107015790505b508152602001600067ffffffffffffffff81111561073757610736610a86565b5b60405190808252806020026020018201604052801561076a57816020015b60608152602001906001900390816107555790505b5081525090505b919050565b8280548282559060005260206000209081019282156107ef579160200282015b828111156107ee5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555091602001919060010190610796565b5b5090506107fc919061093c565b5090565b82805482825590600052602060002090810192821561083c579160200282015b8281111561083b578251825591602001919060010190610820565b5b509050610849919061093c565b5090565b828054828255906000526020600020908101928215610895579160200282015b82811115610894578251829081610884919061183e565b509160200191906001019061086d565b5b5090506108a29190610959565b5090565b8280548282559060005260206000209081019282156108ee579160200282015b828111156108ed5782518290816108dd919061196b565b50916020019190600101906108c6565b5b5090506108fb919061097d565b5090565b6040518060e00160405280600081526020016060815260200160608152602001606081526020016060815260200160608152602001606081525090565b5b8082111561095557600081600090555060010161093d565b5090565b5b80821115610979576000818161097091906109a1565b5060010161095a565b5090565b5b8082111561099d576000818161099491906109e1565b5060010161097e565b5090565b5080546109ad90611661565b6000825580601f106109bf57506109de565b601f0160209004906000526020600020908101906109dd919061093c565b5b50565b5080546109ed90611661565b6000825580601f106109ff5750610a1e565b601f016020900490600052602060002090810190610a1d919061093c565b5b50565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b610a4881610a35565b8114610a5357600080fd5b50565b600081359050610a6581610a3f565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610abe82610a75565b810181811067ffffffffffffffff82111715610add57610adc610a86565b5b80604052505050565b6000610af0610a21565b9050610afc8282610ab5565b919050565b600067ffffffffffffffff821115610b1c57610b1b610a86565b5b610b2582610a75565b9050602081019050919050565b82818337600083830152505050565b6000610b54610b4f84610b01565b610ae6565b905082815260208101848484011115610b7057610b6f610a70565b5b610b7b848285610b32565b509392505050565b600082601f830112610b9857610b97610a6b565b5b8135610ba8848260208601610b41565b91505092915050565b600067ffffffffffffffff821115610bcc57610bcb610a86565b5b602082029050602081019050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610c0d82610be2565b9050919050565b610c1d81610c02565b8114610c2857600080fd5b50565b600081359050610c3a81610c14565b92915050565b6000610c53610c4e84610bb1565b610ae6565b90508083825260208201905060208402830185811115610c7657610c75610bdd565b5b835b81811015610c9f5780610c8b8882610c2b565b845260208401935050602081019050610c78565b5050509392505050565b600082601f830112610cbe57610cbd610a6b565b5b8135610cce848260208601610c40565b91505092915050565b600067ffffffffffffffff821115610cf257610cf1610a86565b5b602082029050602081019050919050565b6000610d16610d1184610cd7565b610ae6565b90508083825260208201905060208402830185811115610d3957610d38610bdd565b5b835b81811015610d625780610d4e8882610a56565b845260208401935050602081019050610d3b565b5050509392505050565b600082601f830112610d8157610d80610a6b565b5b8135610d91848260208601610d03565b91505092915050565b600067ffffffffffffffff821115610db557610db4610a86565b5b602082029050602081019050919050565b6000610dd9610dd484610d9a565b610ae6565b90508083825260208201905060208402830185811115610dfc57610dfb610bdd565b5b835b81811015610e4357803567ffffffffffffffff811115610e2157610e20610a6b565b5b808601610e2e8982610b83565b85526020850194505050602081019050610dfe565b5050509392505050565b600082601f830112610e6257610e61610a6b565b5b8135610e72848260208601610dc6565b91505092915050565b600067ffffffffffffffff821115610e9657610e95610a86565b5b602082029050602081019050919050565b600067ffffffffffffffff821115610ec257610ec1610a86565b5b610ecb82610a75565b9050602081019050919050565b6000610eeb610ee684610ea7565b610ae6565b905082815260208101848484011115610f0757610f06610a70565b5b610f12848285610b32565b509392505050565b600082601f830112610f2f57610f2e610a6b565b5b8135610f3f848260208601610ed8565b91505092915050565b6000610f5b610f5684610e7b565b610ae6565b90508083825260208201905060208402830185811115610f7e57610f7d610bdd565b5b835b81811015610fc557803567ffffffffffffffff811115610fa357610fa2610a6b565b5b808601610fb08982610f1a565b85526020850194505050602081019050610f80565b5050509392505050565b600082601f830112610fe457610fe3610a6b565b5b8135610ff4848260208601610f48565b91505092915050565b600080600080600080600060e0888a03121561101c5761101b610a2b565b5b600061102a8a828b01610a56565b975050602088013567ffffffffffffffff81111561104b5761104a610a30565b5b6110578a828b01610b83565b965050604088013567ffffffffffffffff81111561107857611077610a30565b5b6110848a828b01610b83565b955050606088013567ffffffffffffffff8111156110a5576110a4610a30565b5b6110b18a828b01610ca9565b945050608088013567ffffffffffffffff8111156110d2576110d1610a30565b5b6110de8a828b01610d6c565b93505060a088013567ffffffffffffffff8111156110ff576110fe610a30565b5b61110b8a828b01610e4d565b92505060c088013567ffffffffffffffff81111561112c5761112b610a30565b5b6111388a828b01610fcf565b91505092959891949750929550565b60006020828403121561115d5761115c610a2b565b5b600061116b84828501610a56565b91505092915050565b61117d81610a35565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b838110156111bd5780820151818401526020810190506111a2565b838111156111cc576000848401525b50505050565b60006111dd82611183565b6111e7818561118e565b93506111f781856020860161119f565b61120081610a75565b840191505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b61124081610c02565b82525050565b60006112528383611237565b60208301905092915050565b6000602082019050919050565b60006112768261120b565b6112808185611216565b935061128b83611227565b8060005b838110156112bc5781516112a38882611246565b97506112ae8361125e565b92505060018101905061128f565b5085935050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60006113018383611174565b60208301905092915050565b6000602082019050919050565b6000611325826112c9565b61132f81856112d4565b935061133a836112e5565b8060005b8381101561136b57815161135288826112f5565b975061135d8361130d565b92505060018101905061133e565b5085935050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60006113b083836111d2565b905092915050565b6000602082019050919050565b60006113d082611378565b6113da8185611383565b9350836020820285016113ec85611394565b8060005b85811015611428578484038952815161140985826113a4565b9450611414836113b8565b925060208a019950506001810190506113f0565b50829750879550505050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600081519050919050565b600082825260208201905092915050565b600061148d82611466565b6114978185611471565b93506114a781856020860161119f565b6114b081610a75565b840191505092915050565b60006114c78383611482565b905092915050565b6000602082019050919050565b60006114e78261143a565b6114f18185611445565b93508360208202850161150385611456565b8060005b8581101561153f578484038952815161152085826114bb565b945061152b836114cf565b925060208a01995050600181019050611507565b50829750879550505050505092915050565b600060e0830160008301516115696000860182611174565b506020830151848203602086015261158182826111d2565b9150506040830151848203604086015261159b82826111d2565b915050606083015184820360608601526115b5828261126b565b915050608083015184820360808601526115cf828261131a565b91505060a083015184820360a08601526115e982826113c5565b91505060c083015184820360c086015261160382826114dc565b9150508091505092915050565b6000602082019050818103600083015261162a8184611551565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061167957607f821691505b60208210810361168c5761168b611632565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026116f47fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff826116b7565b6116fe86836116b7565b95508019841693508086168417925050509392505050565b6000819050919050565b600061173b61173661173184610a35565b611716565b610a35565b9050919050565b6000819050919050565b61175583611720565b61176961176182611742565b8484546116c4565b825550505050565b600090565b61177e611771565b61178981848461174c565b505050565b5b818110156117ad576117a2600082611776565b60018101905061178f565b5050565b601f8211156117f2576117c381611692565b6117cc846116a7565b810160208510156117db578190505b6117ef6117e7856116a7565b83018261178e565b50505b505050565b600082821c905092915050565b6000611815600019846008026117f7565b1980831691505092915050565b600061182e8383611804565b9150826002028217905092915050565b61184782611183565b67ffffffffffffffff8111156118605761185f610a86565b5b61186a8254611661565b6118758282856117b1565b600060209050601f8311600181146118a85760008415611896578287015190505b6118a08582611822565b865550611908565b601f1984166118b686611692565b60005b828110156118de578489015182556001820191506020850194506020810190506118b9565b868310156118fb57848901516118f7601f891682611804565b8355505b6001600288020188555050505b505050505050565b60008190508160005260206000209050919050565b601f8211156119665761193781611910565b611940846116a7565b8101602085101561194f578190505b61196361195b856116a7565b83018261178e565b50505b505050565b61197482611466565b67ffffffffffffffff81111561198d5761198c610a86565b5b6119978254611661565b6119a2828285611925565b600060209050601f8311600181146119d557600084156119c3578287015190505b6119cd8582611822565b865550611a35565b601f1984166119e386611910565b60005b82811015611a0b578489015182556001820191506020850194506020810190506119e6565b86831015611a285784890151611a24601f891682611804565b8355505b6001600288020188555050505b50505050505056fea26469706673582212200dba62d022bc9124436e444abbab60b8957053b0afad54f4145c760e340382ec64736f6c634300080f0033",
-    "contractName" : "ProposalStore"
-}
diff --git a/contracts/compiled_contracts/Turnstile.json b/contracts/compiled_contracts/Turnstile.json
deleted file mode 100644
index 169f38d3..00000000
--- a/contracts/compiled_contracts/Turnstile.json
+++ /dev/null
@@ -1,6 +0,0 @@
-
-{
-    "abi": "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AlreadyRegistered\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTokenId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAnOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotSmartContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NothingToDistribute\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NothingToWithdraw\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Unregistered\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"approved\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"ApprovalForAll\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"smartContract\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Assign\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeAmount\",\"type\":\"uint256\"}],\"name\":\"DistributeFees\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"smartContract\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Register\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeAmount\",\"type\":\"uint256\"}],\"name\":\"Withdraw\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_tokenId\",\"type\":\"uint256\"}],\"name\":\"assign\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"balances\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currentCounterId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_tokenId\",\"type\":\"uint256\"}],\"name\":\"distributeFees\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"feeRecipient\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"registered\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"getApproved\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_smartContract\",\"type\":\"address\"}],\"name\":\"getTokenId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"isApprovedForAll\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_smartContract\",\"type\":\"address\"}],\"name\":\"isRegistered\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ownerOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_recipient\",\"type\":\"address\"}],\"name\":\"register\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"setApprovalForAll\",\"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\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"tokenByIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"tokenOfOwnerByIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"tokenURI\",\"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\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_tokenId\",\"type\":\"uint256\"},{\"internalType\":\"addresspayable\",\"name\":\"_recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
-    "bin": "60806040523480156200001157600080fd5b506040518060400160405280600981526020017f5475726e7374696c6500000000000000000000000000000000000000000000008152506040518060400160405280600981526020017f5475726e7374696c6500000000000000000000000000000000000000000000008152506200009e62000092620000ca60201b60201c565b620000d260201b60201c565b8160019081620000af919062000410565b508060029081620000c1919062000410565b505050620004f7565b600033905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200021857607f821691505b6020821081036200022e576200022d620001d0565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620002987fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000259565b620002a4868362000259565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620002f1620002eb620002e584620002bc565b620002c6565b620002bc565b9050919050565b6000819050919050565b6200030d83620002d0565b620003256200031c82620002f8565b84845462000266565b825550505050565b600090565b6200033c6200032d565b6200034981848462000302565b505050565b5b8181101562000371576200036560008262000332565b6001810190506200034f565b5050565b601f821115620003c0576200038a8162000234565b620003958462000249565b81016020851015620003a5578190505b620003bd620003b48562000249565b8301826200034e565b50505b505050565b600082821c905092915050565b6000620003e560001984600802620003c5565b1980831691505092915050565b6000620004008383620003d2565b9150826002028217905092915050565b6200041b8262000196565b67ffffffffffffffff811115620004375762000436620001a1565b5b620004438254620001ff565b6200045082828562000375565b600060209050601f83116001811462000488576000841562000473578287015190505b6200047f8582620003f2565b865550620004ef565b601f198416620004988662000234565b60005b82811015620004c2578489015182556001820191506020850194506020810190506200049b565b86831015620004e25784890151620004de601f891682620003d2565b8355505b6001600288020188555050505b505050505050565b613cf180620005076000396000f3fe6080604052600436106101b75760003560e01c806370a08231116100ec578063c87b56dd1161008a578063e66bb34511610064578063e66bb34514610682578063e985e9c5146106ad578063f1537686146106ea578063f2fde38b14610727576101b7565b8063c87b56dd146105ca578063d78162e914610607578063e63697c814610645576101b7565b806395d89b41116100c657806395d89b4114610510578063a22cb4651461053b578063b88d4fde14610564578063c3c5a5471461058d576101b7565b806370a0823114610491578063715018a6146104ce5780638da5cb5b146104e5576101b7565b806342842e0e116101595780634c081138116101335780634c081138146103be5780634f6ccce7146103fb5780636029bf9f146104385780636352211e14610454576101b7565b806342842e0e1461031b5780634420e486146103445780634903b0d114610381576101b7565b8063095ea7b311610195578063095ea7b31461026157806318160ddd1461028a57806323b872dd146102b55780632f745c59146102de576101b7565b806301ffc9a7146101bc57806306fdde03146101f9578063081812fc14610224575b600080fd5b3480156101c857600080fd5b506101e360048036038101906101de919061290f565b610750565b6040516101f09190612957565b60405180910390f35b34801561020557600080fd5b5061020e6107ca565b60405161021b9190612a02565b60405180910390f35b34801561023057600080fd5b5061024b60048036038101906102469190612a5a565b61085c565b6040516102589190612ac8565b60405180910390f35b34801561026d57600080fd5b5061028860048036038101906102839190612b0f565b6108a2565b005b34801561029657600080fd5b5061029f6109b9565b6040516102ac9190612b5e565b60405180910390f35b3480156102c157600080fd5b506102dc60048036038101906102d79190612b79565b6109c6565b005b3480156102ea57600080fd5b5061030560048036038101906103009190612b0f565b610a26565b6040516103129190612b5e565b60405180910390f35b34801561032757600080fd5b50610342600480360381019061033d9190612b79565b610acb565b005b34801561035057600080fd5b5061036b60048036038101906103669190612bcc565b610aeb565b6040516103789190612b5e565b60405180910390f35b34801561038d57600080fd5b506103a860048036038101906103a39190612a5a565b610c82565b6040516103b59190612b5e565b60405180910390f35b3480156103ca57600080fd5b506103e560048036038101906103e09190612a5a565b610c9a565b6040516103f29190612b5e565b60405180910390f35b34801561040757600080fd5b50610422600480360381019061041d9190612a5a565b610deb565b60405161042f9190612b5e565b60405180910390f35b610452600480360381019061044d9190612a5a565b610e5c565b005b34801561046057600080fd5b5061047b60048036038101906104769190612a5a565b610f04565b6040516104889190612ac8565b60405180910390f35b34801561049d57600080fd5b506104b860048036038101906104b39190612bcc565b610fb5565b6040516104c59190612b5e565b60405180910390f35b3480156104da57600080fd5b506104e361106c565b005b3480156104f157600080fd5b506104fa611080565b6040516105079190612ac8565b60405180910390f35b34801561051c57600080fd5b506105256110a9565b6040516105329190612a02565b60405180910390f35b34801561054757600080fd5b50610562600480360381019061055d9190612c25565b61113b565b005b34801561057057600080fd5b5061058b60048036038101906105869190612d9a565b611151565b005b34801561059957600080fd5b506105b460048036038101906105af9190612bcc565b6111b3565b6040516105c19190612957565b60405180910390f35b3480156105d657600080fd5b506105f160048036038101906105ec9190612a5a565b61120c565b6040516105fe9190612a02565b60405180910390f35b34801561061357600080fd5b5061062e60048036038101906106299190612bcc565b611274565b60405161063c929190612e1d565b60405180910390f35b34801561065157600080fd5b5061066c60048036038101906106679190612e84565b6112a5565b6040516106799190612b5e565b60405180910390f35b34801561068e57600080fd5b506106976113f3565b6040516106a49190612b5e565b60405180910390f35b3480156106b957600080fd5b506106d460048036038101906106cf9190612ed7565b611404565b6040516106e19190612957565b60405180910390f35b3480156106f657600080fd5b50610711600480360381019061070c9190612bcc565b611498565b60405161071e9190612b5e565b60405180910390f35b34801561073357600080fd5b5061074e60048036038101906107499190612bcc565b611523565b005b60007f780e9d63000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806107c357506107c2826115a6565b5b9050919050565b6060600180546107d990612f46565b80601f016020809104026020016040519081016040528092919081815260200182805461080590612f46565b80156108525780601f1061082757610100808354040283529160200191610852565b820191906000526020600020905b81548152906001019060200180831161083557829003601f168201915b5050505050905090565b600061086782611688565b6005600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b60006108ad82610f04565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361091d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161091490612fe9565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1661093c6116d3565b73ffffffffffffffffffffffffffffffffffffffff16148061096b575061096a816109656116d3565b611404565b5b6109aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109a19061307b565b60405180910390fd5b6109b483836116db565b505050565b6000600980549050905090565b6109d76109d16116d3565b82611794565b610a16576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a0d9061310d565b60405180910390fd5b610a21838383611829565b505050565b6000610a3183610fb5565b8210610a72576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a699061319f565b60405180910390fd5b600760008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002054905092915050565b610ae683838360405180602001604052806000815250611151565b505050565b600080339050610afa816111b3565b15610b31576040517f3a81d6fc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000339050600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603610b9c576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ba6600b611a8f565b9250610bb28484611a9d565b610bbc600b611c76565b7fcc0bec1447060c88cdc5a739cf29cfa26c453574dd3f5b9e4dcc317d6401cb1c818585604051610bef939291906131bf565b60405180910390a1604051806040016040528084815260200160011515815250600c60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000820151816000015560208201518160010160006101000a81548160ff0219169083151502179055509050505050919050565b600d6020528060005260406000206000915090505481565b600080339050610ca9816111b3565b15610ce0576040517f3a81d6fc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000339050610cee84611c8c565b610d24576040517f3f6cc76800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f8a0e37b73a0d9c82e205d4d1a3ff3d0b57ce5f4d7bccf6bac03336dc101cb7ba8185604051610d559291906131f6565b60405180910390a1604051806040016040528085815260200160011515815250600c60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000820151816000015560208201518160010160006101000a81548160ff0219169083151502179055509050508392505050919050565b6000610df56109b9565b8210610e36576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e2d90613291565b60405180910390fd5b60098281548110610e4a57610e496132b1565b5b90600052602060002001549050919050565b610e64611cf8565b60003403610e9e576040517f01663f2400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b34600d60008381526020019081526020016000206000828254610ec1919061330f565b925050819055507f916ad8171ef8c567c7790377a142f0200f9565940680c06e30dd105cfd9249688134604051610ef9929190613343565b60405180910390a150565b6000806003600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610fac576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610fa3906133b8565b60405180910390fd5b80915050919050565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611025576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161101c9061344a565b60405180910390fd5b600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b611074611cf8565b61107e6000611d76565b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6060600280546110b890612f46565b80601f01602080910402602001604051908101604052809291908181526020018280546110e490612f46565b80156111315780601f1061110657610100808354040283529160200191611131565b820191906000526020600020905b81548152906001019060200180831161111457829003601f168201915b5050505050905090565b61114d6111466116d3565b8383611e3a565b5050565b61116261115c6116d3565b83611794565b6111a1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111989061310d565b60405180910390fd5b6111ad84848484611fa6565b50505050565b6000600c60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160009054906101000a900460ff169050919050565b606061121782611688565b6000611221612002565b90506000815111611241576040518060200160405280600081525061126c565b8061124b84612019565b60405160200161125c9291906134a6565b6040516020818303038152906040525b915050919050565b600c6020528060005260406000206000915090508060000154908060010160009054906101000a900460ff16905082565b6000833373ffffffffffffffffffffffffffffffffffffffff166112c882610f04565b73ffffffffffffffffffffffffffffffffffffffff1614611315576040517feea91ff800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600d6000878152602001908152602001600020549050600081148061133c5750600084145b15611373576040517fd0d04f6000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8084111561137f578093505b838161138b91906134ca565b600d6000888152602001908152602001600020819055507f9da6493a92039daf47d1f2d7a782299c5994c6323eb1e972f69c432089ec52bf8686866040516113d59392919061355d565b60405180910390a16113e78585612179565b83925050509392505050565b60006113ff600b611a8f565b905090565b6000600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b60006114a3826111b3565b6114d9576040517f7748bce600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001549050919050565b61152b611cf8565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361159a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161159190613606565b60405180910390fd5b6115a381611d76565b50565b60007f80ac58cd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061167157507f5b5e139f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061168157506116808261226d565b5b9050919050565b61169181611c8c565b6116d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116c7906133b8565b60405180910390fd5b50565b600033905090565b816005600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff1661174e83610f04565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000806117a083610f04565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614806117e257506117e18185611404565b5b8061182057508373ffffffffffffffffffffffffffffffffffffffff166118088461085c565b73ffffffffffffffffffffffffffffffffffffffff16145b91505092915050565b8273ffffffffffffffffffffffffffffffffffffffff1661184982610f04565b73ffffffffffffffffffffffffffffffffffffffff161461189f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161189690613698565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361190e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119059061372a565b60405180910390fd5b6119198383836122d7565b6119246000826116db565b6001600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461197491906134ca565b925050819055506001600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546119cb919061330f565b92505081905550816003600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4611a8a8383836123e9565b505050565b600081600001549050919050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611b0c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b0390613796565b60405180910390fd5b611b1581611c8c565b15611b55576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b4c90613802565b60405180910390fd5b611b61600083836122d7565b6001600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611bb1919061330f565b92505081905550816003600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4611c72600083836123e9565b5050565b6001816000016000828254019250508190555050565b60008073ffffffffffffffffffffffffffffffffffffffff166003600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614159050919050565b611d006116d3565b73ffffffffffffffffffffffffffffffffffffffff16611d1e611080565b73ffffffffffffffffffffffffffffffffffffffff1614611d74576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611d6b9061386e565b60405180910390fd5b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603611ea8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611e9f906138da565b60405180910390fd5b80600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051611f999190612957565b60405180910390a3505050565b611fb1848484611829565b611fbd848484846123ee565b611ffc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611ff39061396c565b60405180910390fd5b50505050565b606060405180602001604052806000815250905090565b606060008203612060576040518060400160405280600181526020017f30000000000000000000000000000000000000000000000000000000000000008152509050612174565b600082905060005b6000821461209257808061207b9061398c565b915050600a8261208b9190613a03565b9150612068565b60008167ffffffffffffffff8111156120ae576120ad612c6f565b5b6040519080825280601f01601f1916602001820160405280156120e05781602001600182028036833780820191505090505b5090505b6000851461216d576001826120f991906134ca565b9150600a856121089190613a34565b6030612114919061330f565b60f81b81838151811061212a576121296132b1565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600a856121669190613a03565b94506120e4565b8093505050505b919050565b804710156121bc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121b390613ab1565b60405180910390fd5b60008273ffffffffffffffffffffffffffffffffffffffff16826040516121e290613b02565b60006040518083038185875af1925050503d806000811461221f576040519150601f19603f3d011682016040523d82523d6000602084013e612224565b606091505b5050905080612268576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161225f90613b89565b60405180910390fd5b505050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b6122e2838383612575565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036123245761231f8161257a565b612363565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16146123625761236183826125c3565b5b5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036123a5576123a081612730565b6123e4565b8273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16146123e3576123e28282612801565b5b5b505050565b505050565b600061240f8473ffffffffffffffffffffffffffffffffffffffff16612880565b15612568578373ffffffffffffffffffffffffffffffffffffffff1663150b7a026124386116d3565b8786866040518563ffffffff1660e01b815260040161245a9493929190613bfe565b6020604051808303816000875af192505050801561249657506040513d601f19601f820116820180604052508101906124939190613c5f565b60015b612518573d80600081146124c6576040519150601f19603f3d011682016040523d82523d6000602084013e6124cb565b606091505b506000815103612510576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016125079061396c565b60405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161491505061256d565b600190505b949350505050565b505050565b600980549050600a600083815260200190815260200160002081905550600981908060018154018082558091505060019003906000526020600020016000909190919091505550565b600060016125d084610fb5565b6125da91906134ca565b90506000600860008481526020019081526020016000205490508181146126bf576000600760008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002054905080600760008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002081905550816008600083815260200190815260200160002081905550505b6008600084815260200190815260200160002060009055600760008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008381526020019081526020016000206000905550505050565b6000600160098054905061274491906134ca565b90506000600a6000848152602001908152602001600020549050600060098381548110612774576127736132b1565b5b906000526020600020015490508060098381548110612796576127956132b1565b5b906000526020600020018190555081600a600083815260200190815260200160002081905550600a60008581526020019081526020016000206000905560098054806127e5576127e4613c8c565b5b6001900381819060005260206000200160009055905550505050565b600061280c83610fb5565b905081600760008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002081905550806008600084815260200190815260200160002081905550505050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000604051905090565b600080fd5b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6128ec816128b7565b81146128f757600080fd5b50565b600081359050612909816128e3565b92915050565b600060208284031215612925576129246128ad565b5b6000612933848285016128fa565b91505092915050565b60008115159050919050565b6129518161293c565b82525050565b600060208201905061296c6000830184612948565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156129ac578082015181840152602081019050612991565b60008484015250505050565b6000601f19601f8301169050919050565b60006129d482612972565b6129de818561297d565b93506129ee81856020860161298e565b6129f7816129b8565b840191505092915050565b60006020820190508181036000830152612a1c81846129c9565b905092915050565b6000819050919050565b612a3781612a24565b8114612a4257600080fd5b50565b600081359050612a5481612a2e565b92915050565b600060208284031215612a7057612a6f6128ad565b5b6000612a7e84828501612a45565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000612ab282612a87565b9050919050565b612ac281612aa7565b82525050565b6000602082019050612add6000830184612ab9565b92915050565b612aec81612aa7565b8114612af757600080fd5b50565b600081359050612b0981612ae3565b92915050565b60008060408385031215612b2657612b256128ad565b5b6000612b3485828601612afa565b9250506020612b4585828601612a45565b9150509250929050565b612b5881612a24565b82525050565b6000602082019050612b736000830184612b4f565b92915050565b600080600060608486031215612b9257612b916128ad565b5b6000612ba086828701612afa565b9350506020612bb186828701612afa565b9250506040612bc286828701612a45565b9150509250925092565b600060208284031215612be257612be16128ad565b5b6000612bf084828501612afa565b91505092915050565b612c028161293c565b8114612c0d57600080fd5b50565b600081359050612c1f81612bf9565b92915050565b60008060408385031215612c3c57612c3b6128ad565b5b6000612c4a85828601612afa565b9250506020612c5b85828601612c10565b9150509250929050565b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b612ca7826129b8565b810181811067ffffffffffffffff82111715612cc657612cc5612c6f565b5b80604052505050565b6000612cd96128a3565b9050612ce58282612c9e565b919050565b600067ffffffffffffffff821115612d0557612d04612c6f565b5b612d0e826129b8565b9050602081019050919050565b82818337600083830152505050565b6000612d3d612d3884612cea565b612ccf565b905082815260208101848484011115612d5957612d58612c6a565b5b612d64848285612d1b565b509392505050565b600082601f830112612d8157612d80612c65565b5b8135612d91848260208601612d2a565b91505092915050565b60008060008060808587031215612db457612db36128ad565b5b6000612dc287828801612afa565b9450506020612dd387828801612afa565b9350506040612de487828801612a45565b925050606085013567ffffffffffffffff811115612e0557612e046128b2565b5b612e1187828801612d6c565b91505092959194509250565b6000604082019050612e326000830185612b4f565b612e3f6020830184612948565b9392505050565b6000612e5182612a87565b9050919050565b612e6181612e46565b8114612e6c57600080fd5b50565b600081359050612e7e81612e58565b92915050565b600080600060608486031215612e9d57612e9c6128ad565b5b6000612eab86828701612a45565b9350506020612ebc86828701612e6f565b9250506040612ecd86828701612a45565b9150509250925092565b60008060408385031215612eee57612eed6128ad565b5b6000612efc85828601612afa565b9250506020612f0d85828601612afa565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680612f5e57607f821691505b602082108103612f7157612f70612f17565b5b50919050565b7f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560008201527f7200000000000000000000000000000000000000000000000000000000000000602082015250565b6000612fd360218361297d565b9150612fde82612f77565b604082019050919050565b6000602082019050818103600083015261300281612fc6565b9050919050565b7f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60008201527f6b656e206f776e6572206e6f7220617070726f76656420666f7220616c6c0000602082015250565b6000613065603e8361297d565b915061307082613009565b604082019050919050565b6000602082019050818103600083015261309481613058565b9050919050565b7f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560008201527f72206e6f7220617070726f766564000000000000000000000000000000000000602082015250565b60006130f7602e8361297d565b91506131028261309b565b604082019050919050565b60006020820190508181036000830152613126816130ea565b9050919050565b7f455243373231456e756d657261626c653a206f776e657220696e646578206f7560008201527f74206f6620626f756e6473000000000000000000000000000000000000000000602082015250565b6000613189602b8361297d565b91506131948261312d565b604082019050919050565b600060208201905081810360008301526131b88161317c565b9050919050565b60006060820190506131d46000830186612ab9565b6131e16020830185612ab9565b6131ee6040830184612b4f565b949350505050565b600060408201905061320b6000830185612ab9565b6132186020830184612b4f565b9392505050565b7f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60008201527f7574206f6620626f756e64730000000000000000000000000000000000000000602082015250565b600061327b602c8361297d565b91506132868261321f565b604082019050919050565b600060208201905081810360008301526132aa8161326e565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061331a82612a24565b915061332583612a24565b925082820190508082111561333d5761333c6132e0565b5b92915050565b60006040820190506133586000830185612b4f565b6133656020830184612b4f565b9392505050565b7f4552433732313a20696e76616c696420746f6b656e2049440000000000000000600082015250565b60006133a260188361297d565b91506133ad8261336c565b602082019050919050565b600060208201905081810360008301526133d181613395565b9050919050565b7f4552433732313a2061646472657373207a65726f206973206e6f74206120766160008201527f6c6964206f776e65720000000000000000000000000000000000000000000000602082015250565b600061343460298361297d565b915061343f826133d8565b604082019050919050565b6000602082019050818103600083015261346381613427565b9050919050565b600081905092915050565b600061348082612972565b61348a818561346a565b935061349a81856020860161298e565b80840191505092915050565b60006134b28285613475565b91506134be8284613475565b91508190509392505050565b60006134d582612a24565b91506134e083612a24565b92508282039050818111156134f8576134f76132e0565b5b92915050565b6000819050919050565b600061352361351e61351984612a87565b6134fe565b612a87565b9050919050565b600061353582613508565b9050919050565b60006135478261352a565b9050919050565b6135578161353c565b82525050565b60006060820190506135726000830186612b4f565b61357f602083018561354e565b61358c6040830184612b4f565b949350505050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b60006135f060268361297d565b91506135fb82613594565b604082019050919050565b6000602082019050818103600083015261361f816135e3565b9050919050565b7f4552433732313a207472616e736665722066726f6d20696e636f72726563742060008201527f6f776e6572000000000000000000000000000000000000000000000000000000602082015250565b600061368260258361297d565b915061368d82613626565b604082019050919050565b600060208201905081810360008301526136b181613675565b9050919050565b7f4552433732313a207472616e7366657220746f20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b600061371460248361297d565b915061371f826136b8565b604082019050919050565b6000602082019050818103600083015261374381613707565b9050919050565b7f4552433732313a206d696e7420746f20746865207a65726f2061646472657373600082015250565b600061378060208361297d565b915061378b8261374a565b602082019050919050565b600060208201905081810360008301526137af81613773565b9050919050565b7f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000600082015250565b60006137ec601c8361297d565b91506137f7826137b6565b602082019050919050565b6000602082019050818103600083015261381b816137df565b9050919050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b600061385860208361297d565b915061386382613822565b602082019050919050565b600060208201905081810360008301526138878161384b565b9050919050565b7f4552433732313a20617070726f766520746f2063616c6c657200000000000000600082015250565b60006138c460198361297d565b91506138cf8261388e565b602082019050919050565b600060208201905081810360008301526138f3816138b7565b9050919050565b7f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560008201527f63656976657220696d706c656d656e7465720000000000000000000000000000602082015250565b600061395660328361297d565b9150613961826138fa565b604082019050919050565b6000602082019050818103600083015261398581613949565b9050919050565b600061399782612a24565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036139c9576139c86132e0565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000613a0e82612a24565b9150613a1983612a24565b925082613a2957613a286139d4565b5b828204905092915050565b6000613a3f82612a24565b9150613a4a83612a24565b925082613a5a57613a596139d4565b5b828206905092915050565b7f416464726573733a20696e73756666696369656e742062616c616e6365000000600082015250565b6000613a9b601d8361297d565b9150613aa682613a65565b602082019050919050565b60006020820190508181036000830152613aca81613a8e565b9050919050565b600081905092915050565b50565b6000613aec600083613ad1565b9150613af782613adc565b600082019050919050565b6000613b0d82613adf565b9150819050919050565b7f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260008201527f6563697069656e74206d61792068617665207265766572746564000000000000602082015250565b6000613b73603a8361297d565b9150613b7e82613b17565b604082019050919050565b60006020820190508181036000830152613ba281613b66565b9050919050565b600081519050919050565b600082825260208201905092915050565b6000613bd082613ba9565b613bda8185613bb4565b9350613bea81856020860161298e565b613bf3816129b8565b840191505092915050565b6000608082019050613c136000830187612ab9565b613c206020830186612ab9565b613c2d6040830185612b4f565b8181036060830152613c3f8184613bc5565b905095945050505050565b600081519050613c59816128e3565b92915050565b600060208284031215613c7557613c746128ad565b5b6000613c8384828501613c4a565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea2646970667358221220af8e0e24f2d12a183489099cadea3fda44860b88b97c863a8ef139d5fee2459164736f6c63430008110033",
-    "contractName" : "Turnstile"
-}
diff --git a/contracts/compiled_contracts/callee.json b/contracts/compiled_contracts/callee.json
deleted file mode 100644
index 93922ed2..00000000
--- a/contracts/compiled_contracts/callee.json
+++ /dev/null
@@ -1,6 +0,0 @@
-
-{
-    "abi":"[{\"inputs\":[],\"name\":\"Int\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"setInt\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
-    "bin":"608060405234801561001057600080fd5b50610133806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806344420311146037578063b6c27e5314604f575b600080fd5b604d60048036038101906049919060af565b6069565b005b60556073565b6040516060919060e4565b60405180910390f35b8060008190555050565b60005481565b600080fd5b6000819050919050565b608f81607e565b8114609957600080fd5b50565b60008135905060a9816088565b92915050565b60006020828403121560c25760c16079565b5b600060ce84828501609c565b91505092915050565b60de81607e565b82525050565b600060208201905060f7600083018460d7565b9291505056fea2646970667358221220419f2ec3c731b473ed042fc9894a33156ca82941a01a95cf30ddd465f0fa469164736f6c634300080f0033",
-    "contractName" : "callee"
-}
diff --git a/contracts/compiled_contracts/caller.json b/contracts/compiled_contracts/caller.json
deleted file mode 100644
index 1cdcb984..00000000
--- a/contracts/compiled_contracts/caller.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-"abi":"[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"propId\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"title\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"desc\",\"type\":\"string\"},{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"propId\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"title\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"desc\",\"type\":\"string\"},{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"}],\"name\":\"AddProposal\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"propId\",\"type\":\"uint256\"}],\"name\":\"QueryProp\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"title\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"desc\",\"type\":\"string\"},{\"internalType\":\"address[]\",\"name\":\"targets\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"},{\"internalType\":\"string[]\",\"name\":\"signatures\",\"type\":\"string[]\"},{\"internalType\":\"bytes[]\",\"name\":\"calldatas\",\"type\":\"bytes[]\"}],\"internalType\":\"struct ProposalStore.Proposal\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]",
-"bin":"60806040523480156200001157600080fd5b5060405162002acf38038062002acf833981810160405281019062000037919062000ab5565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060006040518060e0016040528089815260200188815260200187815260200186815260200185815260200184815260200183815250905080600160008a8152602001908152602001600020600082015181600001556020820151816001019081620000e3919062000e63565b506040820151816002019081620000fb919062000e63565b5060608201518160030190805190602001906200011a92919062000189565b5060808201518160040190805190602001906200013992919062000218565b5060a0820151816005019080519060200190620001589291906200026a565b5060c082015181600601908051906020019062000177929190620002ca565b509050505050505050505050620010a1565b82805482825590600052602060002090810192821562000205579160200282015b82811115620002045782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555091602001919060010190620001aa565b5b5090506200021491906200032a565b5090565b82805482825590600052602060002090810192821562000257579160200282015b828111156200025657825182559160200191906001019062000239565b5b5090506200026691906200032a565b5090565b828054828255906000526020600020908101928215620002b7579160200282015b82811115620002b6578251829081620002a5919062000e63565b50916020019190600101906200028b565b5b509050620002c6919062000349565b5090565b82805482825590600052602060002090810192821562000317579160200282015b828111156200031657825182908162000305919062000fba565b5091602001919060010190620002eb565b5b50905062000326919062000371565b5090565b5b80821115620003455760008160009055506001016200032b565b5090565b5b808211156200036d576000818162000363919062000399565b506001016200034a565b5090565b5b808211156200039557600081816200038b9190620003df565b5060010162000372565b5090565b508054620003a79062000c5c565b6000825580601f10620003bb5750620003dc565b601f016020900490600052602060002090810190620003db91906200032a565b5b50565b508054620003ed9062000c5c565b6000825580601f1062000401575062000422565b601f0160209004906000526020600020908101906200042191906200032a565b5b50565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b6200044e8162000439565b81146200045a57600080fd5b50565b6000815190506200046e8162000443565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620004c9826200047e565b810181811067ffffffffffffffff82111715620004eb57620004ea6200048f565b5b80604052505050565b60006200050062000425565b90506200050e8282620004be565b919050565b600067ffffffffffffffff8211156200053157620005306200048f565b5b6200053c826200047e565b9050602081019050919050565b60005b83811015620005695780820151818401526020810190506200054c565b8381111562000579576000848401525b50505050565b600062000596620005908462000513565b620004f4565b905082815260208101848484011115620005b557620005b462000479565b5b620005c284828562000549565b509392505050565b600082601f830112620005e257620005e162000474565b5b8151620005f48482602086016200057f565b91505092915050565b600067ffffffffffffffff8211156200061b576200061a6200048f565b5b602082029050602081019050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200065e8262000631565b9050919050565b620006708162000651565b81146200067c57600080fd5b50565b600081519050620006908162000665565b92915050565b6000620006ad620006a784620005fd565b620004f4565b90508083825260208201905060208402830185811115620006d357620006d26200062c565b5b835b81811015620007005780620006eb88826200067f565b845260208401935050602081019050620006d5565b5050509392505050565b600082601f83011262000722576200072162000474565b5b81516200073484826020860162000696565b91505092915050565b600067ffffffffffffffff8211156200075b576200075a6200048f565b5b602082029050602081019050919050565b6000620007836200077d846200073d565b620004f4565b90508083825260208201905060208402830185811115620007a957620007a86200062c565b5b835b81811015620007d65780620007c188826200045d565b845260208401935050602081019050620007ab565b5050509392505050565b600082601f830112620007f857620007f762000474565b5b81516200080a8482602086016200076c565b91505092915050565b600067ffffffffffffffff8211156200083157620008306200048f565b5b602082029050602081019050919050565b600062000859620008538462000813565b620004f4565b905080838252602082019050602084028301858111156200087f576200087e6200062c565b5b835b81811015620008cd57805167ffffffffffffffff811115620008a857620008a762000474565b5b808601620008b78982620005ca565b8552602085019450505060208101905062000881565b5050509392505050565b600082601f830112620008ef57620008ee62000474565b5b81516200090184826020860162000842565b91505092915050565b600067ffffffffffffffff8211156200092857620009276200048f565b5b602082029050602081019050919050565b600067ffffffffffffffff8211156200095757620009566200048f565b5b62000962826200047e565b9050602081019050919050565b600062000986620009808462000939565b620004f4565b905082815260208101848484011115620009a557620009a462000479565b5b620009b284828562000549565b509392505050565b600082601f830112620009d257620009d162000474565b5b8151620009e48482602086016200096f565b91505092915050565b600062000a04620009fe846200090a565b620004f4565b9050808382526020820190506020840283018581111562000a2a5762000a296200062c565b5b835b8181101562000a7857805167ffffffffffffffff81111562000a535762000a5262000474565b5b80860162000a628982620009ba565b8552602085019450505060208101905062000a2c565b5050509392505050565b600082601f83011262000a9a5762000a9962000474565b5b815162000aac848260208601620009ed565b91505092915050565b600080600080600080600060e0888a03121562000ad75762000ad66200042f565b5b600062000ae78a828b016200045d565b975050602088015167ffffffffffffffff81111562000b0b5762000b0a62000434565b5b62000b198a828b01620005ca565b965050604088015167ffffffffffffffff81111562000b3d5762000b3c62000434565b5b62000b4b8a828b01620005ca565b955050606088015167ffffffffffffffff81111562000b6f5762000b6e62000434565b5b62000b7d8a828b016200070a565b945050608088015167ffffffffffffffff81111562000ba15762000ba062000434565b5b62000baf8a828b01620007e0565b93505060a088015167ffffffffffffffff81111562000bd35762000bd262000434565b5b62000be18a828b01620008d7565b92505060c088015167ffffffffffffffff81111562000c055762000c0462000434565b5b62000c138a828b0162000a82565b91505092959891949750929550565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168062000c7557607f821691505b60208210810362000c8b5762000c8a62000c2d565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b60006008830262000cf57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000cb6565b62000d01868362000cb6565b95508019841693508086168417925050509392505050565b6000819050919050565b600062000d4462000d3e62000d388462000439565b62000d19565b62000439565b9050919050565b6000819050919050565b62000d608362000d23565b62000d7862000d6f8262000d4b565b84845462000cc3565b825550505050565b600090565b62000d8f62000d80565b62000d9c81848462000d55565b505050565b5b8181101562000dc45762000db860008262000d85565b60018101905062000da2565b5050565b601f82111562000e135762000ddd8162000c91565b62000de88462000ca6565b8101602085101562000df8578190505b62000e1062000e078562000ca6565b83018262000da1565b50505b505050565b600082821c905092915050565b600062000e386000198460080262000e18565b1980831691505092915050565b600062000e53838362000e25565b9150826002028217905092915050565b62000e6e8262000c22565b67ffffffffffffffff81111562000e8a5762000e896200048f565b5b62000e96825462000c5c565b62000ea382828562000dc8565b600060209050601f83116001811462000edb576000841562000ec6578287015190505b62000ed2858262000e45565b86555062000f42565b601f19841662000eeb8662000c91565b60005b8281101562000f155784890151825560018201915060208501945060208101905062000eee565b8683101562000f35578489015162000f31601f89168262000e25565b8355505b6001600288020188555050505b505050505050565b600081519050919050565b60008190508160005260206000209050919050565b601f82111562000fb55762000f7f8162000f55565b62000f8a8462000ca6565b8101602085101562000f9a578190505b62000fb262000fa98562000ca6565b83018262000da1565b50505b505050565b62000fc58262000f4a565b67ffffffffffffffff81111562000fe15762000fe06200048f565b5b62000fed825462000c5c565b62000ffa82828562000f6a565b600060209050601f8311600181146200103257600084156200101d578287015190505b62001029858262000e45565b86555062001099565b601f198416620010428662000f55565b60005b828110156200106c5784890151825560018201915060208501945060208101905062001045565b868310156200108c578489015162001088601f89168262000e25565b8355505b6001600288020188555050505b505050505050565b611a1e80620010b16000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806327bc123e1461003b578063f0e147b814610057575b600080fd5b61005560048036038101906100509190610fa8565b610087565b005b610071600480360381019061006c91906110f2565b610189565b60405161007e91906115bb565b60405180910390f35b60006040518060e0016040528089815260200188815260200187815260200186815260200185815260200184815260200183815250905080600160008a81526020019081526020016000206000820151816000015560208201518160010190816100f191906117e9565b50604082015181600201908161010791906117e9565b506060820151816003019080519060200190610124929190610721565b5060808201518160040190805190602001906101419291906107ab565b5060a082015181600501908051906020019061015e9291906107f8565b5060c082015181600601908051906020019061017b929190610851565b509050505050505050505050565b6101916108aa565b8160016000848152602001908152602001600020600001540361059957600160008381526020019081526020016000206040518060e0016040529081600082015481526020016001820180546101e69061160c565b80601f01602080910402602001604051908101604052809291908181526020018280546102129061160c565b801561025f5780601f106102345761010080835404028352916020019161025f565b820191906000526020600020905b81548152906001019060200180831161024257829003601f168201915b505050505081526020016002820180546102789061160c565b80601f01602080910402602001604051908101604052809291908181526020018280546102a49061160c565b80156102f15780601f106102c6576101008083540402835291602001916102f1565b820191906000526020600020905b8154815290600101906020018083116102d457829003601f168201915b505050505081526020016003820180548060200260200160405190810160405280929190818152602001828054801561037f57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610335575b50505050508152602001600482018054806020026020016040519081016040528092919081815260200182805480156103d757602002820191906000526020600020905b8154815260200190600101908083116103c3575b5050505050815260200160058201805480602002602001604051908101604052809291908181526020016000905b828210156104b15783829060005260206000200180546104249061160c565b80601f01602080910402602001604051908101604052809291908181526020018280546104509061160c565b801561049d5780601f106104725761010080835404028352916020019161049d565b820191906000526020600020905b81548152906001019060200180831161048057829003601f168201915b505050505081526020019060010190610405565b50505050815260200160068201805480602002602001604051908101604052809291908181526020016000905b8282101561058a5783829060005260206000200180546104fd9061160c565b80601f01602080910402602001604051908101604052809291908181526020018280546105299061160c565b80156105765780601f1061054b57610100808354040283529160200191610576565b820191906000526020600020905b81548152906001019060200180831161055957829003601f168201915b5050505050815260200190600101906104de565b5050505081525050905061071c565b6040518060e0016040528060008152602001604051806020016040528060008152508152602001604051806020016040528060008152508152602001600067ffffffffffffffff8111156105f0576105ef610a31565b5b60405190808252806020026020018201604052801561061e5781602001602082028036833780820191505090505b508152602001600067ffffffffffffffff81111561063f5761063e610a31565b5b60405190808252806020026020018201604052801561066d5781602001602082028036833780820191505090505b508152602001600067ffffffffffffffff81111561068e5761068d610a31565b5b6040519080825280602002602001820160405280156106c157816020015b60608152602001906001900390816106ac5790505b508152602001600067ffffffffffffffff8111156106e2576106e1610a31565b5b60405190808252806020026020018201604052801561071557816020015b60608152602001906001900390816107005790505b5081525090505b919050565b82805482825590600052602060002090810192821561079a579160200282015b828111156107995782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555091602001919060010190610741565b5b5090506107a791906108e7565b5090565b8280548282559060005260206000209081019282156107e7579160200282015b828111156107e65782518255916020019190600101906107cb565b5b5090506107f491906108e7565b5090565b828054828255906000526020600020908101928215610840579160200282015b8281111561083f57825182908161082f91906117e9565b5091602001919060010190610818565b5b50905061084d9190610904565b5090565b828054828255906000526020600020908101928215610899579160200282015b828111156108985782518290816108889190611916565b5091602001919060010190610871565b5b5090506108a69190610928565b5090565b6040518060e00160405280600081526020016060815260200160608152602001606081526020016060815260200160608152602001606081525090565b5b808211156109005760008160009055506001016108e8565b5090565b5b80821115610924576000818161091b919061094c565b50600101610905565b5090565b5b80821115610948576000818161093f919061098c565b50600101610929565b5090565b5080546109589061160c565b6000825580601f1061096a5750610989565b601f01602090049060005260206000209081019061098891906108e7565b5b50565b5080546109989061160c565b6000825580601f106109aa57506109c9565b601f0160209004906000526020600020908101906109c891906108e7565b5b50565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b6109f3816109e0565b81146109fe57600080fd5b50565b600081359050610a10816109ea565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610a6982610a20565b810181811067ffffffffffffffff82111715610a8857610a87610a31565b5b80604052505050565b6000610a9b6109cc565b9050610aa78282610a60565b919050565b600067ffffffffffffffff821115610ac757610ac6610a31565b5b610ad082610a20565b9050602081019050919050565b82818337600083830152505050565b6000610aff610afa84610aac565b610a91565b905082815260208101848484011115610b1b57610b1a610a1b565b5b610b26848285610add565b509392505050565b600082601f830112610b4357610b42610a16565b5b8135610b53848260208601610aec565b91505092915050565b600067ffffffffffffffff821115610b7757610b76610a31565b5b602082029050602081019050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610bb882610b8d565b9050919050565b610bc881610bad565b8114610bd357600080fd5b50565b600081359050610be581610bbf565b92915050565b6000610bfe610bf984610b5c565b610a91565b90508083825260208201905060208402830185811115610c2157610c20610b88565b5b835b81811015610c4a5780610c368882610bd6565b845260208401935050602081019050610c23565b5050509392505050565b600082601f830112610c6957610c68610a16565b5b8135610c79848260208601610beb565b91505092915050565b600067ffffffffffffffff821115610c9d57610c9c610a31565b5b602082029050602081019050919050565b6000610cc1610cbc84610c82565b610a91565b90508083825260208201905060208402830185811115610ce457610ce3610b88565b5b835b81811015610d0d5780610cf98882610a01565b845260208401935050602081019050610ce6565b5050509392505050565b600082601f830112610d2c57610d2b610a16565b5b8135610d3c848260208601610cae565b91505092915050565b600067ffffffffffffffff821115610d6057610d5f610a31565b5b602082029050602081019050919050565b6000610d84610d7f84610d45565b610a91565b90508083825260208201905060208402830185811115610da757610da6610b88565b5b835b81811015610dee57803567ffffffffffffffff811115610dcc57610dcb610a16565b5b808601610dd98982610b2e565b85526020850194505050602081019050610da9565b5050509392505050565b600082601f830112610e0d57610e0c610a16565b5b8135610e1d848260208601610d71565b91505092915050565b600067ffffffffffffffff821115610e4157610e40610a31565b5b602082029050602081019050919050565b600067ffffffffffffffff821115610e6d57610e6c610a31565b5b610e7682610a20565b9050602081019050919050565b6000610e96610e9184610e52565b610a91565b905082815260208101848484011115610eb257610eb1610a1b565b5b610ebd848285610add565b509392505050565b600082601f830112610eda57610ed9610a16565b5b8135610eea848260208601610e83565b91505092915050565b6000610f06610f0184610e26565b610a91565b90508083825260208201905060208402830185811115610f2957610f28610b88565b5b835b81811015610f7057803567ffffffffffffffff811115610f4e57610f4d610a16565b5b808601610f5b8982610ec5565b85526020850194505050602081019050610f2b565b5050509392505050565b600082601f830112610f8f57610f8e610a16565b5b8135610f9f848260208601610ef3565b91505092915050565b600080600080600080600060e0888a031215610fc757610fc66109d6565b5b6000610fd58a828b01610a01565b975050602088013567ffffffffffffffff811115610ff657610ff56109db565b5b6110028a828b01610b2e565b965050604088013567ffffffffffffffff811115611023576110226109db565b5b61102f8a828b01610b2e565b955050606088013567ffffffffffffffff8111156110505761104f6109db565b5b61105c8a828b01610c54565b945050608088013567ffffffffffffffff81111561107d5761107c6109db565b5b6110898a828b01610d17565b93505060a088013567ffffffffffffffff8111156110aa576110a96109db565b5b6110b68a828b01610df8565b92505060c088013567ffffffffffffffff8111156110d7576110d66109db565b5b6110e38a828b01610f7a565b91505092959891949750929550565b600060208284031215611108576111076109d6565b5b600061111684828501610a01565b91505092915050565b611128816109e0565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561116857808201518184015260208101905061114d565b83811115611177576000848401525b50505050565b60006111888261112e565b6111928185611139565b93506111a281856020860161114a565b6111ab81610a20565b840191505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6111eb81610bad565b82525050565b60006111fd83836111e2565b60208301905092915050565b6000602082019050919050565b6000611221826111b6565b61122b81856111c1565b9350611236836111d2565b8060005b8381101561126757815161124e88826111f1565b975061125983611209565b92505060018101905061123a565b5085935050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60006112ac838361111f565b60208301905092915050565b6000602082019050919050565b60006112d082611274565b6112da818561127f565b93506112e583611290565b8060005b838110156113165781516112fd88826112a0565b9750611308836112b8565b9250506001810190506112e9565b5085935050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600061135b838361117d565b905092915050565b6000602082019050919050565b600061137b82611323565b611385818561132e565b9350836020820285016113978561133f565b8060005b858110156113d357848403895281516113b4858261134f565b94506113bf83611363565b925060208a0199505060018101905061139b565b50829750879550505050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600081519050919050565b600082825260208201905092915050565b600061143882611411565b611442818561141c565b935061145281856020860161114a565b61145b81610a20565b840191505092915050565b6000611472838361142d565b905092915050565b6000602082019050919050565b6000611492826113e5565b61149c81856113f0565b9350836020820285016114ae85611401565b8060005b858110156114ea57848403895281516114cb8582611466565b94506114d68361147a565b925060208a019950506001810190506114b2565b50829750879550505050505092915050565b600060e083016000830151611514600086018261111f565b506020830151848203602086015261152c828261117d565b91505060408301518482036040860152611546828261117d565b915050606083015184820360608601526115608282611216565b9150506080830151848203608086015261157a82826112c5565b91505060a083015184820360a08601526115948282611370565b91505060c083015184820360c08601526115ae8282611487565b9150508091505092915050565b600060208201905081810360008301526115d581846114fc565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061162457607f821691505b602082108103611637576116366115dd565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b60006008830261169f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82611662565b6116a98683611662565b95508019841693508086168417925050509392505050565b6000819050919050565b60006116e66116e16116dc846109e0565b6116c1565b6109e0565b9050919050565b6000819050919050565b611700836116cb565b61171461170c826116ed565b84845461166f565b825550505050565b600090565b61172961171c565b6117348184846116f7565b505050565b5b818110156117585761174d600082611721565b60018101905061173a565b5050565b601f82111561179d5761176e8161163d565b61177784611652565b81016020851015611786578190505b61179a61179285611652565b830182611739565b50505b505050565b600082821c905092915050565b60006117c0600019846008026117a2565b1980831691505092915050565b60006117d983836117af565b9150826002028217905092915050565b6117f28261112e565b67ffffffffffffffff81111561180b5761180a610a31565b5b611815825461160c565b61182082828561175c565b600060209050601f8311600181146118535760008415611841578287015190505b61184b85826117cd565b8655506118b3565b601f1984166118618661163d565b60005b8281101561188957848901518255600182019150602085019450602081019050611864565b868310156118a657848901516118a2601f8916826117af565b8355505b6001600288020188555050505b505050505050565b60008190508160005260206000209050919050565b601f821115611911576118e2816118bb565b6118eb84611652565b810160208510156118fa578190505b61190e61190685611652565b830182611739565b50505b505050565b61191f82611411565b67ffffffffffffffff81111561193857611937610a31565b5b611942825461160c565b61194d8282856118d0565b600060209050601f831160018114611980576000841561196e578287015190505b61197885826117cd565b8655506119e0565b601f19841661198e866118bb565b60005b828110156119b657848901518255600182019150602085019450602081019050611991565b868310156119d357848901516119cf601f8916826117af565b8355505b6001600288020188555050505b50505050505056fea26469706673582212201d571f701f84db94a39e107bb15ae56c5d73c1c37a07b755e0d36211a2eed7f864736f6c634300080f0033\"},\"caller.sol:caller\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"queryProp\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"propContract\",\"type\":\"address\"}],\"name\":\"setPropContract\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"govshuttleContract\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"bin\":\"608060405234801561001057600080fd5b50610cde806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80633fa7b4e81461004657806363abc2c314610064578063e26e848214610080575b600080fd5b61004e61009c565b60405161005b9190610360565b60405180910390f35b61007e600480360381019061007991906103c5565b6100c0565b005b61009a6004803603810190610095919061041e565b610283565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905060008173ffffffffffffffffffffffffffffffffffffffff1663f0e147b8846040518263ffffffff1660e01b8152600401610121919061045a565b600060405180830381865afa15801561013e573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906101679190610b5d565b905060008160a0015160008151811061018357610182610ba6565b5b6020026020010151805190602001208260c001516000815181106101aa576101a9610ba6565b5b60200260200101516040516020016101c3929190610c69565b604051602081830303815290604052905081606001516000815181106101ec576101eb610ba6565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16826080015160008151811061022257610221610ba6565b5b6020026020010151826040516102389190610c91565b60006040518083038185875af1925050503d8060008114610275576040519150601f19603f3d011682016040523d82523d6000602084013e61027a565b606091505b50505050505050565b600073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146102dc57600080fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061034a8261031f565b9050919050565b61035a8161033f565b82525050565b60006020820190506103756000830184610351565b92915050565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b6103a28161038f565b81146103ad57600080fd5b50565b6000813590506103bf81610399565b92915050565b6000602082840312156103db576103da610385565b5b60006103e9848285016103b0565b91505092915050565b6103fb8161033f565b811461040657600080fd5b50565b600081359050610418816103f2565b92915050565b60006020828403121561043457610433610385565b5b600061044284828501610409565b91505092915050565b6104548161038f565b82525050565b600060208201905061046f600083018461044b565b92915050565b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6104c38261047a565b810181811067ffffffffffffffff821117156104e2576104e161048b565b5b80604052505050565b60006104f561037b565b905061050182826104ba565b919050565b600080fd5b60008151905061051a81610399565b92915050565b600080fd5b600080fd5b600067ffffffffffffffff8211156105455761054461048b565b5b61054e8261047a565b9050602081019050919050565b60005b8381101561057957808201518184015260208101905061055e565b83811115610588576000848401525b50505050565b60006105a161059c8461052a565b6104eb565b9050828152602081018484840111156105bd576105bc610525565b5b6105c884828561055b565b509392505050565b600082601f8301126105e5576105e4610520565b5b81516105f584826020860161058e565b91505092915050565b600067ffffffffffffffff8211156106195761061861048b565b5b602082029050602081019050919050565b600080fd5b60008151905061063e816103f2565b92915050565b6000610657610652846105fe565b6104eb565b9050808382526020820190506020840283018581111561067a5761067961062a565b5b835b818110156106a3578061068f888261062f565b84526020840193505060208101905061067c565b5050509392505050565b600082601f8301126106c2576106c1610520565b5b81516106d2848260208601610644565b91505092915050565b600067ffffffffffffffff8211156106f6576106f561048b565b5b602082029050602081019050919050565b600061071a610715846106db565b6104eb565b9050808382526020820190506020840283018581111561073d5761073c61062a565b5b835b818110156107665780610752888261050b565b84526020840193505060208101905061073f565b5050509392505050565b600082601f83011261078557610784610520565b5b8151610795848260208601610707565b91505092915050565b600067ffffffffffffffff8211156107b9576107b861048b565b5b602082029050602081019050919050565b60006107dd6107d88461079e565b6104eb565b90508083825260208201905060208402830185811115610800576107ff61062a565b5b835b8181101561084757805167ffffffffffffffff81111561082557610824610520565b5b80860161083289826105d0565b85526020850194505050602081019050610802565b5050509392505050565b600082601f83011261086657610865610520565b5b81516108768482602086016107ca565b91505092915050565b600067ffffffffffffffff82111561089a5761089961048b565b5b602082029050602081019050919050565b600067ffffffffffffffff8211156108c6576108c561048b565b5b6108cf8261047a565b9050602081019050919050565b60006108ef6108ea846108ab565b6104eb565b90508281526020810184848401111561090b5761090a610525565b5b61091684828561055b565b509392505050565b600082601f83011261093357610932610520565b5b81516109438482602086016108dc565b91505092915050565b600061095f61095a8461087f565b6104eb565b905080838252602082019050602084028301858111156109825761098161062a565b5b835b818110156109c957805167ffffffffffffffff8111156109a7576109a6610520565b5b8086016109b4898261091e565b85526020850194505050602081019050610984565b5050509392505050565b600082601f8301126109e8576109e7610520565b5b81516109f884826020860161094c565b91505092915050565b600060e08284031215610a1757610a16610475565b5b610a2160e06104eb565b90506000610a318482850161050b565b600083015250602082015167ffffffffffffffff811115610a5557610a54610506565b5b610a61848285016105d0565b602083015250604082015167ffffffffffffffff811115610a8557610a84610506565b5b610a91848285016105d0565b604083015250606082015167ffffffffffffffff811115610ab557610ab4610506565b5b610ac1848285016106ad565b606083015250608082015167ffffffffffffffff811115610ae557610ae4610506565b5b610af184828501610770565b60808301525060a082015167ffffffffffffffff811115610b1557610b14610506565b5b610b2184828501610851565b60a08301525060c082015167ffffffffffffffff811115610b4557610b44610506565b5b610b51848285016109d3565b60c08301525092915050565b600060208284031215610b7357610b72610385565b5b600082015167ffffffffffffffff811115610b9157610b9061038a565b5b610b9d84828501610a01565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6000819050919050565b610c1c610c1782610bd5565b610c01565b82525050565b600081519050919050565b600081905092915050565b6000610c4382610c22565b610c4d8185610c2d565b9350610c5d81856020860161055b565b80840191505092915050565b6000610c758285610c0b565b600482019150610c858284610c38565b91508190509392505050565b6000610c9d8284610c38565b91508190509291505056fea26469706673582212207e23adca640af3ebc42115e2da4ac7e5b9658141443bc45f2d21eb2eef06f7c264736f6c634300080f0033",
-"contractName": "caller" 
-}
diff --git a/contracts/csr.go b/contracts/csr.go
deleted file mode 100644
index a299a547..00000000
--- a/contracts/csr.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package contracts
-
-import (
-	_ "embed" // embed compiled smart contract
-	"encoding/json"
-
-	evmtypes "github.com/evmos/ethermint/x/evm/types"
-)
-
-var (
-	//go:embed compiled_contracts/Turnstile.json
-	TurnstileJSON     []byte
-	TurnstileContract evmtypes.CompiledContract
-)
-
-func init() {
-	err := json.Unmarshal(TurnstileJSON, &TurnstileContract)
-	if err != nil {
-		panic(err)
-	}
-
-	if len(TurnstileContract.Bin) == 0 {
-		panic("The turnstile contract was not loaded")
-	}
-}
diff --git a/contracts/erc20.go b/contracts/erc20.go
index e5f488f3..a0387a6c 100644
--- a/contracts/erc20.go
+++ b/contracts/erc20.go
@@ -11,7 +11,7 @@ import (
 )
 
 var (
-	//go:embed compiled_contracts/ERC20MinterBurnerDecimals.json
+	//go:embed compiled/ERC20MinterBurnerDecimals.json
 	ERC20MinterBurnerDecimalsJSON []byte // nolint: golint
 
 	// ERC20MinterBurnerDecimalsContract is the compiled erc20 contract
diff --git a/contracts/erc20DirectBalanceManipulation.go b/contracts/erc20DirectBalanceManipulation.go
index 0238a89f..db3e078d 100644
--- a/contracts/erc20DirectBalanceManipulation.go
+++ b/contracts/erc20DirectBalanceManipulation.go
@@ -13,7 +13,7 @@ import (
 // This is an evil token. Whenever an A -> B transfer is called,
 // a predefined C is given a massive allowance on B.
 var (
-	//go:embed compiled_contracts/ERC20DirectBalanceManipulation.json
+	//go:embed compiled/ERC20DirectBalanceManipulation.json
 	ERC20DirectBalanceManipulationJSON []byte // nolint: golint
 
 	// ERC20DirectBalanceManipulationContract is the compiled erc20 contract
diff --git a/contracts/erc20burnable.go b/contracts/erc20burnable.go
index d5e79449..d04b7633 100644
--- a/contracts/erc20burnable.go
+++ b/contracts/erc20burnable.go
@@ -8,7 +8,7 @@ import (
 )
 
 var (
-	//go:embed compiled_contracts/ERC20Burnable.json
+	//go:embed compiled/ERC20Burnable.json
 	erc20BurnableJSON []byte
 
 	// ERC20BurnableContract is the compiled ERC20Burnable contract
diff --git a/contracts/erc20maliciousdelayed.go b/contracts/erc20maliciousdelayed.go
index 3f38eb38..e51b07b8 100644
--- a/contracts/erc20maliciousdelayed.go
+++ b/contracts/erc20maliciousdelayed.go
@@ -13,7 +13,7 @@ import (
 // This is an evil token. Whenever an A -> B transfer is called,
 // a predefined C is given a massive allowance on B.
 var (
-	//go:embed compiled_contracts/ERC20MaliciousDelayed.json
+	//go:embed compiled/ERC20MaliciousDelayed.json
 	ERC20MaliciousDelayedJSON []byte // nolint: golint
 
 	// ERC20MaliciousDelayedContract is the compiled erc20 contract
diff --git a/contracts/package-lock.json b/contracts/package-lock.json
deleted file mode 100644
index f00733d6..00000000
--- a/contracts/package-lock.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "name": "canto",
-  "version": "1.0.0",
-  "lockfileVersion": 1,
-  "requires": true,
-  "dependencies": {
-    "@openzeppelin/contracts": {
-      "version": "4.4.2",
-      "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.4.2.tgz",
-      "integrity": "sha512-NyJV7sJgoGYqbtNUWgzzOGW4T6rR19FmX1IJgXGdapGPWsuMelGJn9h03nos0iqfforCbCB0iYIR0MtIuIFLLw=="
-    }
-  }
-}
diff --git a/contracts/package.json b/contracts/package.json
deleted file mode 100644
index b40e940d..00000000
--- a/contracts/package.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  "name": "canto",
-  "version": "1.0.0",
-  "description": "<!-- parent:   order: false -->",
-  "main": "index.js",
-  "directories": {
-    "doc": "docs"
-  },
-  "dependencies": {
-    "@openzeppelin/contracts": "^4.4.2"
-  },
-  "devDependencies": {},
-  "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
-  },
-  "repository": {
-    "type": "git",
-    "url": "git+https://github.com/canto/canto.git"
-  },
-  "author": "",
-  "license": "ISC",
-  "bugs": {
-    "url": "https://github.com/canto/canto/issues"
-  },
-  "homepage": "https://github.com/canto/canto#readme"
-}
diff --git a/contracts/turnstile.sol b/contracts/turnstile.sol
deleted file mode 100644
index 2b7b23e3..00000000
--- a/contracts/turnstile.sol
+++ /dev/null
@@ -1,155 +0,0 @@
-// SPDX-License-Identifier: GPLv3
-pragma solidity 0.8.17;
-
-import "@openzeppelin/contracts/access/Ownable.sol";
-import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
-import "@openzeppelin/contracts/utils/Counters.sol";
-
-/// @notice Implementation of CIP-001 https://github.com/Canto-Improvement-Proposals/CIPs/blob/main/CIP-001.md
-/// @dev Every contract is responsible to register itself in the constructor by calling `register(address)`.
-///      If contract is using proxy pattern, it's possible to register retroactively, however past fees will be lost.
-///      Recipient withdraws fees by calling `withdraw(uint256,address,uint256)`.
-contract Turnstile is Ownable, ERC721Enumerable {
-    using Counters for Counters.Counter;
-
-    struct NftData {
-        uint256 tokenId;
-        bool registered;
-    }
-
-    Counters.Counter private _tokenIdTracker;
-
-    /// @notice maps smart contract address to tokenId
-    mapping(address => NftData) public feeRecipient;
-
-    /// @notice maps tokenId to fees earned
-    mapping(uint256 => uint256) public balances;
-
-    event Register(address smartContract, address recipient, uint256 tokenId);
-    event Assign(address smartContract, uint256 tokenId);
-    event Withdraw(uint256 tokenId, address recipient, uint256 feeAmount);
-    event DistributeFees(uint256 tokenId, uint256 feeAmount);
-
-    error NotAnOwner();
-    error NotSmartContract();
-    error AlreadyRegistered();
-    error Unregistered();
-    error InvalidRecipient();
-    error InvalidTokenId();
-    error NothingToWithdraw();
-    error NothingToDistribute();
-
-    /// @dev only owner of _tokenId can call this function
-    modifier onlyNftOwner(uint256 _tokenId) {
-        if (ownerOf(_tokenId) != msg.sender) revert NotAnOwner();
-
-        _;
-    }
-
-    /// @dev only smart contract that is unregistered can call this function
-    modifier onlyUnregistered() {
-        address smartContract = msg.sender;
-
-        if (isRegistered(smartContract)) revert AlreadyRegistered();
-
-        _;
-    }
-
-    constructor() ERC721("Turnstile", "Turnstile") {}
-
-    /// @notice Returns current value of counter used to tokenId of new minted NFTs
-    /// @return current counter value
-    function currentCounterId() external view returns (uint256) {
-        return _tokenIdTracker.current();
-    }
-
-    /// @notice Returns tokenId that collects fees generated by the smart contract
-    /// @param _smartContract address of the smart contract
-    /// @return tokenId that collects fees generated by the smart contract
-    function getTokenId(address _smartContract) external view returns (uint256) {
-        if (!isRegistered(_smartContract)) revert Unregistered();
-
-        return feeRecipient[_smartContract].tokenId;
-    }
-
-    /// @notice Returns true if smart contract is registered to collect fees
-    /// @param _smartContract address of the smart contract
-    /// @return true if smart contract is registered to collect fees, false otherwise
-    function isRegistered(address _smartContract) public view returns (bool) {
-        return feeRecipient[_smartContract].registered;
-    }
-
-    /// @notice Mints ownership NFT that allows the owner to collect fees earned by the smart contract.
-    ///         `msg.sender` is assumed to be a smart contract that earns fees. Only smart contract itself
-    ///         can register a fee receipient.
-    /// @param _recipient recipient of the ownership NFT
-    /// @return tokenId of the ownership NFT that collects fees
-    function register(address _recipient) public onlyUnregistered returns (uint256 tokenId) {
-        address smartContract = msg.sender;
-
-        if (_recipient == address(0)) revert InvalidRecipient();
-
-        tokenId = _tokenIdTracker.current();
-        _mint(_recipient, tokenId);
-        _tokenIdTracker.increment();
-
-        emit Register(smartContract, _recipient, tokenId);
-
-        feeRecipient[smartContract] = NftData({
-            tokenId: tokenId,
-            registered: true
-        });
-    }
-
-    /// @notice Assigns smart contract to existing NFT. That NFT will collect fees generated by the smart contract.
-    ///         Callable only by smart contract itself.
-    /// @param _tokenId tokenId which will collect fees
-    /// @return tokenId of the ownership NFT that collects fees
-    function assign(uint256 _tokenId) public onlyUnregistered returns (uint256) {
-        address smartContract = msg.sender;
-
-        if (!_exists(_tokenId)) revert InvalidTokenId();
-
-        emit Assign(smartContract, _tokenId);
-
-        feeRecipient[smartContract] = NftData({
-            tokenId: _tokenId,
-            registered: true
-        });
-
-        return _tokenId;
-    }
-
-    /// @notice Withdraws earned fees to `_recipient` address. Only callable by NFT owner.
-    /// @param _tokenId token Id
-    /// @param _recipient recipient of fees
-    /// @param _amount amount of fees to withdraw
-    /// @return amount of fees withdrawn
-    function withdraw(uint256 _tokenId, address payable _recipient, uint256 _amount)
-        public
-        onlyNftOwner(_tokenId)
-        returns (uint256)
-    {
-        uint256 earnedFees = balances[_tokenId];
-
-        if (earnedFees == 0 || _amount == 0) revert NothingToWithdraw();
-        if (_amount > earnedFees) _amount = earnedFees;
-
-        balances[_tokenId] = earnedFees - _amount;
-
-        emit Withdraw(_tokenId, _recipient, _amount);
-
-        Address.sendValue(_recipient, _amount);
-
-        return _amount;
-    }
-
-    /// @notice Distributes collected fees to the smart contract. Only callable by owner.
-    /// @param _tokenId NFT that earned fees
-    function distributeFees(uint256 _tokenId) public onlyOwner payable {
-        if (msg.value == 0) revert NothingToDistribute();
-
-        balances[_tokenId] += msg.value;
-        emit DistributeFees(_tokenId, msg.value);
-    }
-}
\ No newline at end of file
diff --git a/scripts/compile-contracts-for-go.sh b/scripts/compile-contracts-for-go.sh
index 55b331ca..75bc327a 100755
--- a/scripts/compile-contracts-for-go.sh
+++ b/scripts/compile-contracts-for-go.sh
@@ -6,7 +6,7 @@ FULL_CONTRACTS_SOURCE="${FULL_CONTRACTS_ROOT}/contracts"
 
 mkdir -p $CONTRACTS_GO_OUTPUT
 
-for f in $(ls ${FULL_CONTRACTS_ARTIFACTS}/ | awk '!/Test/') ; do
+# for f in $(ls ${FULL_CONTRACTS_ARTIFACTS}/ | awk '!/Test/') ; do
     # Uncomment to copy the source file to the contracts directory
     # sourceFile="${FULL_CONTRACTS_SOURCE}/$f"
     # sourceCopy="${CONTRACTS_GO_OUTPUT}/$f"
@@ -15,15 +15,19 @@ for f in $(ls ${FULL_CONTRACTS_ARTIFACTS}/ | awk '!/Test/') ; do
     # Make the contracts/compiled directory
 
     # Get the compiled JSON in solidity/artifacts/contracts/*.sol/*.json, format and output at contracts/compiled/
+    f="ERC20Burnable.sol"
     compiledFile="${FULL_CONTRACTS_ARTIFACTS}/$f/${f%.sol}.json"
     outputFile="${CONTRACTS_GO_OUTPUT}/${f%.sol}.json"
-    echo "Formatting JSON at $compiledFile and copying to $outputFile"
+    echo "Formatting JSON at [$compiledFile] and copying to [$outputFile]"
     # Get the bytecode and strip the leading 0x prefix:
     BYTECODE=$(cat $compiledFile | jq -r '.bytecode')
     BYTECODE=${BYTECODE#0x}
 
     # We need to escape the ABI input, which jq can do for us if we first isolate it
     ABI=$(cat $compiledFile | jq -c '.abi' )
+    echo "ABI: $ABI"
+    echo "\n\n\n\n"
+    echo "Bytecode: $BYTECODE"
     # and then pass it as a variable for use in the output
-    cat $compiledFile | jq --arg abi_escaped ${ABI} --arg bytecode ${BYTECODE} '{ contractName: .contractName, abi: $abi_escaped, bin: $bytecode }' > $outputFile
-done
\ No newline at end of file
+    cat $compiledFile | jq --arg abi_escaped "${ABI}" --arg bytecode "${BYTECODE}" '{ contractName: .contractName, abi: $abi_escaped, bin: $bytecode }' > $outputFile
+# done
\ No newline at end of file
diff --git a/contracts/ERC20Burnable.sol b/solidity/contracts/ERC20Burnable.sol
similarity index 90%
rename from contracts/ERC20Burnable.sol
rename to solidity/contracts/ERC20Burnable.sol
index 695cf090..4be6f3f9 100644
--- a/contracts/ERC20Burnable.sol
+++ b/solidity/contracts/ERC20Burnable.sol
@@ -1,10 +1,10 @@
 // SPDX-License-Identifier: MIT
 // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Burnable.sol)
 
-pragma solidity ^0.8.0;
+pragma solidity 0.8.12;
 
-import "./@openzeppelin/contracts/token/ERC20/ERC20.sol";
-import "./@openzeppelin/contracts/utils/Context.sol";
+import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
+import "@openzeppelin/contracts/utils/Context.sol";
 
 /**
  * @dev Extension of {ERC20} that allows token holders to destroy both their own
diff --git a/contracts/ERC20DirectBalanceManipulation.sol b/solidity/contracts/ERC20DirectBalanceManipulation.sol
similarity index 88%
rename from contracts/ERC20DirectBalanceManipulation.sol
rename to solidity/contracts/ERC20DirectBalanceManipulation.sol
index 54be06b3..9151b82d 100644
--- a/contracts/ERC20DirectBalanceManipulation.sol
+++ b/solidity/contracts/ERC20DirectBalanceManipulation.sol
@@ -1,8 +1,8 @@
 // SPDX-License-Identifier: MIT
 
-pragma solidity ^0.8.0;
+pragma solidity 0.8.12;
 
-import "./@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
+import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
 
 // This is an evil token. Whenever an A -> B transfer is called, half of the amount goes to B
 // and half to a predefined C
diff --git a/contracts/ERC20MaliciousDelayed.sol b/solidity/contracts/ERC20MaliciousDelayed.sol
similarity index 89%
rename from contracts/ERC20MaliciousDelayed.sol
rename to solidity/contracts/ERC20MaliciousDelayed.sol
index 1f825306..352ec9df 100644
--- a/contracts/ERC20MaliciousDelayed.sol
+++ b/solidity/contracts/ERC20MaliciousDelayed.sol
@@ -1,8 +1,8 @@
 // SPDX-License-Identifier: MIT
 
-pragma solidity ^0.8.0;
+pragma solidity 0.8.12;
 
-import "./@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
+import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
 
 // This is an evil token. Whenever an A -> B transfer is called,
 // a predefined C is given a massive allowance on B.
diff --git a/solidity/contracts/ERC20MinterBurnerDecimals.sol b/solidity/contracts/ERC20MinterBurnerDecimals.sol
new file mode 100644
index 00000000..e4b2a2e7
--- /dev/null
+++ b/solidity/contracts/ERC20MinterBurnerDecimals.sol
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.3.2 (token/ERC20/presets/ERC20PresetMinterPauser.sol)
+
+pragma solidity 0.8.12; // Force solidity compliance
+
+import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
+import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
+import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
+import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
+import "@openzeppelin/contracts/utils/Context.sol";
+
+/**
+ * @dev {ERC20} token, including:
+ *
+ *  - ability for holders to burn (destroy) their tokens
+ *  - a minter role that allows for token minting (creation)
+ *  - a pauser role that allows to stop all token transfers
+ *
+ * This contract uses {AccessControl} to lock permissioned functions using the
+ * different roles - head to its documentation for details.
+ *
+ * The account that deploys the contract will be granted the minter and pauser
+ * roles, as well as the default admin role, which will let it grant both minter
+ * and pauser roles to other accounts.
+ */
+contract ERC20MinterBurnerDecimals is
+    Context,
+    AccessControlEnumerable,
+    ERC20Burnable,
+    ERC20Pausable
+{
+    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
+    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
+    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
+    uint8 private _decimals;
+
+    /**
+     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
+     * account that deploys the contract and customizes tokens decimals
+     *
+     * See {ERC20-constructor}.
+     */
+    constructor(
+        string memory name,
+        string memory symbol,
+        uint8 decimals_
+    ) ERC20(name, symbol) {
+        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
+
+        _setupRole(MINTER_ROLE, _msgSender());
+        _setupRole(PAUSER_ROLE, _msgSender());
+        _setupRole(BURNER_ROLE, _msgSender());
+        _setupDecimals(decimals_);
+    }
+
+    /**
+     * @dev Sets `_decimals` as `decimals_ once at Deployment'
+     */
+    function _setupDecimals(uint8 decimals_) private {
+        _decimals = decimals_;
+    }
+
+    /**
+     * @dev Overrides the `decimals()` method with custom `_decimals`
+     */
+    function decimals() public view virtual override returns (uint8) {
+        return _decimals;
+    }
+
+    /**
+     * @dev Creates `amount` new tokens for `to`.
+     *
+     * See {ERC20-_mint}.
+     *
+     * Requirements:
+     *
+     * - the caller must have the `MINTER_ROLE`.
+     */
+    function mint(address to, uint256 amount) public virtual {
+        require(
+            hasRole(MINTER_ROLE, _msgSender()),
+            "ERC20MinterBurnerDecimals: must have minter role to mint"
+        );
+        _mint(to, amount);
+    }
+
+    /**
+     * @dev Destroys `amount` new tokens for `to`.
+     *
+     * See {ERC20-_burn}.
+     *
+     * Requirements:
+     *
+     * - the caller must have the `BURNER_ROLE`.
+     */
+    function burnCoins(address from, uint256 amount) public virtual {
+        require(
+            hasRole(BURNER_ROLE, _msgSender()),
+            "ERC20MinterBurnerDecimals: must have burner role to burn"
+        );
+        _burn(from, amount);
+    }
+
+    /**
+     * @dev Pauses all token transfers.
+     *
+     * See {ERC20Pausable} and {Pausable-_pause}.
+     *
+     * Requirements:
+     *
+     * - the caller must have the `PAUSER_ROLE`.
+     */
+    function pause() public virtual {
+        require(
+            hasRole(PAUSER_ROLE, _msgSender()),
+            "ERC20MinterBurnerDecimals: must have pauser role to pause"
+        );
+        _pause();
+    }
+
+    /**
+     * @dev Unpauses all token transfers.
+     *
+     * See {ERC20Pausable} and {Pausable-_unpause}.
+     *
+     * Requirements:
+     *
+     * - the caller must have the `PAUSER_ROLE`.
+     */
+    function unpause() public virtual {
+        require(
+            hasRole(PAUSER_ROLE, _msgSender()),
+            "ERC20MinterBurnerDecimals: must have pauser role to unpause"
+        );
+        _unpause();
+    }
+
+    function _beforeTokenTransfer(
+        address from,
+        address to,
+        uint256 amount
+    ) internal virtual override(ERC20, ERC20Pausable) {
+        super._beforeTokenTransfer(from, to, amount);
+    }
+}
diff --git a/solidity/contracts/LiquidInfrastructureERC20.sol b/solidity/contracts/LiquidInfrastructureERC20.sol
new file mode 100644
index 00000000..e1965488
--- /dev/null
+++ b/solidity/contracts/LiquidInfrastructureERC20.sol
@@ -0,0 +1,354 @@
+//SPDX-License-Identifier: Apache-2.0
+pragma solidity 0.8.12; // Force solidity compliance
+
+import "@openzeppelin/contracts/utils/math/Math.sol";
+import "@openzeppelin/contracts/access/Ownable.sol";
+import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
+import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
+import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
+import "./LiquidInfrastructureNFT.sol";
+
+/**
+ * @title Liquid Infrastructure ERC20
+ * @author Christian Borst <christian@althea.systems>
+ *
+ * @dev An ERC20 contract used to earn rewards from managed LiquidInfrastructreNFTs.
+ *
+ * A LiquidInfrastructureNFT typically represents some form of infrastructure involved in an Althea pay-per-forward network
+ * which frequently receives payments from peers on the network for performing an automated service (e.g. providing internet).
+ * This LiquidInfrastructureERC20 acts as a convenient aggregation layer to enable dead-simple investment in real-world assets
+ * with automatic revenue accrual. Simply by holding this ERC20 owners are entitled to revenue from the network represented by the token.
+ *
+ * Revenue is gathered from managed LiquidInfrastructureNFTs by the protocol and distributed to token holders on a semi-regular basis,
+ * where there is a minimum number of blocks required to elapse before a new payout to token holders.
+ */
+contract LiquidInfrastructureERC20 is
+    ERC20,
+    ERC20Burnable,
+    Ownable,
+    ERC721Holder
+{
+    event DistributionStarted();
+    event Distribution(address recipient);
+    event DistributionFinished();
+    event WithdrawalStarted();
+    event Withdrawal(address source);
+    event WithdrawalFinished();
+
+    IERC20[] private distributableERC20s;
+    uint256[] private erc20EntitlementPerUnit;
+    address[] private holders;
+
+    /**
+     * @notice This is the current version of the contract. Every update to the contract will introduce a new
+     * version, regardless of anticipated compatibility.
+     */
+    uint256 public constant Version = 1;
+
+    /**
+     * @notice This collection holds the managed LiquidInfrastructureNFTs which periodically generate revenue and deliver
+     * the balances to this contract.
+     */
+    address[] public ManagedNFTs;
+
+    /**
+     * @notice Holds the block of the last distribution, used for limiting distribution lock ups
+     */
+    uint256 public LastDistribution;
+
+    /**
+     * @notice Holds the minimum number of blocks required to elapse before a new distribution can begin
+     */
+    uint256 public MinDistributionPeriod;
+
+    /**
+     * @notice When true, locks all transfers, mints, and burns until the current distribution has completed
+     */
+    bool public LockedForDistribution;
+
+    /**
+     * @dev Holds the index into `holders` of the next account owed the current distribution
+     */
+    uint256 internal nextDistributionRecipient;
+
+    /**
+     * @dev Holds the index into `ManagedNFTs` of the next contract to withdraw funds from
+     */
+    uint256 private nextWithdrawal;
+
+    /**
+     * Implements the lock during distributions, adds `to` to the list of holders when needed
+     * @param from token sender
+     * @param to  token receiver
+     * @param amount  amount sent
+     */
+    function _beforeTokenTransfer(
+        address from,
+        address to,
+        uint256 amount
+    ) internal virtual override {
+        require(!LockedForDistribution, "distribution in progress");
+        if (from == address(0)) {
+            _beforeMint(to, amount);
+        }
+        if (to == address(0)) {
+            _beforeBurn(from, amount);
+        }
+        bool exists = (this.balanceOf(to) == 0);
+        if (!exists) {
+            holders.push(to);
+        }
+    }
+
+    /**
+     * TODO: Reevaluate - maybe this should be combined with _beforeBurn()
+     *
+     * Implements an additional lock on minting, ensuring that mints happen after any potential distributions
+     * @param to the receiver of minted tokens
+     * @param amount the amount minted
+     */
+    function _beforeMint(address to, uint256 amount) internal view {
+        require(
+            !_isPastMinDistributionPeriod(),
+            "must distribute before minting"
+        );
+    }
+
+    /**
+     * TODO: Reevaluate - maybe this should be combined with _beforeMint()
+     *
+     * Implements an additional lock on burning, ensuring that burns happen after any potential distributions
+     * @param to the receiver of minted tokens
+     * @param amount the amount minted
+     */
+    function _beforeBurn(address to, uint256 amount) internal view {
+        require(
+            !_isPastMinDistributionPeriod(),
+            "must distribute before burning"
+        );
+    }
+
+    /**
+     * Removes `from` from the list of holders when they no longer hold any balance
+     * @param from token sender
+     * @param to  token receiver
+     * @param amount  amount sent
+     */
+    function _afterTokenTransfer(
+        address from,
+        address to,
+        uint256 amount
+    ) internal virtual override {
+        bool stillHolding = (this.balanceOf(from) == 0);
+        if (!stillHolding) {
+            for (uint i = 0; i < holders.length; i++) {
+                if (holders[i] == from) {
+                    // Remove the element at i by copying the last one into its place and removing the last element
+                    holders[i] = holders[holders.length - 1];
+                    holders.pop();
+                }
+            }
+        }
+    }
+
+    /**
+     * Begins or continues a distribution, preventing transfers, mints, and burns of the token until all rewards have been paid out
+     *
+     * @notice distributions may only begin once every MinDistributionPeriod.
+     *
+     * @param numDistributions the number of distributions to process in this execution
+     */
+    function distribute(uint256 numDistributions) public {
+        require(numDistributions > 0, "must process at least 1 distribution");
+        if (!LockedForDistribution) {
+            require(
+                _isPastMinDistributionPeriod(),
+                "MinDistributionPeriod not met"
+            );
+            _beginDistribution();
+        }
+
+        uint256 limit = Math.min(
+            nextDistributionRecipient + numDistributions,
+            holders.length
+        );
+
+        uint i;
+        for (i = nextDistributionRecipient; i < limit; i++) {
+            address recipient = holders[i];
+            for (uint j = 0; j < distributableERC20s.length; j++) {
+                IERC20 toDistribute = IERC20(distributableERC20s[j]);
+                uint256 entitlement = erc20EntitlementPerUnit[j] *
+                    this.balanceOf(recipient);
+                bool success = toDistribute.transferFrom(
+                    address(this),
+                    recipient,
+                    entitlement
+                );
+                require(success, "failed to distribute to recipient");
+            }
+            emit Distribution(recipient);
+        }
+        nextDistributionRecipient = i + 1;
+
+        if (nextDistributionRecipient == holders.length) {
+            _endDistribution();
+        }
+    }
+
+    function _isPastMinDistributionPeriod() internal view returns (bool) {
+        return (block.number - LastDistribution) >= MinDistributionPeriod;
+    }
+
+    /**
+     * Prepares this contract for distribution:
+     * - Locks the contract
+     * - Calculates the entitlement to protocol-held ERC20s per unit of the LiquidInfrastructureERC20 held
+     */
+    function _beginDistribution() internal {
+        LockedForDistribution = true;
+
+        // clear the previous entitlements, if any
+        if (erc20EntitlementPerUnit.length > 0) {
+            delete erc20EntitlementPerUnit;
+        }
+
+        // Calculate the entitlement per token held
+        uint256 supply = this.totalSupply();
+        for (uint i = 0; i < distributableERC20s.length; i++) {
+            uint256 entitlement = IERC20(distributableERC20s[i]).balanceOf(
+                address(this)
+            ) / supply;
+            erc20EntitlementPerUnit.push(entitlement);
+        }
+
+        nextDistributionRecipient = 0;
+        emit DistributionStarted();
+    }
+
+    /**
+     * Unlocks this contract at the end of a distribution
+     */
+    function _endDistribution() internal {
+        delete erc20EntitlementPerUnit;
+        LockedForDistribution = false;
+        emit DistributionFinished();
+    }
+
+    /**
+     * Convenience function that allows the contract owner to distribute when necessary and then mint right after
+     *
+     * @notice attempts to distribute to every holder in this block, which may exceed the block gas limit
+     * if this fails then first call distribute
+     */
+    function mintAndDistribute(
+        address account,
+        uint256 amount
+    ) public onlyOwner {
+        if (_isPastMinDistributionPeriod()) {
+            distribute(holders.length);
+        }
+        mint(account, amount);
+    }
+
+    /**
+     * Allows the contract owner to mint tokens for an address
+     *
+     * @notice minting may only occur when a distribution has happened within MinDistributionPeriod blocks
+     */
+    function mint(address account, uint256 amount) public onlyOwner {
+        _mint(account, amount);
+    }
+
+    /**
+     * Convenience function that allows a token holder to distribute when necessary and then burn their tokens right after
+     *
+     * @notice attempts to distribute to every holder in this block, which may exceed the block gas limit
+     * if this fails then first call distribute() enough times to finish a distribution and then call burn()
+     */
+    function burnAndDistribute(uint256 amount) public {
+        if (_isPastMinDistributionPeriod()) {
+            distribute(holders.length);
+        }
+        burn(amount);
+    }
+
+    /**
+     * Convenience function that allows an approved sender to distribute when necessary and then burn the approved tokens right after
+     *
+     * @notice attempts to distribute to every holder in this block, which may exceed the block gas limit
+     * if this fails then first call distribute() enough times to finish a distribution and then call burnFrom()
+     */
+    function burnFromAndDistribute(address account, uint256 amount) public {
+        if (_isPastMinDistributionPeriod()) {
+            distribute(holders.length);
+        }
+        burnFrom(account, amount);
+    }
+
+    /**
+     * Performs withdrawals from the ManagedNFTs collection, depositing all token balances into the custody of this contract
+     * @param numWithdrawals the number of withdrawals to perform
+     */
+    function withdrawFromManagedNFTs(uint256 numWithdrawals) public {
+        require(!LockedForDistribution, "cannot withdraw during distribution");
+
+        if (nextWithdrawal == 0) {
+            emit WithdrawalStarted();
+        }
+
+        uint256 limit = Math.min(
+            numWithdrawals + nextWithdrawal,
+            ManagedNFTs.length
+        );
+        uint256 i;
+        for (i = nextWithdrawal; i < limit; i++) {
+            LiquidInfrastructureNFT withdrawFrom = LiquidInfrastructureNFT(
+                ManagedNFTs[i]
+            );
+
+            (address[] memory withdrawERC20s, ) = withdrawFrom.getThresholds();
+            withdrawFrom.withdrawBalancesTo(withdrawERC20s, address(this));
+            emit Withdrawal(address(withdrawFrom));
+        }
+        nextWithdrawal = i + 1;
+
+        if (nextWithdrawal == ManagedNFTs.length) {
+            nextWithdrawal = 0;
+            emit WithdrawalFinished();
+        }
+    }
+
+    function addManagedNFT(address nftContract) public onlyOwner {
+        LiquidInfrastructureNFT nft = LiquidInfrastructureNFT(nftContract);
+        address nftOwner = nft.ownerOf(nft.AccountId());
+        require(
+            nftOwner == address(this),
+            "this contract does not own the new ManagedNFT"
+        );
+        ManagedNFTs.push(nftContract);
+    }
+
+    function releaseManagedNFT(
+        address nftContract,
+        address to
+    ) public onlyOwner {
+        LiquidInfrastructureNFT nft = LiquidInfrastructureNFT(nftContract);
+        nft.transferFrom(address(this), to, nft.AccountId());
+    }
+
+    /**
+     * Constructs the underlying ERC20 and initializes critical variables
+     *
+     * @param _managedNFTs The addresses of the controlled LiquidInfrastructureNFT contracts
+     */
+    constructor(
+        string memory _name,
+        string memory _symbol,
+        address[] memory _managedNFTs
+    ) ERC20(_name, _symbol) Ownable() {
+        ManagedNFTs = _managedNFTs;
+        LastDistribution = block.number;
+    }
+}

From 2c4768c7576202cc7b73a25813bab35703f7fc1f Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Fri, 3 Jan 2025 12:22:31 -0500
Subject: [PATCH 27/63] Fix DEX test liquidity initialization for testnet use

---
 integration_tests/test_runner/src/bin/main.rs |  2 +-
 .../test_runner/src/tests/dex.rs              | 89 +++++++++----------
 tests/container-scripts/setup-validators.sh   |  2 +-
 3 files changed, 46 insertions(+), 47 deletions(-)

diff --git a/integration_tests/test_runner/src/bin/main.rs b/integration_tests/test_runner/src/bin/main.rs
index 8b189a4b..ed11de52 100644
--- a/integration_tests/test_runner/src/bin/main.rs
+++ b/integration_tests/test_runner/src/bin/main.rs
@@ -82,7 +82,7 @@ pub async fn main() {
         &web30,
         erc20_addresses.clone(),
         EVM_USER_KEYS.clone(),
-        one_eth() * 60_000u32.into(),
+        one_eth() * 10_000_000u32.into(),
     )
     .await
     .unwrap();
diff --git a/integration_tests/test_runner/src/tests/dex.rs b/integration_tests/test_runner/src/tests/dex.rs
index 113a43dc..b7774159 100644
--- a/integration_tests/test_runner/src/tests/dex.rs
+++ b/integration_tests/test_runner/src/tests/dex.rs
@@ -7,9 +7,10 @@ use crate::dex_utils::{
     croc_policy_ops_resolution, croc_policy_treasury_resolution, croc_query_curve_tick,
     croc_query_dex, croc_query_pool_params, croc_query_pool_template, croc_query_price,
     croc_query_range_position, dex_authority_transfer, dex_direct_protocol_cmd,
-    dex_mint_ambient_pos, dex_mint_ranged_in_amount, dex_mint_ranged_pos, dex_query_authority,
-    dex_query_safe_mode, dex_swap, dex_user_cmd, size_ambient_liq, OpsResolutionArgs,
-    ProtocolCmdArgs, SwapArgs, UserCmdArgs, BOOT_PATH, COLD_PATH, MAX_PRICE, MIN_PRICE, WARM_PATH,
+    dex_mint_ambient_in_amount, dex_mint_ambient_pos, dex_mint_ranged_in_amount,
+    dex_mint_ranged_pos, dex_query_authority, dex_query_safe_mode, dex_swap, dex_user_cmd,
+    size_ambient_liq, OpsResolutionArgs, ProtocolCmdArgs, SwapArgs, UserCmdArgs, BOOT_PATH,
+    COLD_PATH, MAX_PRICE, MIN_PRICE, WARM_PATH,
 };
 use crate::type_urls::{
     COLLECT_TREASURY_PROPOSAL_TYPE_URL, HOT_PATH_OPEN_PROPOSAL_TYPE_URL, OPS_PROPOSAL_TYPE_URL,
@@ -32,6 +33,7 @@ use althea_proto::cosmos_sdk_proto::cosmos::params::v1beta1::{
 };
 use clarity::{Address as EthAddress, PrivateKey, Uint256};
 use deep_space::{Coin, Contact};
+use num::Zero;
 use num256::Int256;
 use num_traits::ToPrimitive;
 use rand::Rng;
@@ -130,16 +132,7 @@ pub async fn populate_pool_basic(
         .await
         .expect("Unable to approve erc20");
     }
-    let tick = croc_query_curve_tick(
-        web3,
-        dex_contracts.query,
-        Some(evm_user.eth_address),
-        base,
-        quote,
-        *POOL_IDX,
-    )
-    .await
-    .expect("Could not get curve tick for pool");
+    let tick = Int256::zero();
 
     let bid_tick = tick - 75u8.into();
     let ask_tick = tick + 75u8.into();
@@ -242,20 +235,9 @@ pub async fn advanced_dex_test(
         walthea,
     )
     .await;
-    let ambient_qty = one_eth();
-    let price_root = croc_query_price(
-        web3,
-        dex_contracts.query,
-        Some(*MINER_ETH_ADDRESS),
-        base,
-        quote,
-        *POOL_IDX,
-    )
-    .await
-    .unwrap();
-    let pr_u128 = price_root.to_u128().unwrap();
-    let liq = size_ambient_liq(ambient_qty.to_u128().unwrap(), true, pr_u128, false);
-    dex_mint_ambient_pos(
+    let ambient_qty = one_eth() * 100000u32.into();
+
+    dex_mint_ambient_in_amount(
         web3,
         dex_contracts.dex,
         dex_contracts.query,
@@ -264,25 +246,17 @@ pub async fn advanced_dex_test(
         base,
         quote,
         *POOL_IDX,
-        liq.into(),
+        ambient_qty,
+        false,
     )
     .await;
 
-    let tick = croc_query_curve_tick(
-        web3,
-        dex_contracts.query,
-        Some(evm_user.eth_address),
-        base,
-        quote,
-        *POOL_IDX,
-    )
-    .await
-    .expect("Could not get curve tick for pool");
+    let tick = Int256::zero();
 
-    let liq = one_eth() * 1024u32.into() / 100u32.into();
+    let liq = one_eth() * 1024u32.into();
     let tick_range = 10240;
     for i in 0..10 {
-        let position_width = tick_range / 10; // Make the positions 1/10th of the total range
+        let position_width = tick_range / 2; // Make the positions 1/2 of the total range
         let tick_offset = tick_range / 10 * i - (tick_range / 2); // Divide into 10 portions, center around the current tick
         let bid_tick = tick + tick_offset.into() - (Int256::from(position_width) / 2i8.into());
         let ask_tick = tick + tick_offset.into() + (Int256::from(position_width) / 2i8.into());
@@ -300,12 +274,13 @@ pub async fn advanced_dex_test(
             bid_tick,
             ask_tick,
             liq,
-            true,
+            false,
         )
         .await;
     }
     info!("Successfully tested DEX");
 }
+
 pub async fn dex_swap_many(
     web3: &Web3,
     evm_user_keys: Vec<EthermintUserKey>,
@@ -791,7 +766,18 @@ async fn swap_many(
             Some(OPERATION_TIMEOUT),
         )
         .await
-        .expect("Unable to swap quote for base");
+        .expect("Unable to swap");
+        let price = croc_query_price(
+            web3,
+            dex_contracts.query,
+            Some(*MINER_ETH_ADDRESS),
+            pool_base,
+            pool_quote,
+            *POOL_IDX,
+        )
+        .await
+        .expect("Unable to query price");
+        info!("Price: {}", price.to_string());
         let post_swap_base = web3
             .get_erc20_balance(pool_base, evm_user.eth_address)
             .await
@@ -810,6 +796,11 @@ async fn swap_many(
                 post_swap_quote > pre_swap_quote,
                 "Swap did not increase quote token balance"
             );
+            info!(
+                "Swapped {} base for {} quote",
+                pre_swap_base - post_swap_base,
+                post_swap_quote - pre_swap_quote
+            );
         } else {
             assert!(
                 post_swap_quote < pre_swap_quote && pre_swap_quote - post_swap_quote == qty,
@@ -819,6 +810,11 @@ async fn swap_many(
                 post_swap_base > pre_swap_base,
                 "Swap did not increase base token balance"
             );
+            info!(
+                "Swapped {} quote for {} base",
+                pre_swap_quote - post_swap_quote,
+                post_swap_base - pre_swap_base,
+            );
         }
 
         if direction.is_none() {
@@ -925,15 +921,18 @@ pub async fn basic_dex_setup(
         info!("Dex is in safe mode, disabling safe mode");
         submit_and_pass_safe_mode_proposal(contact, validator_keys, false, true).await;
     }
-
+    info!(
+        "Evm user native token balance: {}",
+        web3.eth_get_balance(evm_user.eth_address).await.unwrap()
+    );
     if web3
         .get_erc20_balance(walthea, evm_user.eth_address)
         .await
         .unwrap()
-        < one_eth() * 60_000u32.into()
+        < one_eth() * 1_000_000u32.into()
     {
         web3.wrap_eth(
-            one_eth() * 60_000u32.into(),
+            one_eth() * 1_000_000u32.into(),
             evm_user.eth_privkey,
             Some(walthea),
             Some(Duration::from_secs(15)),
diff --git a/tests/container-scripts/setup-validators.sh b/tests/container-scripts/setup-validators.sh
index 8a8d5e62..7674cda3 100755
--- a/tests/container-scripts/setup-validators.sh
+++ b/tests/container-scripts/setup-validators.sh
@@ -29,7 +29,7 @@ EVM_MINER_ALLOCATION="5000000000000000000000000${STAKING_TOKEN}"
 EVM_MINER_ETH_PRIVKEY="b1bab011e03a9862664706fc3bbaa1b16651528e5f0e7fbfcbfdd8be302a13e7"
 EVM_MINER_ADDRESS="althea1hanqss6jsq66tfyjz56wz44z0ejtyv0768q8r4"
 EVM_MINER_ETH_ADDRESS="bf660843528035a5a4921534e156a27e64b231fe"
-EVM_USER_ALLOCATION="500000000000000000000000${STAKING_TOKEN}"
+EVM_USER_ALLOCATION="10000000000000000000000000${STAKING_TOKEN}"
 EVM_USER_MNEMONICS=( \
     "dial point debris employ position cheap inmate nominee crisp grow hello body meadow clever cloth strike agree include dirt tenant hello pattern tattoo option" \
     "poverty inside weasel way rabbit staff initial fire near machine icon favorite simple address skill couple embark acquire asthma deny census flush ensure shiver" \

From 41940309a9fe30b24043216ed744550f23935e51 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Fri, 3 Jan 2025 12:22:52 -0500
Subject: [PATCH 28/63] Update solidity-dex

---
 solidity-dex | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/solidity-dex b/solidity-dex
index 1f970635..4c64755f 160000
--- a/solidity-dex
+++ b/solidity-dex
@@ -1 +1 @@
-Subproject commit 1f970635f0de00ba133b201fafb1f28ee03bcd89
+Subproject commit 4c64755f78e2fbbccc4c34fde3707755a39413e9

From ebb5936385e4f6d9ee90cfeef482f8b0f43f8e82 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Fri, 3 Jan 2025 12:23:13 -0500
Subject: [PATCH 29/63] Make two script tests even easier to run

---
 tests/run-tests.sh | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/tests/run-tests.sh b/tests/run-tests.sh
index ed76acf2..bb29081e 100755
--- a/tests/run-tests.sh
+++ b/tests/run-tests.sh
@@ -2,5 +2,14 @@
 TEST_TYPE=$1
 set -eu
 
+CONTAINER=$(docker ps | grep althea_test_instance | awk '{print $1}')
+echo "Waiting for container to start before starting the test"
+while [ -z "$CONTAINER" ];
+do
+    CONTAINER=$(docker ps | grep althea_test_instance | awk '{print $1}')
+    sleep 1
+done
+echo "Container started, running tests"
+
 # Run test entry point script
 docker exec althea_test_instance /bin/sh -c "pushd /althea/ && tests/container-scripts/integration-tests.sh 1 $TEST_TYPE"
\ No newline at end of file

From 1ede53a6c5d35d5bdb741dd98f1b5e7ccfcff2fa Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Mon, 6 Jan 2025 11:42:46 -0500
Subject: [PATCH 30/63] Add MsgEthereumTx testing to lockup unit test

---
 x/lockup/ante.go      |  2 +-
 x/lockup/ante_test.go | 71 ++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/x/lockup/ante.go b/x/lockup/ante.go
index e1dcaeb7..88578708 100644
--- a/x/lockup/ante.go
+++ b/x/lockup/ante.go
@@ -127,7 +127,7 @@ func (lad LockAnteDecorator) isAcceptable(ctx sdk.Context, msg sdk.Msg) error {
 	if _, typePresent := lockedMsgTypesSet[msgType]; typePresent {
 		// Check that any locked msg is permissible on a type-case basis
 		if allow, err := allowMessage(msg, exemptSet, lockedTokenDenomsSet); !allow {
-			return sdkerrors.Wrap(err, fmt.Sprintf("Transaction blocked because of message %v", msg))
+			return sdkerrors.Wrap(err, "Transaction blocked because of a message")
 		} else {
 			// The user is exempt, allow it to pass
 			return nil
diff --git a/x/lockup/ante_test.go b/x/lockup/ante_test.go
index 8b638750..79a51bbe 100644
--- a/x/lockup/ante_test.go
+++ b/x/lockup/ante_test.go
@@ -2,6 +2,7 @@ package lockup
 
 import (
 	"fmt"
+	"math/big"
 	"testing"
 
 	"github.com/cosmos/cosmos-sdk/client"
@@ -12,8 +13,11 @@ import (
 	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
 	ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
 	ibcclienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
+	"github.com/ethereum/go-ethereum/common"
 	"github.com/stretchr/testify/assert"
 
+	evmtypes "github.com/evmos/ethermint/x/evm/types"
+
 	"github.com/AltheaFoundation/althea-L1/x/lockup/keeper"
 	"github.com/AltheaFoundation/althea-L1/x/lockup/types"
 	microtxtypes "github.com/AltheaFoundation/althea-L1/x/microtx/types"
@@ -21,6 +25,7 @@ import (
 
 func TestLockAnteHandler(t *testing.T) {
 	// Test with the default of locked, only 0x0000.. is exempt, block bank's MsgSend and MsgMultiSend
+	sdk.GetConfig().SetBech32PrefixForAccount("althea", "altheapub")
 	input := keeper.CreateTestEnv(t)
 	ctx := input.Context
 	appCodec := keeper.MakeTestMarshaler()
@@ -35,7 +40,9 @@ func TestLockAnteHandler(t *testing.T) {
 
 	// Lock the chain
 	keeper.SetChainLocked(ctx, true)
-	keeper.SetLockExemptAddresses(ctx, []string{"0x0000000000000000000000000000000000000000"})
+	// 0x0 and its ethermint interpretation
+	lockExemptAddrs := []string{"0x0000000000000000000000000000000000000000", "althea1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq8p93tc"}
+	keeper.SetLockExemptAddresses(ctx, lockExemptAddrs)
 	keeper.SetLockedTokenDenoms(ctx, []string{"aalthea"})
 
 	AnteHandlerLockedHappy(t, handler, keeper, ctx, txCfg, txFct)
@@ -86,6 +93,12 @@ func AnteHandlerLockedHappy(t *testing.T, handler sdk.AnteHandler, keeper keeper
 	assert.Equal(t, ctx, allTransCtx)
 	assert.Nil(t, allTransErr)
 	t.Log("Successful good MsgTransfer")
+
+	allowedMsgEthereumTx := GetAllowedMsgEthereumTxTx(keeper, ctx, txFct, txCfg)
+	allEvmCtx, allEvmErr := handler(ctx, allowedMsgEthereumTx, false)
+	assert.Equal(t, ctx, allEvmCtx)
+	assert.Nil(t, allEvmErr)
+	t.Log("Successful good MsgEthereumTx")
 }
 
 // Test failing messages on a locked chain
@@ -121,6 +134,12 @@ func AnteHandlerLockedUnhappy(t *testing.T, handler sdk.AnteHandler, keeper keep
 	assert.Equal(t, ctx, unallTransCtx)
 	assert.NotNil(t, unallTransErr)
 	t.Log("Successful bad MsgTransfer")
+
+	unallowedMsgEvmTx := GetUnallowedMsgEthereumTxTx(keeper, ctx, txFct, txCfg)
+	unallEvmCtx, unallEvmErr := handler(ctx, unallowedMsgEvmTx, false)
+	assert.Equal(t, ctx, unallEvmCtx)
+	assert.NotNil(t, unallEvmErr)
+	t.Log("Successful bad MsgEthereumTx")
 }
 
 // nolint: dupl
@@ -162,6 +181,12 @@ func AnteHandlerUnlockedHappy(t *testing.T, handler sdk.AnteHandler, keeper keep
 	assert.Equal(t, ctx, unallTransCtx)
 	assert.Nil(t, unallTransErr)
 	t.Log("Successful bad MsgTransfer")
+
+	unallowedMsgEvmTx := GetUnallowedMsgEthereumTxTx(keeper, ctx, txFct, txCfg)
+	unallEvmCtx, unallEvmErr := handler(ctx, unallowedMsgEvmTx, false)
+	assert.Equal(t, ctx, unallEvmCtx)
+	assert.Nil(t, unallEvmErr)
+	t.Log("Successful bad MsgEthereumTx")
 }
 
 func GetAllowedMsgSendTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Factory, txCfg client.TxConfig) sdk.Tx {
@@ -300,6 +325,28 @@ func GetAllowedMsgMicrotx(keeper keeper.Keeper, ctx sdk.Context) microtxtypes.Ms
 	}
 }
 
+func GetAllowedMsgEthereumTx(keeper keeper.Keeper, ctx sdk.Context) evmtypes.MsgEthereumTx {
+	fromAddr := "0x0000000000000000000000000000000000000000"
+	exemptSet := keeper.GetLockExemptAddressesSet(ctx)
+	if _, ok := exemptSet[fromAddr]; !ok {
+		panic(fmt.Sprintf("The exemptSet has been changed, it needs to contain %v", fromAddr))
+	}
+	fromAddress := common.HexToAddress(fromAddr)
+	return *evmtypes.NewTx(big.NewInt(1234), 0, &fromAddress, big.NewInt(1234), 2000000, big.NewInt(1234), big.NewInt(4567), big.NewInt(7890), []byte{}, nil)
+}
+
+func GetAllowedMsgEthereumTxTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Factory, txCfg client.TxConfig) sdk.Tx {
+	msgEvmTx := GetAllowedMsgEthereumTx(keeper, ctx)
+	fromAddr := "0x0000000000000000000000000000000000000000"
+	msgEvmTx.From = fromAddr
+	txBld, err := txFct.BuildUnsignedTx(&msgEvmTx)
+	if err != nil {
+		panic(fmt.Sprintf("Unable to build unsigned transaction containing %v: %v", msgEvmTx, err))
+	}
+
+	return txBld.GetTx()
+}
+
 func GetUnallowedMsgSendTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Factory, txCfg client.TxConfig) sdk.Tx {
 	fromAddr := "0x1111111111111111111111111111111111111111"
 	exemptSet := keeper.GetLockExemptAddressesSet(ctx)
@@ -410,3 +457,25 @@ func GetUnallowedMsgMicrotx(keeper keeper.Keeper, ctx sdk.Context) microtxtypes.
 		Amount:   amount,
 	}
 }
+
+func GetUnallowedMsgEthereumTx(keeper keeper.Keeper, ctx sdk.Context) evmtypes.MsgEthereumTx {
+	fromAddr := "0x1111111111111111111111111111111111111111"
+	exemptSet := keeper.GetLockExemptAddressesSet(ctx)
+	if _, ok := exemptSet[fromAddr]; ok {
+		panic(fmt.Sprintf("The exemptSet has been changed, it MUST NOT contain %v", fromAddr))
+	}
+	fromAddress := common.HexToAddress(fromAddr)
+	return *evmtypes.NewTx(big.NewInt(1234), 0, &fromAddress, big.NewInt(1234), 2000000, big.NewInt(1234), big.NewInt(4567), big.NewInt(7890), []byte{}, nil)
+}
+
+func GetUnallowedMsgEthereumTxTx(keeper keeper.Keeper, ctx sdk.Context, txFct tx.Factory, txCfg client.TxConfig) sdk.Tx {
+	msgEvmTx := GetUnallowedMsgEthereumTx(keeper, ctx)
+	fromAddr := "0x1111111111111111111111111111111111111111"
+	msgEvmTx.From = fromAddr
+	txBld, err := txFct.BuildUnsignedTx(&msgEvmTx)
+	if err != nil {
+		panic(fmt.Sprintf("Unable to build unsigned transaction containing %v: %v", msgEvmTx, err))
+	}
+
+	return txBld.GetTx()
+}

From 1c12f46480cefcaa7d1683745e7576802984ad06 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Mon, 6 Jan 2025 14:16:47 -0500
Subject: [PATCH 31/63] Add MsgEthereumTx testing to LOCKUP test

---
 .../test_runner/src/tests/lockup.rs           | 178 ++++++++++++++----
 .../test_runner/src/type_urls.rs              |   4 +
 2 files changed, 148 insertions(+), 34 deletions(-)

diff --git a/integration_tests/test_runner/src/tests/lockup.rs b/integration_tests/test_runner/src/tests/lockup.rs
index b0122a5c..032bb941 100644
--- a/integration_tests/test_runner/src/tests/lockup.rs
+++ b/integration_tests/test_runner/src/tests/lockup.rs
@@ -1,9 +1,9 @@
 use std::time::{Duration, SystemTime};
 
 use crate::type_urls::{
-    GENERIC_AUTHORIZATION_TYPE_URL, MSG_EXEC_TYPE_URL, MSG_GRANT_TYPE_URL, MSG_MICROTX_TYPE_URL,
-    MSG_MULTI_SEND_TYPE_URL, MSG_SEND_TYPE_URL, MSG_SET_WITHDRAW_ADDRESS_TYPE_URL,
-    MSG_TRANSFER_TYPE_URL,
+    EIP1559_TRANSACTION_DATA_TYPE_URL, GENERIC_AUTHORIZATION_TYPE_URL, MSG_ETHEREUM_TX_TYPE_URL,
+    MSG_EXEC_TYPE_URL, MSG_GRANT_TYPE_URL, MSG_MICROTX_TYPE_URL, MSG_MULTI_SEND_TYPE_URL,
+    MSG_SEND_TYPE_URL, MSG_SET_WITHDRAW_ADDRESS_TYPE_URL, MSG_TRANSFER_TYPE_URL,
 };
 use crate::utils::{
     create_parameter_change_proposal, encode_any, footoken_metadata, get_user_key, one_atom,
@@ -18,10 +18,13 @@ use althea_proto::cosmos_sdk_proto::cosmos::bank::v1beta1::{Input, MsgMultiSend,
 use althea_proto::cosmos_sdk_proto::cosmos::base::v1beta1::Coin as ProtoCoin;
 use althea_proto::cosmos_sdk_proto::cosmos::distribution::v1beta1::MsgSetWithdrawAddress;
 use althea_proto::cosmos_sdk_proto::cosmos::params::v1beta1::ParamChange;
-use clarity::Address as EthAddress;
+use althea_proto::ethermint::evm::v1::{DynamicFeeTx, MsgEthereumTx};
+use clarity::PrivateKey as EthPrivateKey;
 use clarity::Uint256;
+use clarity::{Address as EthAddress, Transaction};
 use deep_space::error::CosmosGrpcError;
-use deep_space::{Address, Coin, Contact, Msg, PrivateKey};
+use deep_space::{Address, Coin, Contact, EthermintPrivateKey, Msg, PrivateKey};
+use num_traits::ToPrimitive;
 use web30::client::Web3;
 
 /// These *_PARAM_KEY constants are defined in x/lockup/types/types.go and must match those values exactly
@@ -44,12 +47,16 @@ pub async fn lockup_test(
     let msg_send_authorized = get_user_key(None);
     let msg_multi_send_authorized = get_user_key(None);
     let msg_microtx_authorized = get_user_key(None);
+    let msg_ethereum_tx_authorized = get_user_key(None);
     fund_lock_exempt_user(contact, &validator_keys, lock_exempt).await;
     fund_authorized_users(
         contact,
         &validator_keys,
-        msg_send_authorized,
-        msg_multi_send_authorized,
+        &[
+            msg_send_authorized,
+            msg_multi_send_authorized,
+            msg_ethereum_tx_authorized,
+        ],
     )
     .await;
     lockup_the_chain(
@@ -61,14 +68,16 @@ pub async fn lockup_test(
     send_evm_tx(evm_user_keys[1], web3, &erc20_addresses, false).await;
     send_evm_tx(evm_user_keys[0], web3, &erc20_addresses, true).await;
 
-    // TODO: Add ibc transfer and check that transfers are blocked outbound for aalthea
     fail_to_send(
         contact,
+        web3,
         &validator_keys,
+        evm_user_keys[0],
         [
             msg_send_authorized,
             msg_multi_send_authorized,
             msg_microtx_authorized,
+            msg_ethereum_tx_authorized,
         ],
     )
     .await;
@@ -79,6 +88,8 @@ pub async fn lockup_test(
     successfully_send(contact, &validator_keys, lock_exempt).await;
     send_evm_tx(evm_user_keys[1], web3, &erc20_addresses, true).await;
     send_evm_tx(evm_user_keys[0], web3, &erc20_addresses, true).await;
+
+    info!("Successfully tested Lockup module");
 }
 
 async fn fund_lock_exempt_user(
@@ -108,36 +119,26 @@ async fn fund_lock_exempt_user(
 async fn fund_authorized_users(
     contact: &Contact,
     validator_keys: &[ValidatorKeys],
-    auth_1: EthermintUserKey,
-    auth_2: EthermintUserKey,
+    auth_users: &[EthermintUserKey],
 ) {
     let sender = validator_keys.first().unwrap().validator_key;
     let amount = Coin {
         denom: STAKING_TOKEN.clone(),
         amount: one_atom(),
     };
-    info!("Funding auth_1 user {}", auth_1.ethermint_address);
-    contact
-        .send_coins(
-            amount.clone(),
-            Some(amount.clone()),
-            auth_1.ethermint_address,
-            Some(OPERATION_TIMEOUT),
-            sender,
-        )
-        .await
-        .expect("Unable to send funds to auth_1 user!");
-    info!("Funding auth_2 user {}", auth_2.ethermint_address);
-    contact
-        .send_coins(
-            amount.clone(),
-            Some(amount),
-            auth_2.ethermint_address,
-            Some(OPERATION_TIMEOUT),
-            sender,
-        )
-        .await
-        .expect("Unable to send funds to auth_2 user!");
+    for (i, auth) in auth_users.iter().enumerate() {
+        info!("Funding auth user {} {}", i, auth.ethermint_address);
+        contact
+            .send_coins(
+                amount.clone(),
+                Some(amount.clone()),
+                auth.ethermint_address,
+                Some(OPERATION_TIMEOUT),
+                sender,
+            )
+            .await
+            .unwrap_or_else(|_| panic!("Unable to send funds to auth user {}!", i));
+    }
 }
 
 pub async fn lockup_the_chain(
@@ -198,8 +199,10 @@ pub fn create_lockup_param_changes(exempt_users: Vec<&EthermintUserKey>) -> Vec<
 
 pub async fn fail_to_send(
     contact: &Contact,
+    web3: &Web3,
     validator_keys: &[ValidatorKeys],
-    authorized_users: [EthermintUserKey; 3],
+    evm_key: EthermintUserKey,
+    authorized_users: [EthermintUserKey; 4],
 ) {
     let sender = validator_keys.first().unwrap().validator_key;
     let receiver = get_user_key(None);
@@ -326,7 +329,7 @@ pub async fn fail_to_send(
         )
         .await;
     res.expect_err("Successfully sent via authz Exec(MsgMultiSend)? Should not be possible!");
-    let msg_microtx_authorized = authorized_users[1];
+    let msg_microtx_authorized = authorized_users[2];
     let authz_msg_microtx = create_authz_microtx_msg_microtx(
         contact,
         sender,
@@ -347,6 +350,16 @@ pub async fn fail_to_send(
         )
         .await;
     res.expect_err("Successfully sent via authz Exec(MsgMicrotx)? Should not be possible!");
+    let msg_ethereum_tx_authorized = authorized_users[3];
+    let _ = create_authz_evm_msg_ethereum_tx(
+        contact,
+        web3,
+        evm_key,
+        msg_ethereum_tx_authorized,
+        amount.amount.parse().unwrap(),
+    )
+    .await
+    .expect_err("Authorization of EVM transaction should fail!");
 }
 
 /// Creates a x/bank MsgSend to transfer `amount` from `sender` to `receiver`
@@ -407,6 +420,61 @@ pub fn create_distribution_msg_set_withdraw_address(
     Msg::new(MSG_SET_WITHDRAW_ADDRESS_TYPE_URL, send)
 }
 
+/// Creates a x/evm MsgEthereumTx to transfer `amount` from `sender` to `receiver`
+pub async fn create_evm_msg_ethereum_tx(
+    web3: &Web3,
+    sender: EthPrivateKey,
+    receiver: EthAddress,
+    amount: Uint256,
+) -> Msg {
+    // Generate a simple Ethereum transaction
+    let evm_tx = web3
+        .prepare_transaction(receiver, vec![], amount, sender, vec![])
+        .await
+        .expect("Unable to generate EVM transaction!");
+    // Now map the EIP1559 transaction to the MsgEthereumTx type via the DynamicFeeTx transaction Data type
+    if let Transaction::Eip1559 {
+        chain_id,
+        nonce,
+        max_priority_fee_per_gas: _,
+        max_fee_per_gas: _,
+        gas_limit,
+        to,
+        value,
+        data,
+        signature,
+        access_list: _,
+    } = evm_tx
+    {
+        let sig = signature.unwrap();
+        let send = MsgEthereumTx {
+            hash: String::new(),
+            from: String::new(),
+            data: Some(encode_any(
+                DynamicFeeTx {
+                    chain_id: chain_id.to_string(),
+                    nonce: nonce.to_u64().unwrap(),
+                    gas_tip_cap: "0".to_string(),
+                    gas_fee_cap: gas_limit.to_string(),
+                    gas: gas_limit.to_u64().unwrap(),
+                    to: to.to_string(),
+                    value: value.to_string(),
+                    data,
+                    accesses: vec![],
+                    v: sig.get_v().to_be_bytes().to_vec(),
+                    r: sig.get_r().to_be_bytes().to_vec(),
+                    s: sig.get_s().to_be_bytes().to_vec(),
+                },
+                EIP1559_TRANSACTION_DATA_TYPE_URL,
+            )),
+            size: 0.0,
+        };
+        Msg::new(MSG_MICROTX_TYPE_URL, send)
+    } else {
+        panic!("Unexpected transaction type!");
+    }
+}
+
 /// Submits an Authorization using x/authz to give the returned private key control over `sender`'s tokens, then crafts
 /// an authz MsgExec-wrapped bank MsgSend and returns that as well
 pub async fn create_authz_bank_msg_send(
@@ -570,6 +638,48 @@ pub async fn create_authz_microtx_msg_microtx(
     Ok(exec_msg)
 }
 
+/// Submits an Authorization using x/authz to give the returned private key control over `sender`'s tokens, then crafts
+/// an authz MsgExec-wrapped evm MsgEthereumTx and returns that as well
+pub async fn create_authz_evm_msg_ethereum_tx(
+    contact: &Contact,
+    web3: &Web3,
+    sender: EthermintUserKey,
+    authorizee: EthermintUserKey,
+    amount: Uint256,
+) -> Result<Msg, CosmosGrpcError> {
+    let grant_msg_ethereum_tx = create_authorization(
+        sender.ethermint_key,
+        authorizee.ethermint_address,
+        MSG_ETHEREUM_TX_TYPE_URL.to_string(),
+    );
+
+    let res = contact
+        .send_message(
+            &[grant_msg_ethereum_tx],
+            None,
+            &[],
+            Some(OPERATION_TIMEOUT),
+            None,
+            sender.ethermint_key,
+        )
+        .await;
+    info!(
+        "Granted MsgEthereumTx authorization with response {:?}",
+        res
+    );
+    res?;
+
+    let evmtx =
+        create_evm_msg_ethereum_tx(web3, sender.eth_privkey, authorizee.eth_address, amount).await;
+    let evmtx_any: prost_types::Any = evmtx.into();
+    let exec = MsgExec {
+        grantee: authorizee.ethermint_address.to_string(),
+        msgs: vec![evmtx_any],
+    };
+    let exec_msg = Msg::new(MSG_EXEC_TYPE_URL, exec);
+
+    Ok(exec_msg)
+}
 /// Creates a MsgGrant to give a GenericAuthorization for `authorizee` to submit any Msg with the given `msg_type_url`
 /// on behalf of `authorizer`
 pub fn create_authorization(
diff --git a/integration_tests/test_runner/src/type_urls.rs b/integration_tests/test_runner/src/type_urls.rs
index 07061779..1f48d2f2 100644
--- a/integration_tests/test_runner/src/type_urls.rs
+++ b/integration_tests/test_runner/src/type_urls.rs
@@ -39,6 +39,10 @@ pub const SOFTWARE_UPGRADE_PROPOSAL_TYPE_URL: &str =
 // ibc-go msgs
 pub const MSG_TRANSFER_TYPE_URL: &str = "/ibc.applications.transfer.v1.MsgTransfer";
 
+// ethermint
+pub const MSG_ETHEREUM_TX_TYPE_URL: &str = "/ethermint.evm.v1.MsgEthereumTx";
+pub const EIP1559_TRANSACTION_DATA_TYPE_URL: &str = "/ethermint.evm.v1.DynamicFeeTx";
+
 // canto
 pub const MSG_CONVERT_ERC20_TYPE_URL: &str = "/canto.erc20.v1.MsgConvertERC20";
 pub const MSG_CONVERT_COIN_TYPE_URL: &str = "/canto.erc20.v1.MsgConvertCoin";

From 1df03d1a1789cdeba44494a46e8c2f49f0ba3e90 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Mon, 6 Jan 2025 19:49:09 -0500
Subject: [PATCH 32/63] Solidity: Update @openzeppelin/contracts to v5.1.0

---
 solidity/package-lock.json | 14 +++++++-------
 solidity/package.json      |  4 ++--
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/solidity/package-lock.json b/solidity/package-lock.json
index affbcc53..7813e4f6 100644
--- a/solidity/package-lock.json
+++ b/solidity/package-lock.json
@@ -18,7 +18,7 @@
         "@ethersproject/bignumber": "^5.0.8",
         "@nomiclabs/hardhat-ethers": "^2.0.0",
         "@nomiclabs/hardhat-waffle": "^2.0.0",
-        "@openzeppelin/contracts": "4.3.1",
+        "@openzeppelin/contracts": "^5.1.0",
         "@typechain/ethers-v5": "^5.0.0",
         "@types/chai": "^4.2.13",
         "@types/command-line-args": "^5.0.0",
@@ -2057,9 +2057,9 @@
       }
     },
     "node_modules/@openzeppelin/contracts": {
-      "version": "4.3.1",
-      "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.3.1.tgz",
-      "integrity": "sha512-QjgbPPlmDK2clK1hzjw2ROfY8KA5q+PfhDUUxZFEBCZP9fi6d5FuNoh/Uq0oCTMEKPmue69vhX2jcl0N/tFKGw==",
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.1.0.tgz",
+      "integrity": "sha512-p1ULhl7BXzjjbha5aqst+QMLY+4/LCWADXOCsmLHRM77AqiPjnd9vvUN9sosUfhL9JGKpZ0TjEGxgvnizmWGSA==",
       "dev": true
     },
     "node_modules/@resolver-engine/core": {
@@ -28545,9 +28545,9 @@
       }
     },
     "@openzeppelin/contracts": {
-      "version": "4.3.1",
-      "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.3.1.tgz",
-      "integrity": "sha512-QjgbPPlmDK2clK1hzjw2ROfY8KA5q+PfhDUUxZFEBCZP9fi6d5FuNoh/Uq0oCTMEKPmue69vhX2jcl0N/tFKGw==",
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.1.0.tgz",
+      "integrity": "sha512-p1ULhl7BXzjjbha5aqst+QMLY+4/LCWADXOCsmLHRM77AqiPjnd9vvUN9sosUfhL9JGKpZ0TjEGxgvnizmWGSA==",
       "dev": true
     },
     "@resolver-engine/core": {
diff --git a/solidity/package.json b/solidity/package.json
index 4a575304..a77e938e 100644
--- a/solidity/package.json
+++ b/solidity/package.json
@@ -19,7 +19,7 @@
     "@ethersproject/bignumber": "^5.0.8",
     "@nomiclabs/hardhat-ethers": "^2.0.0",
     "@nomiclabs/hardhat-waffle": "^2.0.0",
-    "@openzeppelin/contracts": "4.3.1",
+    "@openzeppelin/contracts": "^5.1.0",
     "@typechain/ethers-v5": "^5.0.0",
     "@types/chai": "^4.2.13",
     "@types/command-line-args": "^5.0.0",
@@ -60,4 +60,4 @@
   "dependencies": {
     "pkg": "^4.4.9"
   }
-}
\ No newline at end of file
+}

From dd6ad1b4cb716dccf63a7025d3d2b4d79acb392d Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Mon, 6 Jan 2025 19:52:09 -0500
Subject: [PATCH 33/63] Solidity 0.8.28, update solidity contracts

The solidity compiler version and the contracts have been updated in
order to work with @openzeppelin/contracts v5.1.0
ERC20PresetMinterPauser was copied over from v4.3.1 since it was removed
from the repo in v5
---
 solidity/contracts/ERC20Burnable.sol          |   2 +-
 .../ERC20DirectBalanceManipulation.sol        |   6 +-
 solidity/contracts/ERC20MaliciousDelayed.sol  |   6 +-
 .../contracts/ERC20MinterBurnerDecimals.sol   |  16 +--
 .../contracts/ERC20PresetMinterPauser.sol     | 102 ++++++++++++++++++
 .../contracts/LiquidInfrastructureERC20.sol   |  47 ++++----
 .../contracts/LiquidInfrastructureNFT.sol     |   4 +-
 .../contracts/OwnableApprovableERC721.sol     |   9 +-
 solidity/contracts/TestERC20A.sol             |   2 +-
 solidity/contracts/TestERC20B.sol             |   2 +-
 solidity/contracts/TestERC20C.sol             |   2 +-
 solidity/contracts/TestERC721A.sol            |   2 +-
 solidity/hardhat.config.ts                    |   2 +-
 13 files changed, 151 insertions(+), 51 deletions(-)
 create mode 100644 solidity/contracts/ERC20PresetMinterPauser.sol

diff --git a/solidity/contracts/ERC20Burnable.sol b/solidity/contracts/ERC20Burnable.sol
index 4be6f3f9..28de700a 100644
--- a/solidity/contracts/ERC20Burnable.sol
+++ b/solidity/contracts/ERC20Burnable.sol
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: MIT
 // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Burnable.sol)
 
-pragma solidity 0.8.12;
+pragma solidity 0.8.28;
 
 import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
 import "@openzeppelin/contracts/utils/Context.sol";
diff --git a/solidity/contracts/ERC20DirectBalanceManipulation.sol b/solidity/contracts/ERC20DirectBalanceManipulation.sol
index 9151b82d..3fe7e809 100644
--- a/solidity/contracts/ERC20DirectBalanceManipulation.sol
+++ b/solidity/contracts/ERC20DirectBalanceManipulation.sol
@@ -1,8 +1,8 @@
 // SPDX-License-Identifier: MIT
 
-pragma solidity 0.8.12;
+pragma solidity 0.8.28;
 
-import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
+import "./ERC20PresetMinterPauser.sol";
 
 // This is an evil token. Whenever an A -> B transfer is called, half of the amount goes to B
 // and half to a predefined C
@@ -10,7 +10,7 @@ contract ERC20DirectBalanceManipulation is ERC20PresetMinterPauser {
   address private _thief = 0x4dC6ac40Af078661fc43823086E1513635Eeab14;
   constructor(uint256 initialSupply)
     ERC20PresetMinterPauser("ERC20DirectBalanceManipulation", "ERC20DirectBalanceManipulation") {
-      _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
+      _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
       _mint(msg.sender, initialSupply);
   }
   function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
diff --git a/solidity/contracts/ERC20MaliciousDelayed.sol b/solidity/contracts/ERC20MaliciousDelayed.sol
index 352ec9df..c263abad 100644
--- a/solidity/contracts/ERC20MaliciousDelayed.sol
+++ b/solidity/contracts/ERC20MaliciousDelayed.sol
@@ -1,8 +1,8 @@
 // SPDX-License-Identifier: MIT
 
-pragma solidity 0.8.12;
+pragma solidity 0.8.28;
 
-import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
+import "./ERC20PresetMinterPauser.sol";
 
 // This is an evil token. Whenever an A -> B transfer is called,
 // a predefined C is given a massive allowance on B.
@@ -11,7 +11,7 @@ contract ERC20MaliciousDelayed is ERC20PresetMinterPauser {
   uint256 private _bigNum = 1000000000000000000; // ~uint256(0)
   constructor(uint256 initialSupply)
     ERC20PresetMinterPauser("ERC20MaliciousDelayed", "ERC20MALICIOUSDELAYED") {
-      _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
+      _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
       _mint(msg.sender, initialSupply);
 
   }
diff --git a/solidity/contracts/ERC20MinterBurnerDecimals.sol b/solidity/contracts/ERC20MinterBurnerDecimals.sol
index e4b2a2e7..aac24525 100644
--- a/solidity/contracts/ERC20MinterBurnerDecimals.sol
+++ b/solidity/contracts/ERC20MinterBurnerDecimals.sol
@@ -1,12 +1,12 @@
 // SPDX-License-Identifier: MIT
 // OpenZeppelin Contracts v4.3.2 (token/ERC20/presets/ERC20PresetMinterPauser.sol)
 
-pragma solidity 0.8.12; // Force solidity compliance
+pragma solidity 0.8.28; // Force solidity compliance
 
 import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
 import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
 import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
-import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
+import "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol";
 import "@openzeppelin/contracts/utils/Context.sol";
 
 /**
@@ -45,11 +45,11 @@ contract ERC20MinterBurnerDecimals is
         string memory symbol,
         uint8 decimals_
     ) ERC20(name, symbol) {
-        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
+        _grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
 
-        _setupRole(MINTER_ROLE, _msgSender());
-        _setupRole(PAUSER_ROLE, _msgSender());
-        _setupRole(BURNER_ROLE, _msgSender());
+        _grantRole(MINTER_ROLE, _msgSender());
+        _grantRole(PAUSER_ROLE, _msgSender());
+        _grantRole(BURNER_ROLE, _msgSender());
         _setupDecimals(decimals_);
     }
 
@@ -135,11 +135,11 @@ contract ERC20MinterBurnerDecimals is
         _unpause();
     }
 
-    function _beforeTokenTransfer(
+    function _update(
         address from,
         address to,
         uint256 amount
     ) internal virtual override(ERC20, ERC20Pausable) {
-        super._beforeTokenTransfer(from, to, amount);
+        super._update(from, to, amount);
     }
 }
diff --git a/solidity/contracts/ERC20PresetMinterPauser.sol b/solidity/contracts/ERC20PresetMinterPauser.sol
new file mode 100644
index 00000000..76fc9717
--- /dev/null
+++ b/solidity/contracts/ERC20PresetMinterPauser.sol
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: MIT
+
+// Note: This contract is a copy of a removed OpenZeppelin contract located at
+// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.3.1/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol
+// This contract was removed from recent versions of the openzeppelin-contracts repo in favor of using their contracts wizard.
+// The contract is being copied here so that ERC20DirectBalanceManipulation can work as intended in the tests. The only
+// changes made to it were:
+// * the solidity compiler version
+// * the imports were fixed for the new version of the repo
+// * _setupRole was changed to _grantRole and _beforeTokenTransfer was changed to _update
+
+pragma solidity 0.8.28;
+
+
+import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
+import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
+import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
+import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
+import "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol";
+import "@openzeppelin/contracts/utils/Context.sol";
+
+/**
+ * @dev {ERC20} token, including:
+ *
+ *  - ability for holders to burn (destroy) their tokens
+ *  - a minter role that allows for token minting (creation)
+ *  - a pauser role that allows to stop all token transfers
+ *
+ * This contract uses {AccessControl} to lock permissioned functions using the
+ * different roles - head to its documentation for details.
+ *
+ * The account that deploys the contract will be granted the minter and pauser
+ * roles, as well as the default admin role, which will let it grant both minter
+ * and pauser roles to other accounts.
+ */
+contract ERC20PresetMinterPauser is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable {
+    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
+    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
+
+    /**
+     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
+     * account that deploys the contract.
+     *
+     * See {ERC20-constructor}.
+     */
+    constructor(string memory name, string memory symbol) ERC20(name, symbol) {
+        _grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
+
+        _grantRole(MINTER_ROLE, _msgSender());
+        _grantRole(PAUSER_ROLE, _msgSender());
+    }
+
+    /**
+     * @dev Creates `amount` new tokens for `to`.
+     *
+     * See {ERC20-_mint}.
+     *
+     * Requirements:
+     *
+     * - the caller must have the `MINTER_ROLE`.
+     */
+    function mint(address to, uint256 amount) public virtual {
+        require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint");
+        _mint(to, amount);
+    }
+
+    /**
+     * @dev Pauses all token transfers.
+     *
+     * See {ERC20Pausable} and {Pausable-_pause}.
+     *
+     * Requirements:
+     *
+     * - the caller must have the `PAUSER_ROLE`.
+     */
+    function pause() public virtual {
+        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to pause");
+        _pause();
+    }
+
+    /**
+     * @dev Unpauses all token transfers.
+     *
+     * See {ERC20Pausable} and {Pausable-_unpause}.
+     *
+     * Requirements:
+     *
+     * - the caller must have the `PAUSER_ROLE`.
+     */
+    function unpause() public virtual {
+        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to unpause");
+        _unpause();
+    }
+
+    function _update(
+        address from,
+        address to,
+        uint256 amount
+    ) internal virtual override(ERC20, ERC20Pausable) {
+        super._update(from, to, amount);
+    }
+}
\ No newline at end of file
diff --git a/solidity/contracts/LiquidInfrastructureERC20.sol b/solidity/contracts/LiquidInfrastructureERC20.sol
index e1965488..25685b44 100644
--- a/solidity/contracts/LiquidInfrastructureERC20.sol
+++ b/solidity/contracts/LiquidInfrastructureERC20.sol
@@ -1,5 +1,5 @@
 //SPDX-License-Identifier: Apache-2.0
-pragma solidity 0.8.12; // Force solidity compliance
+pragma solidity 0.8.28; // Force solidity compliance
 
 import "@openzeppelin/contracts/utils/math/Math.sol";
 import "@openzeppelin/contracts/access/Ownable.sol";
@@ -83,11 +83,12 @@ contract LiquidInfrastructureERC20 is
      * @param to  token receiver
      * @param amount  amount sent
      */
-    function _beforeTokenTransfer(
+    function _update(
         address from,
         address to,
         uint256 amount
     ) internal virtual override {
+        // Before transfer
         require(!LockedForDistribution, "distribution in progress");
         if (from == address(0)) {
             _beforeMint(to, amount);
@@ -99,6 +100,22 @@ contract LiquidInfrastructureERC20 is
         if (!exists) {
             holders.push(to);
         }
+
+        // Transfer
+        super._update(from, to, amount);
+
+        // After transfer
+        bool stillHolding = (this.balanceOf(from) == 0);
+        if (!stillHolding) {
+            for (uint i = 0; i < holders.length; i++) {
+                if (holders[i] == from) {
+                    // Remove the element at i by copying the last one into its place and removing the last element
+                    holders[i] = holders[holders.length - 1];
+                    holders.pop();
+                }
+            }
+        }
+
     }
 
     /**
@@ -129,29 +146,6 @@ contract LiquidInfrastructureERC20 is
         );
     }
 
-    /**
-     * Removes `from` from the list of holders when they no longer hold any balance
-     * @param from token sender
-     * @param to  token receiver
-     * @param amount  amount sent
-     */
-    function _afterTokenTransfer(
-        address from,
-        address to,
-        uint256 amount
-    ) internal virtual override {
-        bool stillHolding = (this.balanceOf(from) == 0);
-        if (!stillHolding) {
-            for (uint i = 0; i < holders.length; i++) {
-                if (holders[i] == from) {
-                    // Remove the element at i by copying the last one into its place and removing the last element
-                    holders[i] = holders[holders.length - 1];
-                    holders.pop();
-                }
-            }
-        }
-    }
-
     /**
      * Begins or continues a distribution, preventing transfers, mints, and burns of the token until all rewards have been paid out
      *
@@ -346,8 +340,9 @@ contract LiquidInfrastructureERC20 is
     constructor(
         string memory _name,
         string memory _symbol,
+        address initialOwner,
         address[] memory _managedNFTs
-    ) ERC20(_name, _symbol) Ownable() {
+    ) ERC20(_name, _symbol) Ownable(initialOwner == address(0) ? msg.sender : initialOwner) {
         ManagedNFTs = _managedNFTs;
         LastDistribution = block.number;
     }
diff --git a/solidity/contracts/LiquidInfrastructureNFT.sol b/solidity/contracts/LiquidInfrastructureNFT.sol
index 1f068be2..30cef7ec 100644
--- a/solidity/contracts/LiquidInfrastructureNFT.sol
+++ b/solidity/contracts/LiquidInfrastructureNFT.sol
@@ -1,5 +1,5 @@
 //SPDX-License-Identifier: Apache-2.0
-pragma solidity 0.8.12; // Force solidity compliance
+pragma solidity 0.8.28; // Force solidity compliance
 
 import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
 import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
@@ -29,7 +29,7 @@ import "./OwnableApprovableERC721.sol";
  * will ignore thresholds and transfer all of the Liquid Account's balances to this NFT, which may be withdrawn
  * normally with withdrawBalances().
  */
-contract LiquidInfrastructureNFT is ERC721, OwnableApprovableERC721 {
+contract LiquidInfrastructureNFT is OwnableApprovableERC721 {
     event SuccessfulWithdrawal(address[] erc20s);
     event TryRecover();
     event SuccessfulRecovery(address[] erc20s, uint256[] amounts);
diff --git a/solidity/contracts/OwnableApprovableERC721.sol b/solidity/contracts/OwnableApprovableERC721.sol
index 1f124f42..e286bc0c 100644
--- a/solidity/contracts/OwnableApprovableERC721.sol
+++ b/solidity/contracts/OwnableApprovableERC721.sol
@@ -1,5 +1,5 @@
 //SPDX-License-Identifier: Apache-2.0
-pragma solidity 0.8.12; // Force solidity compliance
+pragma solidity 0.8.28; // Force solidity compliance
 
 import "@openzeppelin/contracts/utils/Context.sol";
 import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
@@ -21,7 +21,8 @@ abstract contract OwnableApprovableERC721 is Context, ERC721 {
      * @dev Throws if called by any account other than the owner.
      */
     modifier onlyOwner(uint256 tokenId) {
-        require(ERC721(this).ownerOf(tokenId) == _msgSender(), "OwnableApprovable: caller is not the owner");
+        address owner = _requireOwned(tokenId);
+        require(owner == _msgSender(), "OwnableApprovable: caller is not the owner");
         _;
     }
 
@@ -29,8 +30,10 @@ abstract contract OwnableApprovableERC721 is Context, ERC721 {
      * @dev Throws if called by any account other than the owner or someone approved by the owner
      */
     modifier onlyOwnerOrApproved(uint256 tokenId) {
+        address owner = _requireOwned(tokenId);
+        address sender = _msgSender();
         // Get approval directly from ERC721's internal method
-        if (_isApprovedOrOwner(_msgSender(), tokenId)) {
+        if (owner == sender || _isAuthorized(owner, sender, tokenId)) {
             _;
         } else {
             revert("OwnableApprovable: caller is not owner nor approved");
diff --git a/solidity/contracts/TestERC20A.sol b/solidity/contracts/TestERC20A.sol
index ce5a96bf..01195179 100644
--- a/solidity/contracts/TestERC20A.sol
+++ b/solidity/contracts/TestERC20A.sol
@@ -1,5 +1,5 @@
 //SPDX-License-Identifier: Apache-2.0
-pragma solidity 0.8.12; // Force solidity compliance
+pragma solidity 0.8.28; // Force solidity compliance
 import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
 
 // One of three testing coins
diff --git a/solidity/contracts/TestERC20B.sol b/solidity/contracts/TestERC20B.sol
index 226a0d22..e0503905 100644
--- a/solidity/contracts/TestERC20B.sol
+++ b/solidity/contracts/TestERC20B.sol
@@ -1,5 +1,5 @@
 //SPDX-License-Identifier: Apache-2.0
-pragma solidity 0.8.12; // Force solidity compliance
+pragma solidity 0.8.28; // Force solidity compliance
 import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
 
 // One of three testing coins
diff --git a/solidity/contracts/TestERC20C.sol b/solidity/contracts/TestERC20C.sol
index f73d887a..1797e761 100644
--- a/solidity/contracts/TestERC20C.sol
+++ b/solidity/contracts/TestERC20C.sol
@@ -1,5 +1,5 @@
 //SPDX-License-Identifier: Apache-2.0
-pragma solidity 0.8.12; // Force solidity compliance
+pragma solidity 0.8.28; // Force solidity compliance
 import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
 
 // One of three testing coins
diff --git a/solidity/contracts/TestERC721A.sol b/solidity/contracts/TestERC721A.sol
index 616654fa..341b049c 100644
--- a/solidity/contracts/TestERC721A.sol
+++ b/solidity/contracts/TestERC721A.sol
@@ -1,5 +1,5 @@
 //SPDX-License-Identifier: Apache-2.0
-pragma solidity 0.8.12; // Force solidity compliance
+pragma solidity 0.8.28; // Force solidity compliance
 import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
 
 // Generate NFTs with token ids 1-10 and 190-195
diff --git a/solidity/hardhat.config.ts b/solidity/hardhat.config.ts
index 5a6f58fd..77d3d6e8 100644
--- a/solidity/hardhat.config.ts
+++ b/solidity/hardhat.config.ts
@@ -28,7 +28,7 @@ task("accounts", "Prints the list of accounts", async (args, hre) => {
 module.exports = {
   // This is a sample solc configuration that specifies which version of solc to use
   solidity: {
-    version: "0.8.12",
+    version: "0.8.28",
     settings: {
       optimizer: {
         enabled: true

From 89ccaad032a01f9f5a5559c8e9de01785dff978f Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Mon, 6 Jan 2025 19:59:39 -0500
Subject: [PATCH 34/63] Solidity: Make ERC20s also ERC20Permit (EIP-2612)

To support permits on all wrapped Cosmos coins, we add ERC20Permit to
the ERC20MinterBurnerDecimals contract. This aids in the support of
gasfree transactions on Althea-L1 by enabling accounts with auto-wrapped
Cosmos coins to sign a relayable transaction with EIP-2612. Ideally
users will be able to transfer from Ethereum to their account on
Althea-L1 via Gravity Bridge without needing any gas on Althea-L1.

Ethereum -> Gravity Bridge -> IBC Auto-Forwarding -> Althea-L1 Cosmos
layer -> x/onboarding auto-wrapping -> Althea-L1 EVM layer

After this flow users should be able to sign a gasfree transaction on
the Althea L1 native DEX to deposit tokens into the DEX and from there
submit other transactions to perform swaps and LP. This should provide
an end-to-end flow where users can transfer an ERC20 from Ethereum
mainnet and purchase ALTHEA tokens on Althea-L1 without a catch-22
requiring them to have ALTHEA to pay for gas.
---
 solidity/contracts/ERC20MinterBurnerDecimals.sol | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/solidity/contracts/ERC20MinterBurnerDecimals.sol b/solidity/contracts/ERC20MinterBurnerDecimals.sol
index aac24525..0991a163 100644
--- a/solidity/contracts/ERC20MinterBurnerDecimals.sol
+++ b/solidity/contracts/ERC20MinterBurnerDecimals.sol
@@ -6,6 +6,7 @@ pragma solidity 0.8.28; // Force solidity compliance
 import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
 import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
 import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
+import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
 import "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol";
 import "@openzeppelin/contracts/utils/Context.sol";
 
@@ -27,7 +28,8 @@ contract ERC20MinterBurnerDecimals is
     Context,
     AccessControlEnumerable,
     ERC20Burnable,
-    ERC20Pausable
+    ERC20Pausable,
+    ERC20Permit
 {
     bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
     bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
@@ -44,7 +46,7 @@ contract ERC20MinterBurnerDecimals is
         string memory name,
         string memory symbol,
         uint8 decimals_
-    ) ERC20(name, symbol) {
+    ) ERC20(name, symbol) ERC20Permit(name) {
         _grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
 
         _grantRole(MINTER_ROLE, _msgSender());

From ea48dc25fbb70e63a66fc71784a3b42b0bfd51b8 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Tue, 7 Jan 2025 15:11:40 -0500
Subject: [PATCH 35/63] Update LiquidInfrastructure contracts from contracts
 repo

---
 .../contracts/LiquidInfrastructureERC20.sol   | 349 ------------------
 .../contracts/LiquidInfrastructureNFT.sol     |  72 +++-
 .../contracts/OwnableApprovableERC721.sol     |  13 +-
 3 files changed, 61 insertions(+), 373 deletions(-)
 delete mode 100644 solidity/contracts/LiquidInfrastructureERC20.sol

diff --git a/solidity/contracts/LiquidInfrastructureERC20.sol b/solidity/contracts/LiquidInfrastructureERC20.sol
deleted file mode 100644
index 25685b44..00000000
--- a/solidity/contracts/LiquidInfrastructureERC20.sol
+++ /dev/null
@@ -1,349 +0,0 @@
-//SPDX-License-Identifier: Apache-2.0
-pragma solidity 0.8.28; // Force solidity compliance
-
-import "@openzeppelin/contracts/utils/math/Math.sol";
-import "@openzeppelin/contracts/access/Ownable.sol";
-import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
-import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
-import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
-import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
-import "./LiquidInfrastructureNFT.sol";
-
-/**
- * @title Liquid Infrastructure ERC20
- * @author Christian Borst <christian@althea.systems>
- *
- * @dev An ERC20 contract used to earn rewards from managed LiquidInfrastructreNFTs.
- *
- * A LiquidInfrastructureNFT typically represents some form of infrastructure involved in an Althea pay-per-forward network
- * which frequently receives payments from peers on the network for performing an automated service (e.g. providing internet).
- * This LiquidInfrastructureERC20 acts as a convenient aggregation layer to enable dead-simple investment in real-world assets
- * with automatic revenue accrual. Simply by holding this ERC20 owners are entitled to revenue from the network represented by the token.
- *
- * Revenue is gathered from managed LiquidInfrastructureNFTs by the protocol and distributed to token holders on a semi-regular basis,
- * where there is a minimum number of blocks required to elapse before a new payout to token holders.
- */
-contract LiquidInfrastructureERC20 is
-    ERC20,
-    ERC20Burnable,
-    Ownable,
-    ERC721Holder
-{
-    event DistributionStarted();
-    event Distribution(address recipient);
-    event DistributionFinished();
-    event WithdrawalStarted();
-    event Withdrawal(address source);
-    event WithdrawalFinished();
-
-    IERC20[] private distributableERC20s;
-    uint256[] private erc20EntitlementPerUnit;
-    address[] private holders;
-
-    /**
-     * @notice This is the current version of the contract. Every update to the contract will introduce a new
-     * version, regardless of anticipated compatibility.
-     */
-    uint256 public constant Version = 1;
-
-    /**
-     * @notice This collection holds the managed LiquidInfrastructureNFTs which periodically generate revenue and deliver
-     * the balances to this contract.
-     */
-    address[] public ManagedNFTs;
-
-    /**
-     * @notice Holds the block of the last distribution, used for limiting distribution lock ups
-     */
-    uint256 public LastDistribution;
-
-    /**
-     * @notice Holds the minimum number of blocks required to elapse before a new distribution can begin
-     */
-    uint256 public MinDistributionPeriod;
-
-    /**
-     * @notice When true, locks all transfers, mints, and burns until the current distribution has completed
-     */
-    bool public LockedForDistribution;
-
-    /**
-     * @dev Holds the index into `holders` of the next account owed the current distribution
-     */
-    uint256 internal nextDistributionRecipient;
-
-    /**
-     * @dev Holds the index into `ManagedNFTs` of the next contract to withdraw funds from
-     */
-    uint256 private nextWithdrawal;
-
-    /**
-     * Implements the lock during distributions, adds `to` to the list of holders when needed
-     * @param from token sender
-     * @param to  token receiver
-     * @param amount  amount sent
-     */
-    function _update(
-        address from,
-        address to,
-        uint256 amount
-    ) internal virtual override {
-        // Before transfer
-        require(!LockedForDistribution, "distribution in progress");
-        if (from == address(0)) {
-            _beforeMint(to, amount);
-        }
-        if (to == address(0)) {
-            _beforeBurn(from, amount);
-        }
-        bool exists = (this.balanceOf(to) == 0);
-        if (!exists) {
-            holders.push(to);
-        }
-
-        // Transfer
-        super._update(from, to, amount);
-
-        // After transfer
-        bool stillHolding = (this.balanceOf(from) == 0);
-        if (!stillHolding) {
-            for (uint i = 0; i < holders.length; i++) {
-                if (holders[i] == from) {
-                    // Remove the element at i by copying the last one into its place and removing the last element
-                    holders[i] = holders[holders.length - 1];
-                    holders.pop();
-                }
-            }
-        }
-
-    }
-
-    /**
-     * TODO: Reevaluate - maybe this should be combined with _beforeBurn()
-     *
-     * Implements an additional lock on minting, ensuring that mints happen after any potential distributions
-     * @param to the receiver of minted tokens
-     * @param amount the amount minted
-     */
-    function _beforeMint(address to, uint256 amount) internal view {
-        require(
-            !_isPastMinDistributionPeriod(),
-            "must distribute before minting"
-        );
-    }
-
-    /**
-     * TODO: Reevaluate - maybe this should be combined with _beforeMint()
-     *
-     * Implements an additional lock on burning, ensuring that burns happen after any potential distributions
-     * @param to the receiver of minted tokens
-     * @param amount the amount minted
-     */
-    function _beforeBurn(address to, uint256 amount) internal view {
-        require(
-            !_isPastMinDistributionPeriod(),
-            "must distribute before burning"
-        );
-    }
-
-    /**
-     * Begins or continues a distribution, preventing transfers, mints, and burns of the token until all rewards have been paid out
-     *
-     * @notice distributions may only begin once every MinDistributionPeriod.
-     *
-     * @param numDistributions the number of distributions to process in this execution
-     */
-    function distribute(uint256 numDistributions) public {
-        require(numDistributions > 0, "must process at least 1 distribution");
-        if (!LockedForDistribution) {
-            require(
-                _isPastMinDistributionPeriod(),
-                "MinDistributionPeriod not met"
-            );
-            _beginDistribution();
-        }
-
-        uint256 limit = Math.min(
-            nextDistributionRecipient + numDistributions,
-            holders.length
-        );
-
-        uint i;
-        for (i = nextDistributionRecipient; i < limit; i++) {
-            address recipient = holders[i];
-            for (uint j = 0; j < distributableERC20s.length; j++) {
-                IERC20 toDistribute = IERC20(distributableERC20s[j]);
-                uint256 entitlement = erc20EntitlementPerUnit[j] *
-                    this.balanceOf(recipient);
-                bool success = toDistribute.transferFrom(
-                    address(this),
-                    recipient,
-                    entitlement
-                );
-                require(success, "failed to distribute to recipient");
-            }
-            emit Distribution(recipient);
-        }
-        nextDistributionRecipient = i + 1;
-
-        if (nextDistributionRecipient == holders.length) {
-            _endDistribution();
-        }
-    }
-
-    function _isPastMinDistributionPeriod() internal view returns (bool) {
-        return (block.number - LastDistribution) >= MinDistributionPeriod;
-    }
-
-    /**
-     * Prepares this contract for distribution:
-     * - Locks the contract
-     * - Calculates the entitlement to protocol-held ERC20s per unit of the LiquidInfrastructureERC20 held
-     */
-    function _beginDistribution() internal {
-        LockedForDistribution = true;
-
-        // clear the previous entitlements, if any
-        if (erc20EntitlementPerUnit.length > 0) {
-            delete erc20EntitlementPerUnit;
-        }
-
-        // Calculate the entitlement per token held
-        uint256 supply = this.totalSupply();
-        for (uint i = 0; i < distributableERC20s.length; i++) {
-            uint256 entitlement = IERC20(distributableERC20s[i]).balanceOf(
-                address(this)
-            ) / supply;
-            erc20EntitlementPerUnit.push(entitlement);
-        }
-
-        nextDistributionRecipient = 0;
-        emit DistributionStarted();
-    }
-
-    /**
-     * Unlocks this contract at the end of a distribution
-     */
-    function _endDistribution() internal {
-        delete erc20EntitlementPerUnit;
-        LockedForDistribution = false;
-        emit DistributionFinished();
-    }
-
-    /**
-     * Convenience function that allows the contract owner to distribute when necessary and then mint right after
-     *
-     * @notice attempts to distribute to every holder in this block, which may exceed the block gas limit
-     * if this fails then first call distribute
-     */
-    function mintAndDistribute(
-        address account,
-        uint256 amount
-    ) public onlyOwner {
-        if (_isPastMinDistributionPeriod()) {
-            distribute(holders.length);
-        }
-        mint(account, amount);
-    }
-
-    /**
-     * Allows the contract owner to mint tokens for an address
-     *
-     * @notice minting may only occur when a distribution has happened within MinDistributionPeriod blocks
-     */
-    function mint(address account, uint256 amount) public onlyOwner {
-        _mint(account, amount);
-    }
-
-    /**
-     * Convenience function that allows a token holder to distribute when necessary and then burn their tokens right after
-     *
-     * @notice attempts to distribute to every holder in this block, which may exceed the block gas limit
-     * if this fails then first call distribute() enough times to finish a distribution and then call burn()
-     */
-    function burnAndDistribute(uint256 amount) public {
-        if (_isPastMinDistributionPeriod()) {
-            distribute(holders.length);
-        }
-        burn(amount);
-    }
-
-    /**
-     * Convenience function that allows an approved sender to distribute when necessary and then burn the approved tokens right after
-     *
-     * @notice attempts to distribute to every holder in this block, which may exceed the block gas limit
-     * if this fails then first call distribute() enough times to finish a distribution and then call burnFrom()
-     */
-    function burnFromAndDistribute(address account, uint256 amount) public {
-        if (_isPastMinDistributionPeriod()) {
-            distribute(holders.length);
-        }
-        burnFrom(account, amount);
-    }
-
-    /**
-     * Performs withdrawals from the ManagedNFTs collection, depositing all token balances into the custody of this contract
-     * @param numWithdrawals the number of withdrawals to perform
-     */
-    function withdrawFromManagedNFTs(uint256 numWithdrawals) public {
-        require(!LockedForDistribution, "cannot withdraw during distribution");
-
-        if (nextWithdrawal == 0) {
-            emit WithdrawalStarted();
-        }
-
-        uint256 limit = Math.min(
-            numWithdrawals + nextWithdrawal,
-            ManagedNFTs.length
-        );
-        uint256 i;
-        for (i = nextWithdrawal; i < limit; i++) {
-            LiquidInfrastructureNFT withdrawFrom = LiquidInfrastructureNFT(
-                ManagedNFTs[i]
-            );
-
-            (address[] memory withdrawERC20s, ) = withdrawFrom.getThresholds();
-            withdrawFrom.withdrawBalancesTo(withdrawERC20s, address(this));
-            emit Withdrawal(address(withdrawFrom));
-        }
-        nextWithdrawal = i + 1;
-
-        if (nextWithdrawal == ManagedNFTs.length) {
-            nextWithdrawal = 0;
-            emit WithdrawalFinished();
-        }
-    }
-
-    function addManagedNFT(address nftContract) public onlyOwner {
-        LiquidInfrastructureNFT nft = LiquidInfrastructureNFT(nftContract);
-        address nftOwner = nft.ownerOf(nft.AccountId());
-        require(
-            nftOwner == address(this),
-            "this contract does not own the new ManagedNFT"
-        );
-        ManagedNFTs.push(nftContract);
-    }
-
-    function releaseManagedNFT(
-        address nftContract,
-        address to
-    ) public onlyOwner {
-        LiquidInfrastructureNFT nft = LiquidInfrastructureNFT(nftContract);
-        nft.transferFrom(address(this), to, nft.AccountId());
-    }
-
-    /**
-     * Constructs the underlying ERC20 and initializes critical variables
-     *
-     * @param _managedNFTs The addresses of the controlled LiquidInfrastructureNFT contracts
-     */
-    constructor(
-        string memory _name,
-        string memory _symbol,
-        address initialOwner,
-        address[] memory _managedNFTs
-    ) ERC20(_name, _symbol) Ownable(initialOwner == address(0) ? msg.sender : initialOwner) {
-        ManagedNFTs = _managedNFTs;
-        LastDistribution = block.number;
-    }
-}
diff --git a/solidity/contracts/LiquidInfrastructureNFT.sol b/solidity/contracts/LiquidInfrastructureNFT.sol
index 30cef7ec..fefbc3c9 100644
--- a/solidity/contracts/LiquidInfrastructureNFT.sol
+++ b/solidity/contracts/LiquidInfrastructureNFT.sol
@@ -1,5 +1,5 @@
 //SPDX-License-Identifier: Apache-2.0
-pragma solidity 0.8.28; // Force solidity compliance
+pragma solidity 0.8.19; // Force solidity compliance
 
 import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
 import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
@@ -10,7 +10,8 @@ import "./OwnableApprovableERC721.sol";
  * @author Christian Borst <christian@althea.systems>
  *
  * @dev An NFT contract used to control a Liquid Infrastructure Account - a Cosmos Bank module account intrinsically connected to the EVM
- * through Althea's x/microtx module.
+ * through Althea's x/microtx module. On chains which are not Althea-L1 this is a standalone ERC721 which should receive revenue periodically,
+ * and the protocol manager must ensure LiquidInfrastructureNFTs receive the revenue they are owed.
  *
  * A Liquid Infrastructure Account typically represents some form of infrastructure involved in an Althea pay-per-forward network
  * which frequently receives payments from peers on the network for performing an automated service (e.g. providing internet).
@@ -29,8 +30,8 @@ import "./OwnableApprovableERC721.sol";
  * will ignore thresholds and transfer all of the Liquid Account's balances to this NFT, which may be withdrawn
  * normally with withdrawBalances().
  */
-contract LiquidInfrastructureNFT is OwnableApprovableERC721 {
-    event SuccessfulWithdrawal(address[] erc20s);
+contract LiquidInfrastructureNFT is ERC721, OwnableApprovableERC721 {
+    event SuccessfulWithdrawal(address to, address[] erc20s, uint256[] amounts);
     event TryRecover();
     event SuccessfulRecovery(address[] erc20s, uint256[] amounts);
     event ThresholdsChanged(address[] newErc20s, uint256[] newAmounts);
@@ -53,14 +54,22 @@ contract LiquidInfrastructureNFT is OwnableApprovableERC721 {
 
     /**
      * Constructs the underlying ERC721 with a URI like "althea://liquid-infrastructure-account/{accountName}", and
-     * a symbol like "TA:{accountName}".
+     * a symbol like "LIA:{accountName}".
      * Mints the Account token (ID=1), the only token held in this NFT.
      *
      * @param accountName The bech32 address of the controlled x/bank account
      */
-    constructor(string memory accountName)
-        ERC721(string.concat("althea://liquid-infrastructure-account/", accountName), string.concat("LIA:", accountName)) {
-
+    constructor(
+        string memory accountName
+    )
+        ERC721(
+            string.concat(
+                "althea://liquid-infrastructure-account/",
+                accountName
+            ),
+            string.concat("LIA:", accountName)
+        )
+    {
         _mint(msg.sender, AccountId);
     }
 
@@ -72,7 +81,12 @@ contract LiquidInfrastructureNFT is OwnableApprovableERC721 {
      * @return address[]: The ERC20 balances to control
      * @return uint256[]: The maximum operating amount of the associated ERC20
      */
-    function getThresholds() public virtual view returns (address[] memory, uint256[] memory) {
+    function getThresholds()
+        public
+        view
+        virtual
+        returns (address[] memory, uint256[] memory)
+    {
         return (thresholdErc20s, thresholdAmounts);
     }
 
@@ -91,13 +105,19 @@ contract LiquidInfrastructureNFT is OwnableApprovableERC721 {
      *
      * @notice this function is access controlled, only the owner or an approved msg.sender may call this function
      */
-    function setThresholds(address[] calldata newErc20s, uint256[] calldata newAmounts) public virtual onlyOwnerOrApproved(AccountId) {
-        require(newErc20s.length == newAmounts.length, "threshold values must have the same length");
+    function setThresholds(
+        address[] calldata newErc20s,
+        uint256[] calldata newAmounts
+    ) public virtual onlyOwnerOrApproved(AccountId) {
+        require(
+            newErc20s.length == newAmounts.length,
+            "threshold values must have the same length"
+        );
         // Clear the thresholds before overwriting
         delete thresholdErc20s;
         delete thresholdAmounts;
 
-        for (uint i=0; i<newErc20s.length; i++) {
+        for (uint i = 0; i < newErc20s.length; i++) {
             thresholdErc20s.push(newErc20s[i]);
             thresholdAmounts.push(newAmounts[i]);
         }
@@ -112,7 +132,11 @@ contract LiquidInfrastructureNFT is OwnableApprovableERC721 {
      *
      * @notice This function is access controlled, only the owner or an approved msg.sender may call this function
      */
-    function withdrawBalances(address[] calldata erc20s) public virtual onlyOwnerOrApproved(AccountId) {
+    function withdrawBalances(address[] calldata erc20s) public virtual {
+        require(
+            _isApprovedOrOwner(_msgSender(), AccountId),
+            "caller is not the owner of the Account token and is not approved either"
+        );
         address destination = ownerOf(AccountId);
         _withdrawBalancesTo(erc20s, destination);
     }
@@ -125,8 +149,15 @@ contract LiquidInfrastructureNFT is OwnableApprovableERC721 {
      * @param destination The address to send all the NFT's ERC20 balances to
      *
      * @notice This function is access controlled, only the owner or an approved msg.sender may call this function
-    */
-    function withdrawBalancesTo(address[] calldata erc20s, address destination) public virtual onlyOwnerOrApproved(AccountId) {
+     */
+    function withdrawBalancesTo(
+        address[] calldata erc20s,
+        address destination
+    ) public virtual {
+        require(
+            _isApprovedOrOwner(_msgSender(), AccountId),
+            "caller is not the owner of the Account token and is not approved either"
+        );
         _withdrawBalancesTo(erc20s, destination);
     }
 
@@ -136,16 +167,21 @@ contract LiquidInfrastructureNFT is OwnableApprovableERC721 {
      * @param erc20s A list of ERC20 tokens to withdraw NFT balances from
      * @param destination The address to send all the NFT's ERC20 balances to
      */
-    function _withdrawBalancesTo(address[] calldata erc20s, address destination) internal {
-        for (uint i=0; i<erc20s.length; i++) {
+    function _withdrawBalancesTo(
+        address[] calldata erc20s,
+        address destination
+    ) internal {
+        uint256[] memory amounts = new uint256[](erc20s.length);
+        for (uint i = 0; i < erc20s.length; i++) {
             address erc20 = erc20s[i];
             uint256 balance = IERC20(erc20).balanceOf(address(this));
             if (balance > 0) {
                 bool result = IERC20(erc20).transfer(destination, balance);
                 require(result, "unsuccessful withdrawal");
+                amounts[i] = balance;
             }
         }
-        emit SuccessfulWithdrawal(erc20s);
+        emit SuccessfulWithdrawal(destination, erc20s, amounts);
     }
 
     /**
diff --git a/solidity/contracts/OwnableApprovableERC721.sol b/solidity/contracts/OwnableApprovableERC721.sol
index e286bc0c..7b7c631d 100644
--- a/solidity/contracts/OwnableApprovableERC721.sol
+++ b/solidity/contracts/OwnableApprovableERC721.sol
@@ -1,8 +1,9 @@
 //SPDX-License-Identifier: Apache-2.0
-pragma solidity 0.8.28; // Force solidity compliance
+pragma solidity 0.8.19; // Force solidity compliance
 
 import "@openzeppelin/contracts/utils/Context.sol";
 import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
+
 /**
  * An abstract contract which provides onlyOwner(id) and onlyOwnerOrApproved(id) modifiers derived from ERC721's
  * onwerOf, getApproved, and isApprovedForAll functions
@@ -21,8 +22,10 @@ abstract contract OwnableApprovableERC721 is Context, ERC721 {
      * @dev Throws if called by any account other than the owner.
      */
     modifier onlyOwner(uint256 tokenId) {
-        address owner = _requireOwned(tokenId);
-        require(owner == _msgSender(), "OwnableApprovable: caller is not the owner");
+        require(
+            ERC721(this).ownerOf(tokenId) == _msgSender(),
+            "OwnableApprovable: caller is not the owner"
+        );
         _;
     }
 
@@ -30,10 +33,8 @@ abstract contract OwnableApprovableERC721 is Context, ERC721 {
      * @dev Throws if called by any account other than the owner or someone approved by the owner
      */
     modifier onlyOwnerOrApproved(uint256 tokenId) {
-        address owner = _requireOwned(tokenId);
-        address sender = _msgSender();
         // Get approval directly from ERC721's internal method
-        if (owner == sender || _isAuthorized(owner, sender, tokenId)) {
+        if (_isApprovedOrOwner(_msgSender(), tokenId)) {
             _;
         } else {
             revert("OwnableApprovable: caller is not owner nor approved");

From 97e0359c376ef493e045c8ca8668918187ad7964 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Tue, 7 Jan 2025 15:42:36 -0500
Subject: [PATCH 36/63] Update LiquidInfrastructure contracts so they compile

---
 .../contracts/LiquidInfrastructureNFT.sol     |  14 +-
 .../contracts/OwnableApprovableERC721.sol     |  13 +-
 solidity/test/liquidAccount.ts                | 278 ------------------
 3 files changed, 9 insertions(+), 296 deletions(-)
 delete mode 100644 solidity/test/liquidAccount.ts

diff --git a/solidity/contracts/LiquidInfrastructureNFT.sol b/solidity/contracts/LiquidInfrastructureNFT.sol
index fefbc3c9..cc21b2fc 100644
--- a/solidity/contracts/LiquidInfrastructureNFT.sol
+++ b/solidity/contracts/LiquidInfrastructureNFT.sol
@@ -1,5 +1,5 @@
 //SPDX-License-Identifier: Apache-2.0
-pragma solidity 0.8.19; // Force solidity compliance
+pragma solidity 0.8.28; // Force solidity compliance
 
 import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
 import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
@@ -132,11 +132,7 @@ contract LiquidInfrastructureNFT is ERC721, OwnableApprovableERC721 {
      *
      * @notice This function is access controlled, only the owner or an approved msg.sender may call this function
      */
-    function withdrawBalances(address[] calldata erc20s) public virtual {
-        require(
-            _isApprovedOrOwner(_msgSender(), AccountId),
-            "caller is not the owner of the Account token and is not approved either"
-        );
+    function withdrawBalances(address[] calldata erc20s) public virtual onlyOwnerOrApproved(AccountId) {
         address destination = ownerOf(AccountId);
         _withdrawBalancesTo(erc20s, destination);
     }
@@ -153,11 +149,7 @@ contract LiquidInfrastructureNFT is ERC721, OwnableApprovableERC721 {
     function withdrawBalancesTo(
         address[] calldata erc20s,
         address destination
-    ) public virtual {
-        require(
-            _isApprovedOrOwner(_msgSender(), AccountId),
-            "caller is not the owner of the Account token and is not approved either"
-        );
+    ) public virtual onlyOwnerOrApproved(AccountId) {
         _withdrawBalancesTo(erc20s, destination);
     }
 
diff --git a/solidity/contracts/OwnableApprovableERC721.sol b/solidity/contracts/OwnableApprovableERC721.sol
index 7b7c631d..e286bc0c 100644
--- a/solidity/contracts/OwnableApprovableERC721.sol
+++ b/solidity/contracts/OwnableApprovableERC721.sol
@@ -1,9 +1,8 @@
 //SPDX-License-Identifier: Apache-2.0
-pragma solidity 0.8.19; // Force solidity compliance
+pragma solidity 0.8.28; // Force solidity compliance
 
 import "@openzeppelin/contracts/utils/Context.sol";
 import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
-
 /**
  * An abstract contract which provides onlyOwner(id) and onlyOwnerOrApproved(id) modifiers derived from ERC721's
  * onwerOf, getApproved, and isApprovedForAll functions
@@ -22,10 +21,8 @@ abstract contract OwnableApprovableERC721 is Context, ERC721 {
      * @dev Throws if called by any account other than the owner.
      */
     modifier onlyOwner(uint256 tokenId) {
-        require(
-            ERC721(this).ownerOf(tokenId) == _msgSender(),
-            "OwnableApprovable: caller is not the owner"
-        );
+        address owner = _requireOwned(tokenId);
+        require(owner == _msgSender(), "OwnableApprovable: caller is not the owner");
         _;
     }
 
@@ -33,8 +30,10 @@ abstract contract OwnableApprovableERC721 is Context, ERC721 {
      * @dev Throws if called by any account other than the owner or someone approved by the owner
      */
     modifier onlyOwnerOrApproved(uint256 tokenId) {
+        address owner = _requireOwned(tokenId);
+        address sender = _msgSender();
         // Get approval directly from ERC721's internal method
-        if (_isApprovedOrOwner(_msgSender(), tokenId)) {
+        if (owner == sender || _isAuthorized(owner, sender, tokenId)) {
             _;
         } else {
             revert("OwnableApprovable: caller is not owner nor approved");
diff --git a/solidity/test/liquidAccount.ts b/solidity/test/liquidAccount.ts
deleted file mode 100644
index 883d4a04..00000000
--- a/solidity/test/liquidAccount.ts
+++ /dev/null
@@ -1,278 +0,0 @@
-import chai from "chai";
-import { ethers } from "hardhat";
-import { solidity } from "ethereum-waffle";
-
-import { deployContracts, deployLiquidAccount, liquidAccountAsNewOwner } from "../test-utils";
-import { BigNumber, ContractTransaction } from "ethers";
-import { TestERC20A, TestERC20B, TestERC20C, LiquidInfrastructureNFT } from "../typechain";
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-
-chai.use(solidity);
-const { expect } = chai;
-
-// This test makes assertions about the LiquidInfrastructureNFT contract by running it on hardhat, this contract
-// is part of a hybrid Cosmos implementation, so it is not possible to test the interactions with the x/microtx
-// module here. In particular, this test asserts the access control offered by OwnableApprovableERC721' modifiers
-//
-// Important test details:
-// Contract interactions happen via hardhat-ethers: https://hardhat.org/hardhat-runner/plugins/nomiclabs-hardhat-ethers
-// Chai is used to make assertions https://www.chaijs.com/api/bdd/
-// Ethereum-waffle is used to extend chai and add ethereum matchers: https://ethereum-waffle.readthedocs.io/en/latest/matchers.html
-async function runTest(opts: {}) {
-  const signers = await ethers.getSigners();
-  const deployer = signers[0];
-  const newOwner = signers[1];
-  const toApprove = signers[2];
-  console.log("deployer", deployer.address, "newOwner", newOwner.address);
-
-  // Deploy a LiquidInfrastructureNFT, subsequent function calls use the deployer as the message signer
-  //////////////////
-  const accountAsDeployer = await deployLiquidAccount(deployer.address);
-  // Enable calls on the LiquidInfrastructureNFT as the future owner
-  const accountAsNewOwner = await liquidAccountAsNewOwner(accountAsDeployer.address, newOwner);
-  // Enable calls as an account which must be approved by the owner
-  const accountToApprove = await liquidAccountAsNewOwner(accountAsDeployer.address, toApprove);
-
-  // Deploy several ERC20 tokens
-  //////////////////
-  const { testERC20A, testERC20B, testERC20C } = await deployContracts(deployer);
-
-  await runOwnerTests(accountAsDeployer, deployer, accountAsNewOwner, newOwner, testERC20A, testERC20B, testERC20C);
-
-  await runApprovalTests(accountAsNewOwner, newOwner, accountAsDeployer, deployer, accountToApprove, toApprove, testERC20A, testERC20B, testERC20C);
-}
-
-// Test based on ownership changes
-async function runOwnerTests(
-  accountAsDeployer: LiquidInfrastructureNFT,
-  deployer: SignerWithAddress,
-  accountAsNewOwner: LiquidInfrastructureNFT,
-  newOwner: SignerWithAddress,
-  testERC20A: TestERC20A,
-  testERC20B: TestERC20B,
-  testERC20C: TestERC20C,
-) {
-  const accountTokenId = await accountAsDeployer.AccountId();
-
-  // owner and future owner balance assertions
-  const owner = await accountAsDeployer.ownerOf(accountTokenId);
-  expect(owner).to.equal(deployer.address);
-  const ownerBalance = await accountAsDeployer.balanceOf(owner);
-  expect(ownerBalance).to.equal(BigNumber.from(1));
-  const futureOwnerBalance = await accountAsDeployer.balanceOf(newOwner.address);
-  expect(futureOwnerBalance).to.equal(BigNumber.from(0));
-
-  // Transfer from the deployer to the owner
-  //////////////////
-  expect(
-    await accountAsDeployer.transferFrom(deployer.address, newOwner.address, accountTokenId)
-  ).to
-  .emit(accountAsDeployer, 'Transfer')
-  .withArgs(deployer.address, newOwner.address, accountTokenId);
-
-  // updated owner and prev owner balance assertions
-  const currOwner = await accountAsDeployer.ownerOf(accountTokenId);
-  expect(currOwner).to.equal(newOwner.address);
-  const currOwnerBalance = await accountAsDeployer.balanceOf(currOwner);
-  expect(currOwnerBalance).to.equal(BigNumber.from(1));
-  const deployerBalance = await accountAsDeployer.balanceOf(deployer.address);
-  expect(deployerBalance).to.equal(BigNumber.from(0));
-
-  // thresholds assertions
-  const [erc20s, amounts] = await accountAsDeployer.getThresholds();
-  expect(erc20s.length).to.equal(0);
-  expect(amounts.length).to.equal(0);
-
-  // Use USDC as an example contract
-  const mainnetUSDC: string = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
-  const expectedThreshold = { amount: BigNumber.from(1000000), erc20: mainnetUSDC};
-  
-  // Fail to call setThresholds with the old owner
-  await expect(accountAsDeployer.setThresholds([expectedThreshold.erc20], [expectedThreshold.amount])).to.be.reverted;
-
-  // Set the thresholds with the new owner and assert the event is correct
-  await checkThresholdsChangedEventArgs(
-    await accountAsNewOwner.setThresholds([expectedThreshold.erc20], [expectedThreshold.amount]),
-    [expectedThreshold.erc20], [expectedThreshold.amount],
-  );
-
-
-  // transfer tokens to the NFT as if this were via x/microtx
-  const erc20Signer = await testERC20A.signer.getAddress();
-  const withdrawalAmount = BigNumber.from(1000000);
-  await sendTestERC20sToAccount(testERC20A, testERC20B, testERC20C, accountAsNewOwner.address, withdrawalAmount);
-
-  await withdrawSomeERC20sAndAssertBalances(accountAsDeployer, accountAsNewOwner, mainnetUSDC, withdrawalAmount, testERC20A, testERC20B, testERC20C);
-
-  await testRecoveryProcessInit(accountAsDeployer, accountAsNewOwner);
-}
-
-// Test approving an account, along with ownership changes revoking an old approval
-async function runApprovalTests(
-  accountAsOwner: LiquidInfrastructureNFT, // Contract with current owner as signer
-  owner: SignerWithAddress, // Current owner
-  accountAsNewOwner: LiquidInfrastructureNFT, // Contract with signer who will become owner
-  newOwner: SignerWithAddress, // Will become owner
-  accountAsToApprove: LiquidInfrastructureNFT, // Contract with account who will be approved by owner
-  toApprove: SignerWithAddress, // Will become approved by owner
-  testERC20A: TestERC20A,
-  testERC20B: TestERC20B,
-  testERC20C: TestERC20C,
-) {
-  const accountTokenId = await accountAsOwner.AccountId();
-  // event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId)
-  await expect(accountAsOwner.approve(toApprove.address, accountTokenId))
-  .to
-  .emit(accountAsOwner, "Approval")
-  .withArgs(owner.address, toApprove.address, accountTokenId);
-
-  // Call all the restricted functions as the approved account
-  const expectedThresholds = [{ amount: BigNumber.from(1000000), erc20: testERC20A.address}, { amount: BigNumber.from(2000000), erc20: testERC20B.address}, { amount: BigNumber.from(3000000), erc20: testERC20C.address}];
-  const expectedThresholdAmounts = expectedThresholds.map((et) => et.amount);
-  const expectedThresholdAddresses = expectedThresholds.map((et) => et.erc20);
-  
-  // Set the thresholds with the new owner and assert the event is correct
-  await checkThresholdsChangedEventArgs(
-    await accountAsToApprove.setThresholds(expectedThresholdAddresses, expectedThresholdAmounts),
-    expectedThresholdAddresses, expectedThresholdAmounts,
-  );
-  // thresholds assertions
-  const [erc20s, amounts] = await accountAsToApprove.getThresholds();
-  expect(erc20s.length).to.equal(amounts.length);
-  expect(erc20s.length).to.equal(expectedThresholdAddresses.length);
-  expect(amounts.length).to.equal(expectedThresholdAmounts.length);
-  for (let i=0; i<erc20s.length; i++) {
-    const actualErc20 = erc20s[i];
-    const actualAmt = amounts[i];
-    const expErc20 = expectedThresholdAddresses[i];
-    const expAmt = expectedThresholdAmounts[i];
-    expect(actualErc20).to.equal(expErc20);
-    expect(actualAmt).to.equal(expAmt);
-  }
-
-  const withdrawalAmount = BigNumber.from(1000000000);
-  await sendTestERC20sToAccount(testERC20A, testERC20B, testERC20C, accountAsNewOwner.address, withdrawalAmount);
-  await withdrawSomeERC20sAndAssertBalances(accountAsNewOwner, accountAsToApprove, newOwner.address, withdrawalAmount, testERC20A, testERC20B, testERC20C);
-
-  // Now transfer to the new owner and assert that the account approved by the old sender is no longer approved
-  expect(
-    await accountAsOwner.transferFrom(owner.address, newOwner.address, accountTokenId)
-  ).to
-  .emit(accountAsOwner, 'Transfer')
-  .withArgs(owner.address, newOwner.address, accountTokenId);
-
-  const newExpectedThresholds = [{ amount: BigNumber.from(2000000), erc20: testERC20A.address}];
-  const newExpectedThresholdAmounts = newExpectedThresholds.map((et) => et.amount);
-  const newExpectedThresholdAddresses = newExpectedThresholds.map((et) => et.erc20);
-  
-  // Set the thresholds with the new owner and assert the event is correct
-  await expect(accountAsToApprove.setThresholds(newExpectedThresholdAddresses, newExpectedThresholdAmounts)).to.be.reverted;
-
-  // Use the old approver as the bad signer in withdrawal and recovery
-  await sendTestERC20sToAccount(testERC20A, testERC20B, testERC20C, accountAsNewOwner.address, withdrawalAmount);
-  await withdrawSomeERC20sAndAssertBalances(accountAsToApprove, accountAsNewOwner, toApprove.address, withdrawalAmount, testERC20A, testERC20B, testERC20C);
-  await testRecoveryProcessInit(accountAsToApprove, accountAsNewOwner);
-}
-
-// Checks that the result of setting the thresholds is successful and emits an event with the right array values in it
-// Call with the result of liquidaccount.setThresholds() and the passed threshold values
-async function checkThresholdsChangedEventArgs(tx: ContractTransaction, expectedErc20s: string[], expectedAmounts: BigNumber[]) {
-  const receipt = await ethers.provider.getTransactionReceipt(tx.hash);
-  const iface = new ethers.utils.Interface(["event ThresholdsChanged(address[] newErc20s,uint256[] newAmounts)"]);
-  const data = receipt.logs[0].data;
-  const topics = receipt.logs[0].topics;
-  const event = iface.decodeEventLog("ThresholdsChanged", data, topics);
-
-  const actualErc20s = event["newErc20s"];
-  const actualAmounts = event["newAmounts"];
-
-  expect(actualErc20s.length == expectedErc20s.length);
-  expect(actualAmounts.length == expectedAmounts.length);
-  for (let i=0; i<actualErc20s.length; i++) {
-    const expectedErc20 = expectedErc20s[i];
-    const actualErc20 = actualErc20s[i];
-    expect(expectedErc20).to.equal(actualErc20);
-    const expectedAmt = expectedAmounts[i];
-    const actualAmt = actualAmounts[i];
-    expect(expectedAmt).to.equal(actualAmt);
-  }
-}
-
-// Sends `transferAmount` of each test ERC20 to `reciever`
-async function sendTestERC20sToAccount(testERC20A: TestERC20A, testERC20B: TestERC20B, testERC20C: TestERC20C, receiver: string, transferAmount: BigNumber) {
-  const initialBalances = {A: await testERC20A.balanceOf(receiver), B: await testERC20B.balanceOf(receiver), C: await testERC20C.balanceOf(receiver) };
-  await testERC20A.transfer(receiver, transferAmount);
-  await testERC20B.transfer(receiver, transferAmount);
-  await testERC20C.transfer(receiver, transferAmount);
-
-  expect(await testERC20A.balanceOf(receiver)).to.equal(transferAmount.add(initialBalances.A));
-  expect(await testERC20B.balanceOf(receiver)).to.equal(transferAmount.add(initialBalances.B));
-  expect(await testERC20C.balanceOf(receiver)).to.equal(transferAmount.add(initialBalances.C));
-}
-
-// Withdraws `withdrawalAmount` of testERC20A and testERC20C from the Liquid Infrastructure Account to `withdrawalReceiver`,
-// asserting events and balances change (or don't) as expected. accountBadSender is used to test access control
-// failure, while accountGoodSender is used for the happy path testing.
-async function withdrawSomeERC20sAndAssertBalances(
-  accountBadSender: LiquidInfrastructureNFT,
-  accountGoodSender: LiquidInfrastructureNFT,
-  withdrawalReceiver: string,
-  withdrawalAmount: BigNumber,
-  testERC20A: TestERC20A,
-  testERC20B: TestERC20B,
-  testERC20C: TestERC20C,
-) {
-  const initialBalances = {A: await testERC20A.balanceOf(accountGoodSender.address), B: await testERC20B.balanceOf(accountGoodSender.address), C: await testERC20C.balanceOf(accountGoodSender.address) };
-  const receiverInitialBalances = {A: await testERC20A.balanceOf(withdrawalReceiver), B: await testERC20B.balanceOf(withdrawalReceiver), C: await testERC20C.balanceOf(withdrawalReceiver) };
-  const erc20sToWithdraw = [testERC20A.address, testERC20C.address];
-
-  // Reverted when not sent as current owner
-  await expect(accountBadSender.withdrawBalancesTo(erc20sToWithdraw, withdrawalReceiver)).to.be.reverted;
-  await expect(accountBadSender.withdrawBalances(erc20sToWithdraw)).to.be.reverted;
-
-  // Successful event emitted when sent as good owner
-  await expect(accountGoodSender.withdrawBalancesTo(erc20sToWithdraw, withdrawalReceiver))
-  .to
-  .emit(accountGoodSender, "SuccessfulWithdrawal")
-  .withArgs(erc20sToWithdraw);
-
-  expect(await testERC20A.balanceOf(withdrawalReceiver)).to.equal(withdrawalAmount.add(receiverInitialBalances.A));
-  expect(await testERC20A.balanceOf(accountGoodSender.address)).to.equal(initialBalances.A.sub(withdrawalAmount));
-
-  // Nothing happened with B, should not see any balance changes
-  expect(await testERC20B.balanceOf(withdrawalReceiver)).to.equal(receiverInitialBalances.B);
-  expect(await testERC20B.balanceOf(accountGoodSender.address)).to.equal(initialBalances.B);
-
-  expect(await testERC20C.balanceOf(withdrawalReceiver)).to.equal(withdrawalAmount.add(receiverInitialBalances.C));
-  expect(await testERC20C.balanceOf(accountGoodSender.address)).to.equal(initialBalances.C.sub(withdrawalAmount));
-
-  // Test the withdraw to owner function with just TestERC20B
-  const accountId = await accountGoodSender.AccountId();
-  const owner = await accountGoodSender.ownerOf(accountId);
-  const initialOwnerBalances = {A: await testERC20A.balanceOf(owner), B: await testERC20B.balanceOf(owner), C: await testERC20C.balanceOf(owner) };
-  await accountGoodSender.withdrawBalances([testERC20B.address]); // Perform the withdrawal to the owner
-  const resultantOwnerBalances = {A: await testERC20A.balanceOf(owner), B: await testERC20B.balanceOf(owner), C: await testERC20C.balanceOf(owner) };
-  expect(resultantOwnerBalances.A).to.equal(initialOwnerBalances.A); // unchanged A
-  expect(resultantOwnerBalances.B).to.equal(initialOwnerBalances.B.add(initialBalances.B)); // Expect NFT's balances to be added to the owner's
-  expect(resultantOwnerBalances.C).to.equal(initialOwnerBalances.C); // unchanged C
-}
-
-// Tests that accountBadSender is not allowed to init the recovery process for the Liquid Infrastructure Account, yet the 
-// accountGoodSender is and the correct event is emitted
-// Note that this will not trigger an actual recovery given that the ethereum provider is hardhat,
-// recovery requires EVM <-> Cosmos module interactions which happen separate from the EVM runtime
-async function testRecoveryProcessInit(accountBadSender: LiquidInfrastructureNFT, accountGoodSender: LiquidInfrastructureNFT) {
-  // Reverted when not sent as current owner
-  await expect(accountBadSender.recoverAccount()).to.be.reverted
-
-  // Successful event emitted when sent as good owner
-  await expect(accountGoodSender.recoverAccount())
-  .to
-  .emit(accountGoodSender, "TryRecover");
-}
-
-describe("LiquidInfrastructureNFT tests", function () {
-  it("works right", async function () {
-    await runTest({})
-  });
-});
\ No newline at end of file

From 526479a6ae384afe1315d7fbd6aa6af5681572b7 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Tue, 7 Jan 2025 16:10:54 -0500
Subject: [PATCH 37/63] Make the testnet ERC20s EIP-2612 tokens

---
 solidity/contracts/TestERC20A.sol | 5 +++--
 solidity/contracts/TestERC20B.sol | 5 +++--
 solidity/contracts/TestERC20C.sol | 5 +++--
 3 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/solidity/contracts/TestERC20A.sol b/solidity/contracts/TestERC20A.sol
index 01195179..faed023c 100644
--- a/solidity/contracts/TestERC20A.sol
+++ b/solidity/contracts/TestERC20A.sol
@@ -1,10 +1,11 @@
 //SPDX-License-Identifier: Apache-2.0
 pragma solidity 0.8.28; // Force solidity compliance
 import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
+import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
 
 // One of three testing coins
-contract TestERC20A is ERC20 {
-    constructor() ERC20("Bitcoin MAX", "MAX") {
+contract TestERC20A is ERC20, ERC20Permit {
+    constructor() ERC20("Bitcoin MAX", "MAX") ERC20Permit("Bitcoin MAX") {
         _mint(
             0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266,
             100000000000000000000000000
diff --git a/solidity/contracts/TestERC20B.sol b/solidity/contracts/TestERC20B.sol
index e0503905..ac940f90 100644
--- a/solidity/contracts/TestERC20B.sol
+++ b/solidity/contracts/TestERC20B.sol
@@ -1,10 +1,11 @@
 //SPDX-License-Identifier: Apache-2.0
 pragma solidity 0.8.28; // Force solidity compliance
 import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
+import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
 
 // One of three testing coins
-contract TestERC20B is ERC20 {
-    constructor() ERC20("2 Ethereum", "E2H") {
+contract TestERC20B is ERC20, ERC20Permit {
+    constructor() ERC20("2 Ethereum", "E2H") ERC20Permit("2 Ethereum") {
         _mint(
             0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266,
             100000000000000000000000000
diff --git a/solidity/contracts/TestERC20C.sol b/solidity/contracts/TestERC20C.sol
index 1797e761..323b1c45 100644
--- a/solidity/contracts/TestERC20C.sol
+++ b/solidity/contracts/TestERC20C.sol
@@ -1,10 +1,11 @@
 //SPDX-License-Identifier: Apache-2.0
 pragma solidity 0.8.28; // Force solidity compliance
 import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
+import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
 
 // One of three testing coins
-contract TestERC20C is ERC20 {
-    constructor() ERC20("Byecoin", "BYE") {
+contract TestERC20C is ERC20, ERC20Permit {
+    constructor() ERC20("Byecoin", "BYE") ERC20Permit("Byecoin") {
         _mint(
             0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266,
             100000000000000000000000000

From 7affe7857b80ac46bfe3aa47c047f8dc1c57a12f Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Tue, 7 Jan 2025 19:01:44 -0500
Subject: [PATCH 38/63] Add feegrant module

---
 app/app.go | 56 ++++++++++++++++++++++++++++++------------------------
 1 file changed, 31 insertions(+), 25 deletions(-)

diff --git a/app/app.go b/app/app.go
index ade22785..b823de24 100644
--- a/app/app.go
+++ b/app/app.go
@@ -62,6 +62,9 @@ import (
 	"github.com/cosmos/cosmos-sdk/x/evidence"
 	evidencekeeper "github.com/cosmos/cosmos-sdk/x/evidence/keeper"
 	evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
+	"github.com/cosmos/cosmos-sdk/x/feegrant"
+	feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper"
+	feegrantmodule "github.com/cosmos/cosmos-sdk/x/feegrant/module"
 	"github.com/cosmos/cosmos-sdk/x/genutil"
 	genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
 	"github.com/cosmos/cosmos-sdk/x/gov"
@@ -189,6 +192,7 @@ var (
 		staking.AppModuleBasic{},
 		mint.AppModuleBasic{},
 		distr.AppModuleBasic{},
+		feegrantmodule.AppModuleBasic{},
 		gov.NewAppModuleBasic(
 			[]govclient.ProposalHandler{
 				paramsclient.ProposalHandler,
@@ -274,19 +278,19 @@ type AltheaApp struct { // nolint: golint
 
 	// keepers
 	// NOTE: If you add anything to this struct, add a nil check to ValidateMembers below!
-	AccountKeeper    *authkeeper.AccountKeeper
-	AuthzKeeper      *authzkeeper.Keeper
-	BankKeeper       *bankkeeper.BaseKeeper
-	CapabilityKeeper *capabilitykeeper.Keeper
-	StakingKeeper    *stakingkeeper.Keeper
-	SlashingKeeper   *slashingkeeper.Keeper
-	MintKeeper       *mintkeeper.Keeper
-	DistrKeeper      *distrkeeper.Keeper
-	GovKeeper        *govkeeper.Keeper
-	CrisisKeeper     *crisiskeeper.Keeper
-	UpgradeKeeper    *upgradekeeper.Keeper
-	ParamsKeeper     *paramskeeper.Keeper
-	// TODO: Add feegrant
+	AccountKeeper     *authkeeper.AccountKeeper
+	AuthzKeeper       *authzkeeper.Keeper
+	BankKeeper        *bankkeeper.BaseKeeper
+	CapabilityKeeper  *capabilitykeeper.Keeper
+	StakingKeeper     *stakingkeeper.Keeper
+	SlashingKeeper    *slashingkeeper.Keeper
+	MintKeeper        *mintkeeper.Keeper
+	DistrKeeper       *distrkeeper.Keeper
+	GovKeeper         *govkeeper.Keeper
+	CrisisKeeper      *crisiskeeper.Keeper
+	UpgradeKeeper     *upgradekeeper.Keeper
+	ParamsKeeper      *paramskeeper.Keeper
+	FeegrantKeeper    *feegrantkeeper.Keeper
 	IbcKeeper         *ibckeeper.Keeper
 	EvidenceKeeper    *evidencekeeper.Keeper
 	IbcTransferKeeper *ibctransferkeeper.Keeper
@@ -364,6 +368,9 @@ func (app AltheaApp) ValidateMembers() {
 	if app.ParamsKeeper == nil {
 		panic("Nil ParamsKeeper!")
 	}
+	if app.FeegrantKeeper == nil {
+		panic("Nil FeegrantKeeper!")
+	}
 	if app.IbcKeeper == nil {
 		panic("Nil IbcKeeper!")
 	}
@@ -457,8 +464,7 @@ func NewAltheaApp(
 		ibchost.StoreKey, upgradetypes.StoreKey, evidencetypes.StoreKey,
 		ibctransfertypes.StoreKey, capabilitytypes.StoreKey,
 		erc20types.StoreKey, evmtypes.StoreKey, feemarkettypes.StoreKey,
-		icahosttypes.StoreKey, group.StoreKey,
-		// TODO: Add feegrant
+		icahosttypes.StoreKey, group.StoreKey, feegrant.StoreKey,
 
 		lockuptypes.StoreKey, microtxtypes.StoreKey, gasfreetypes.StoreKey,
 		onboardingtypes.StoreKey, nativedextypes.StoreKey,
@@ -710,7 +716,8 @@ func NewAltheaApp(
 	)
 	app.CrisisKeeper = &crisisKeeper
 
-	// TODO: add feegrant keeper
+	feegrantKeeper := feegrantkeeper.NewKeeper(appCodec, keys[feegrant.StoreKey], app.AccountKeeper)
+	app.FeegrantKeeper = &feegrantKeeper
 
 	// Nativedex allows management of the native DEX instance from the Cosmos side
 	nativedexKeeper := nativedexkeeper.NewKeeper(
@@ -820,7 +827,7 @@ func NewAltheaApp(
 			&crisisKeeper,
 			skipGenesisInvariants,
 		),
-		// TODO: Add feegrant app module
+		feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, *app.FeegrantKeeper, app.interfaceRegistry),
 		gov.NewAppModule(
 			appCodec,
 			govKeeper,
@@ -896,7 +903,7 @@ func NewAltheaApp(
 		crisistypes.ModuleName,
 		genutiltypes.ModuleName,
 		authz.ModuleName,
-		//TODO: Add feegrant
+		feegrant.ModuleName,
 		paramstypes.ModuleName,
 		vestingtypes.ModuleName,
 		gasfreetypes.ModuleName,
@@ -930,7 +937,7 @@ func NewAltheaApp(
 		genutiltypes.ModuleName,
 		evidencetypes.ModuleName,
 		authz.ModuleName,
-		// TODO: add feegrant
+		feegrant.ModuleName,
 		paramstypes.ModuleName,
 		upgradetypes.ModuleName,
 		vestingtypes.ModuleName,
@@ -961,7 +968,7 @@ func NewAltheaApp(
 		ibctransfertypes.ModuleName,
 		icatypes.ModuleName,
 		authz.ModuleName,
-		// TODO: add feegrant
+		feegrant.ModuleName,
 		paramstypes.ModuleName,
 		upgradetypes.ModuleName,
 		vestingtypes.ModuleName,
@@ -1319,11 +1326,10 @@ func (app *AltheaApp) registerStoreLoaders() {
 func (app *AltheaApp) NewAnteHandlerOptions(appOpts servertypes.AppOptions) ante.HandlerOptions {
 	maxGasWanted := cast.ToUint64(appOpts.Get(ethermintsrvflags.EVMMaxTxGasWanted))
 	return ante.HandlerOptions{
-		AccountKeeper:   app.AccountKeeper,
-		BankKeeper:      app.BankKeeper,
-		SignModeHandler: app.EncodingConfig.TxConfig.SignModeHandler(),
-		// TODO: Add feegrant
-		FeegrantKeeper:         nil,
+		AccountKeeper:          app.AccountKeeper,
+		BankKeeper:             app.BankKeeper,
+		SignModeHandler:        app.EncodingConfig.TxConfig.SignModeHandler(),
+		FeegrantKeeper:         app.FeegrantKeeper,
 		SigGasConsumer:         SigVerificationGasConsumer,
 		IBCKeeper:              app.IbcKeeper,
 		EvmKeeper:              app.EvmKeeper,

From e4c3e32b44e276467e71c198f9a7e7fef5a4412e Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Tue, 7 Jan 2025 19:49:14 -0500
Subject: [PATCH 39/63] Neutrino -> Tethys, set distribution params in upgrade

---
 app/app.go                                    | 14 +++---
 app/upgrades/neutrino/handler.go              | 32 -------------
 app/upgrades/register.go                      | 14 +++---
 app/upgrades/{neutrino => tethys}/README.md   |  4 +-
 app/upgrades/tethys/handler.go                | 46 +++++++++++++++++++
 .../test_runner/src/tests/upgrade.rs          |  2 +-
 6 files changed, 64 insertions(+), 48 deletions(-)
 delete mode 100644 app/upgrades/neutrino/handler.go
 rename app/upgrades/{neutrino => tethys}/README.md (67%)
 create mode 100644 app/upgrades/tethys/handler.go

diff --git a/app/app.go b/app/app.go
index b823de24..4635a828 100644
--- a/app/app.go
+++ b/app/app.go
@@ -12,6 +12,7 @@ import (
 	"github.com/rakyll/statik/fs"
 	"github.com/spf13/cast"
 
+	// Tendermint
 	abci "github.com/tendermint/tendermint/abci/types"
 	tmjson "github.com/tendermint/tendermint/libs/json"
 	"github.com/tendermint/tendermint/libs/log"
@@ -113,8 +114,7 @@ import (
 	ibckeeper "github.com/cosmos/ibc-go/v6/modules/core/keeper"
 	ibctesting "github.com/cosmos/ibc-go/v6/testing/types"
 
-	// EVM + ERC20
-
+	// EVM
 	ethante "github.com/evmos/ethermint/app/ante"
 	"github.com/evmos/ethermint/ethereum/eip712"
 	ethermintsrvflags "github.com/evmos/ethermint/server/flags"
@@ -133,7 +133,7 @@ import (
 	"github.com/AltheaFoundation/althea-L1/app/ante"
 	altheaappparams "github.com/AltheaFoundation/althea-L1/app/params"
 	"github.com/AltheaFoundation/althea-L1/app/upgrades"
-	"github.com/AltheaFoundation/althea-L1/app/upgrades/neutrino"
+	"github.com/AltheaFoundation/althea-L1/app/upgrades/tethys"
 	altheacfg "github.com/AltheaFoundation/althea-L1/config"
 	"github.com/AltheaFoundation/althea-L1/x/erc20"
 	erc20client "github.com/AltheaFoundation/althea-L1/x/erc20/client"
@@ -1291,7 +1291,7 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino
 // registerUpgradeHandlers registers in-place upgrades, which are faster and easier than genesis-based upgrades
 func (app *AltheaApp) registerUpgradeHandlers() {
 	upgrades.RegisterUpgradeHandlers(
-		app.MM, app.Configurator, app.UpgradeKeeper, app.CrisisKeeper,
+		app.MM, app.Configurator, app.UpgradeKeeper, app.CrisisKeeper, app.DistrKeeper,
 	)
 }
 
@@ -1311,10 +1311,10 @@ func (app *AltheaApp) registerStoreLoaders() {
 	// Deleted: []string{"bazmodule"}, example deleted bazmodule
 
 	// <name> Group module store loader setup
-	if upgradeInfo.Name == neutrino.PlanName {
-		// Register the Group module as a new module that needs a new store allocated
+	if upgradeInfo.Name == tethys.PlanName {
+		// Register the Group and Feegrant modules as new modules that need new stores allocated
 		storeUpgrades := storetypes.StoreUpgrades{
-			Added:   []string{group.StoreKey},
+			Added:   []string{group.StoreKey, feegrant.StoreKey},
 			Renamed: nil,
 			Deleted: nil,
 		}
diff --git a/app/upgrades/neutrino/handler.go b/app/upgrades/neutrino/handler.go
deleted file mode 100644
index fa9d9a13..00000000
--- a/app/upgrades/neutrino/handler.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package neutrino
-
-import (
-	sdk "github.com/cosmos/cosmos-sdk/types"
-	"github.com/cosmos/cosmos-sdk/types/module"
-	crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper"
-	upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
-)
-
-var PlanName = "neutrino"
-
-func GetNeutrinoUpgradeHandler(
-	mm *module.Manager, configurator *module.Configurator, crisisKeeper *crisiskeeper.Keeper,
-) func(
-	ctx sdk.Context, plan upgradetypes.Plan, vmap module.VersionMap,
-) (module.VersionMap, error) {
-	if mm == nil {
-		panic("Nil argument to GetNeutrinoUpgradeHandler")
-	}
-	return func(ctx sdk.Context, plan upgradetypes.Plan, vmap module.VersionMap) (module.VersionMap, error) {
-		ctx.Logger().Info("Module Consensus Version Map", "vmap", vmap)
-
-		ctx.Logger().Info("Neutrino Upgrade: Running any configured module migrations")
-		out, outErr := mm.RunMigrations(ctx, *configurator, vmap)
-
-		ctx.Logger().Info("Asserting invariants after upgrade")
-		crisisKeeper.AssertInvariants(ctx)
-
-		ctx.Logger().Info("Neutrino Upgrade Successful")
-		return out, outErr
-	}
-}
diff --git a/app/upgrades/register.go b/app/upgrades/register.go
index cc4bc419..3de65534 100644
--- a/app/upgrades/register.go
+++ b/app/upgrades/register.go
@@ -3,9 +3,10 @@ package upgrades
 import (
 	"github.com/cosmos/cosmos-sdk/types/module"
 	crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper"
+	distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper"
 	upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper"
 
-	"github.com/AltheaFoundation/althea-L1/app/upgrades/neutrino"
+	"github.com/AltheaFoundation/althea-L1/app/upgrades/tethys"
 )
 
 // RegisterUpgradeHandlers registers handlers for all upgrades
@@ -13,14 +14,15 @@ import (
 // along with an interface
 func RegisterUpgradeHandlers(
 	mm *module.Manager, configurator *module.Configurator, upgradeKeeper *upgradekeeper.Keeper,
-	crisisKeeper *crisiskeeper.Keeper,
+	crisisKeeper *crisiskeeper.Keeper, distrKeeper *distrkeeper.Keeper,
 ) {
-	if mm == nil || configurator == nil || crisisKeeper == nil || upgradeKeeper == nil {
+	if mm == nil || configurator == nil || crisisKeeper == nil || upgradeKeeper == nil || distrKeeper == nil {
 		panic("Nil argument to RegisterUpgradeHandlers()!")
 	}
-	// Neutrino upgrade
+
+	// Tethys upgrade
 	upgradeKeeper.SetUpgradeHandler(
-		neutrino.PlanName,
-		neutrino.GetNeutrinoUpgradeHandler(mm, configurator, crisisKeeper),
+		tethys.PlanName,
+		tethys.GetTethysUpgradeHandler(mm, configurator, crisisKeeper, distrKeeper),
 	)
 }
diff --git a/app/upgrades/neutrino/README.md b/app/upgrades/tethys/README.md
similarity index 67%
rename from app/upgrades/neutrino/README.md
rename to app/upgrades/tethys/README.md
index c71b0539..cc925977 100644
--- a/app/upgrades/neutrino/README.md
+++ b/app/upgrades/tethys/README.md
@@ -1,6 +1,6 @@
-# Neutrino UPGRADE
+# Tethys UPGRADE
 
-The *Neutrino* upgrade contains the following changes.
+The *Tethys* upgrade contains the following changes.
 
 ## Summary of Changes
 
diff --git a/app/upgrades/tethys/handler.go b/app/upgrades/tethys/handler.go
new file mode 100644
index 00000000..54003348
--- /dev/null
+++ b/app/upgrades/tethys/handler.go
@@ -0,0 +1,46 @@
+package tethys
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/module"
+	crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper"
+	distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper"
+	upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
+)
+
+var PlanName = "tethys"
+
+func GetTethysUpgradeHandler(
+	mm *module.Manager, configurator *module.Configurator, crisisKeeper *crisiskeeper.Keeper, distrKeeper *distrkeeper.Keeper,
+) func(
+	ctx sdk.Context, plan upgradetypes.Plan, vmap module.VersionMap,
+) (module.VersionMap, error) {
+	if mm == nil {
+		panic("Nil argument to GetTethysUpgradeHandler")
+	}
+	return func(ctx sdk.Context, plan upgradetypes.Plan, vmap module.VersionMap) (module.VersionMap, error) {
+		ctx.Logger().Info("Module Consensus Version Map", "vmap", vmap)
+
+		ctx.Logger().Info("Tethys Upgrade: Running any configured module migrations")
+		out, outErr := mm.RunMigrations(ctx, *configurator, vmap)
+
+		updateDistributionParams(ctx, distrKeeper)
+
+		ctx.Logger().Info("Asserting invariants after upgrade")
+		crisisKeeper.AssertInvariants(ctx)
+
+		ctx.Logger().Info("Tethys Upgrade Successful")
+		return out, outErr
+	}
+}
+
+func updateDistributionParams(ctx sdk.Context, distrKeeper *distrkeeper.Keeper) {
+	ctx.Logger().Info("Tethys Upgrade: Updating Distribution module Params")
+	distrParams := distrKeeper.GetParams(ctx)
+	updatedBaseReward := sdk.NewDecWithPrec(50, 2)  // 50%
+	updatedBonusReward := sdk.NewDecWithPrec(04, 2) // 04%
+	distrParams.BaseProposerReward = updatedBaseReward
+	distrParams.BonusProposerReward = updatedBonusReward
+	ctx.Logger().Info("Updating Distribution module Params from ", distrKeeper.GetParams(ctx).String(), " to ", distrParams.String())
+	distrKeeper.SetParams(ctx, distrParams)
+}
diff --git a/integration_tests/test_runner/src/tests/upgrade.rs b/integration_tests/test_runner/src/tests/upgrade.rs
index 32e68f99..69cf62d1 100644
--- a/integration_tests/test_runner/src/tests/upgrade.rs
+++ b/integration_tests/test_runner/src/tests/upgrade.rs
@@ -12,7 +12,7 @@ use super::erc20_conversion::erc20_conversion_test;
 use super::microtx_fees::microtx_fees_test;
 use super::native_token::native_token_test;
 
-pub const UPGRADE_NAME: &str = "neutrino";
+pub const UPGRADE_NAME: &str = "tethys";
 
 /// Perform a series of integration tests to seed the system with data, then submit and pass a chain
 /// upgrade proposal

From 99318f539ba69b7075debbdb03fa0b5298c9efc4 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Tue, 7 Jan 2025 20:21:58 -0500
Subject: [PATCH 40/63] Fix contract helper script, update compiled liquid
 infrastructure nft contract

---
 contracts/compiled/LiquidInfrastructureNFT.json | 4 ++--
 scripts/compile-contracts-for-go.sh             | 8 ++------
 2 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/contracts/compiled/LiquidInfrastructureNFT.json b/contracts/compiled/LiquidInfrastructureNFT.json
index 21cd44c6..2ee1b3f3 100644
--- a/contracts/compiled/LiquidInfrastructureNFT.json
+++ b/contracts/compiled/LiquidInfrastructureNFT.json
@@ -1,5 +1,5 @@
 {
   "contractName": "LiquidInfrastructureNFT",
-  "abi": "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"accountName\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"approved\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"ApprovalForAll\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"erc20s\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"}],\"name\":\"SuccessfulRecovery\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"erc20s\",\"type\":\"address[]\"}],\"name\":\"SuccessfulWithdrawal\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"newErc20s\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"newAmounts\",\"type\":\"uint256[]\"}],\"name\":\"ThresholdsChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"TryRecover\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"AccountId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Version\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"getApproved\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThresholds\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"isApprovedForAll\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ownerOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"recoverAccount\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"setApprovalForAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"newErc20s\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"newAmounts\",\"type\":\"uint256[]\"}],\"name\":\"setThresholds\",\"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\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"tokenURI\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"erc20s\",\"type\":\"address[]\"}],\"name\":\"withdrawBalances\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"erc20s\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"withdrawBalancesTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
-  "bin": "60806040523480156200001157600080fd5b5060405162002072380380620020728339810160408190526200003491620002fc565b80604051602001620000479190620003b4565b604051602081830303815290604052816040516020016200006991906200040b565b60408051601f1981840301815291905281516200008e9060009060208501906200020d565b508051620000a49060019060208401906200020d565b505050620000ba336001620000c160201b60201c565b506200049d565b6001600160a01b0382166200011d5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f206164647265737360448201526064015b60405180910390fd5b6000818152600260205260409020546001600160a01b031615620001845760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000604482015260640162000114565b6001600160a01b0382166000908152600360205260408120805460019290620001af90849062000439565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b8280546200021b9062000460565b90600052602060002090601f0160209004810192826200023f57600085556200028a565b82601f106200025a57805160ff19168380011785556200028a565b828001600101855582156200028a579182015b828111156200028a5782518255916020019190600101906200026d565b50620002989291506200029c565b5090565b5b808211156200029857600081556001016200029d565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620002e6578181015183820152602001620002cc565b83811115620002f6576000848401525b50505050565b6000602082840312156200030f57600080fd5b81516001600160401b03808211156200032757600080fd5b818401915084601f8301126200033c57600080fd5b815181811115620003515762000351620002b3565b604051601f8201601f19908116603f011681019083821181831017156200037c576200037c620002b3565b816040528281528760208487010111156200039657600080fd5b620003a9836020830160208801620002c9565b979650505050505050565b7f616c746865613a2f2f6c69717569642d696e6672617374727563747572652d6181526663636f756e742f60c81b602082015260008251620003fe816027850160208701620002c9565b9190910160270192915050565b632624a09d60e11b8152600082516200042c816004850160208701620002c9565b9190910160040192915050565b600082198211156200045b57634e487b7160e01b600052601160045260246000fd5b500190565b600181811c908216806200047557607f821691505b602082108114156200049757634e487b7160e01b600052602260045260246000fd5b50919050565b611bc580620004ad6000396000f3fe608060405234801561001057600080fd5b506004361061012c5760003560e01c80638b702802116100ad578063bb62860d11610071578063bb62860d146101fa578063bfe9d7571461027a578063c87b56dd14610282578063cb0e095c14610295578063e985e9c5146102a857600080fd5b80638b7028021461022357806395d89b41146102365780639c3977b51461023e578063a22cb46514610254578063b88d4fde1461026757600080fd5b806342842e0e116100f457806342842e0e146101c1578063570fa5bc146101d45780636352211e146101e75780636cf324ef146101fa57806370a082311461021057600080fd5b806301ffc9a71461013157806306fdde0314610159578063081812fc1461016e578063095ea7b31461019957806323b872dd146101ae575b600080fd5b61014461013f3660046113f2565b6102e4565b60405190151581526020015b60405180910390f35b610161610336565b6040516101509190611467565b61018161017c36600461147a565b6103c8565b6040516001600160a01b039091168152602001610150565b6101ac6101a73660046114a8565b610462565b005b6101ac6101bc3660046114d4565b610578565b6101ac6101cf3660046114d4565b6105aa565b6101ac6101e2366004611561565b6105c5565b6101816101f536600461147a565b6107ab565b610202600181565b604051908152602001610150565b61020261021e3660046115cd565b610822565b6101ac6102313660046115ea565b6108a9565b6101616108d8565b6102466108e7565b60405161015092919061162c565b6101ac6102623660046116be565b6109a4565b6101ac61027536600461170d565b610a69565b6101ac610a9b565b61016161029036600461147a565b610ba0565b6101ac6102a33660046117ed565b610c88565b6101446102b6366004611844565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b60006001600160e01b031982166380ac58cd60e01b148061031557506001600160e01b03198216635b5e139f60e01b145b8061033057506301ffc9a760e01b6001600160e01b03198316145b92915050565b60606000805461034590611872565b80601f016020809104026020016040519081016040528092919081815260200182805461037190611872565b80156103be5780601f10610393576101008083540402835291602001916103be565b820191906000526020600020905b8154815290600101906020018083116103a157829003601f168201915b5050505050905090565b6000818152600260205260408120546001600160a01b03166104465760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b60648201526084015b60405180910390fd5b506000908152600460205260409020546001600160a01b031690565b600061046d826107ab565b9050806001600160a01b0316836001600160a01b031614156104db5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b606482015260840161043d565b336001600160a01b03821614806104f757506104f781336102b6565b6105695760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760448201527f6e6572206e6f7220617070726f76656420666f7220616c6c0000000000000000606482015260840161043d565b6105738383610ca8565b505050565b610583335b82610d16565b61059f5760405162461bcd60e51b815260040161043d906118ad565b610573838383610e0d565b61057383838360405180602001604052806000815250610a69565b60016105d03361057d565b15610740578382146106375760405162461bcd60e51b815260206004820152602a60248201527f7468726573686f6c642076616c756573206d75737420686176652074686520736044820152690c2daca40d8cadccee8d60b31b606482015260840161043d565b610643600660006113a2565b61064f600760006113a2565b60005b848110156106fd57600686868381811061066e5761066e6118fe565b905060200201602081019061068391906115cd565b81546001810183556000928352602090922090910180546001600160a01b0319166001600160a01b0390921691909117905560078484838181106106c9576106c96118fe565b83546001810185556000948552602094859020919094029290920135919092015550806106f58161192a565b915050610652565b507f654ec6a6c41d997f6ceb2748132213f6ee151c84895ef4a08f17b5743754809b85858585604051610733949392919061198e565b60405180910390a16107a4565b60405162461bcd60e51b815260206004820152603360248201527f4f776e61626c65417070726f7661626c653a2063616c6c6572206973206e6f74604482015272081bdddb995c881b9bdc88185c1c1c9bdd9959606a1b606482015260840161043d565b5050505050565b6000818152600260205260408120546001600160a01b0316806103305760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b606482015260840161043d565b60006001600160a01b03821661088d5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b606482015260840161043d565b506001600160a01b031660009081526003602052604090205490565b60016108b43361057d565b156107405760006108c560016107ab565b90506108d2848483610fad565b50505050565b60606001805461034590611872565b606080600660078180548060200260200160405190810160405280929190818152602001828054801561094357602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610925575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561099557602002820191906000526020600020905b815481526020019060010190808311610981575b50505050509050915091509091565b6001600160a01b0382163314156109fd5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c657200000000000000604482015260640161043d565b3360008181526005602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b610a733383610d16565b610a8f5760405162461bcd60e51b815260040161043d906118ad565b6108d284848484611173565b6001336040516331a9108f60e11b8152600481018390526001600160a01b0391909116903090636352211e90602401602060405180830381865afa158015610ae7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0b91906119e0565b6001600160a01b031614610b745760405162461bcd60e51b815260206004820152602a60248201527f4f776e61626c65417070726f7661626c653a2063616c6c6572206973206e6f74604482015269103a34329037bbb732b960b11b606482015260840161043d565b6040517f9dbe82bd7c9a3230967fa6a4082f47590628e191353da33cef87339fbd74934790600090a150565b6000818152600260205260409020546060906001600160a01b0316610c1f5760405162461bcd60e51b815260206004820152602f60248201527f4552433732314d657461646174613a2055524920717565727920666f72206e6f60448201526e3732bc34b9ba32b73a103a37b5b2b760891b606482015260840161043d565b6000610c3660408051602081019091526000815290565b90506000815111610c565760405180602001604052806000815250610c81565b80610c60846111a6565b604051602001610c719291906119fd565b6040516020818303038152906040525b9392505050565b6001610c933361057d565b1561074057610ca3848484610fad565b6108d2565b600081815260046020526040902080546001600160a01b0319166001600160a01b0384169081179091558190610cdd826107ab565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000818152600260205260408120546001600160a01b0316610d8f5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b606482015260840161043d565b6000610d9a836107ab565b9050806001600160a01b0316846001600160a01b03161480610dd55750836001600160a01b0316610dca846103c8565b6001600160a01b0316145b80610e0557506001600160a01b0380821660009081526005602090815260408083209388168352929052205460ff165b949350505050565b826001600160a01b0316610e20826107ab565b6001600160a01b031614610e885760405162461bcd60e51b815260206004820152602960248201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960448201526839903737ba1037bbb760b91b606482015260840161043d565b6001600160a01b038216610eea5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b606482015260840161043d565b610ef5600082610ca8565b6001600160a01b0383166000908152600360205260408120805460019290610f1e908490611a2c565b90915550506001600160a01b0382166000908152600360205260408120805460019290610f4c908490611a43565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b60005b82811015611134576000848483818110610fcc57610fcc6118fe565b9050602002016020810190610fe191906115cd565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa15801561102b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061104f9190611a5b565b9050801561111f5760405163a9059cbb60e01b81526001600160a01b038581166004830152602482018390526000919084169063a9059cbb906044016020604051808303816000875af11580156110aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ce9190611a74565b90508061111d5760405162461bcd60e51b815260206004820152601760248201527f756e7375636365737366756c207769746864726177616c000000000000000000604482015260640161043d565b505b5050808061112c9061192a565b915050610fb0565b507ffbbf2a0bd3dc085cafe424f837600e98b62c9fb098bdf12441e1018cfa6a10028383604051611166929190611a91565b60405180910390a1505050565b61117e848484610e0d565b61118a848484846112a4565b6108d25760405162461bcd60e51b815260040161043d90611aa5565b6060816111ca5750506040805180820190915260018152600360fc1b602082015290565b8160005b81156111f457806111de8161192a565b91506111ed9050600a83611b0d565b91506111ce565b60008167ffffffffffffffff81111561120f5761120f6116f7565b6040519080825280601f01601f191660200182016040528015611239576020820181803683370190505b5090505b8415610e055761124e600183611a2c565b915061125b600a86611b21565b611266906030611a43565b60f81b81838151811061127b5761127b6118fe565b60200101906001600160f81b031916908160001a90535061129d600a86611b0d565b945061123d565b60006001600160a01b0384163b1561139757604051630a85bd0160e11b81526001600160a01b0385169063150b7a02906112e8903390899088908890600401611b35565b6020604051808303816000875af1925050508015611323575060408051601f3d908101601f1916820190925261132091810190611b72565b60015b61137d573d808015611351576040519150601f19603f3d011682016040523d82523d6000602084013e611356565b606091505b5080516113755760405162461bcd60e51b815260040161043d90611aa5565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610e05565b506001949350505050565b50805460008255906000526020600020908101906113c091906113c3565b50565b5b808211156113d857600081556001016113c4565b5090565b6001600160e01b0319811681146113c057600080fd5b60006020828403121561140457600080fd5b8135610c81816113dc565b60005b8381101561142a578181015183820152602001611412565b838111156108d25750506000910152565b6000815180845261145381602086016020860161140f565b601f01601f19169290920160200192915050565b602081526000610c81602083018461143b565b60006020828403121561148c57600080fd5b5035919050565b6001600160a01b03811681146113c057600080fd5b600080604083850312156114bb57600080fd5b82356114c681611493565b946020939093013593505050565b6000806000606084860312156114e957600080fd5b83356114f481611493565b9250602084013561150481611493565b929592945050506040919091013590565b60008083601f84011261152757600080fd5b50813567ffffffffffffffff81111561153f57600080fd5b6020830191508360208260051b850101111561155a57600080fd5b9250929050565b6000806000806040858703121561157757600080fd5b843567ffffffffffffffff8082111561158f57600080fd5b61159b88838901611515565b909650945060208701359150808211156115b457600080fd5b506115c187828801611515565b95989497509550505050565b6000602082840312156115df57600080fd5b8135610c8181611493565b600080602083850312156115fd57600080fd5b823567ffffffffffffffff81111561161457600080fd5b61162085828601611515565b90969095509350505050565b604080825283519082018190526000906020906060840190828701845b8281101561166e5781516001600160a01b031684529284019290840190600101611649565b5050508381038285015284518082528583019183019060005b818110156116a357835183529284019291840191600101611687565b5090979650505050505050565b80151581146113c057600080fd5b600080604083850312156116d157600080fd5b82356116dc81611493565b915060208301356116ec816116b0565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b6000806000806080858703121561172357600080fd5b843561172e81611493565b9350602085013561173e81611493565b925060408501359150606085013567ffffffffffffffff8082111561176257600080fd5b818701915087601f83011261177657600080fd5b813581811115611788576117886116f7565b604051601f8201601f19908116603f011681019083821181831017156117b0576117b06116f7565b816040528281528a60208487010111156117c957600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b60008060006040848603121561180257600080fd5b833567ffffffffffffffff81111561181957600080fd5b61182586828701611515565b909450925050602084013561183981611493565b809150509250925092565b6000806040838503121561185757600080fd5b823561186281611493565b915060208301356116ec81611493565b600181811c9082168061188657607f821691505b602082108114156118a757634e487b7160e01b600052602260045260246000fd5b50919050565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060001982141561193e5761193e611914565b5060010190565b8183526000602080850194508260005b8581101561198357813561196881611493565b6001600160a01b031687529582019590820190600101611955565b509495945050505050565b6040815260006119a2604083018688611945565b82810360208401528381526001600160fb1b038411156119c157600080fd5b8360051b80866020840137600091016020019081529695505050505050565b6000602082840312156119f257600080fd5b8151610c8181611493565b60008351611a0f81846020880161140f565b835190830190611a2381836020880161140f565b01949350505050565b600082821015611a3e57611a3e611914565b500390565b60008219821115611a5657611a56611914565b500190565b600060208284031215611a6d57600080fd5b5051919050565b600060208284031215611a8657600080fd5b8151610c81816116b0565b602081526000610e05602083018486611945565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b634e487b7160e01b600052601260045260246000fd5b600082611b1c57611b1c611af7565b500490565b600082611b3057611b30611af7565b500690565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090611b689083018461143b565b9695505050505050565b600060208284031215611b8457600080fd5b8151610c81816113dc56fea2646970667358221220aa126a5fc2a7249fc46b2c90ef3a4f0fad1d2b3c6c7bec25da275d2a912e191164736f6c634300080c0033"
+  "abi": "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"accountName\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"ERC721IncorrectOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ERC721InsufficientApproval\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC721InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"ERC721InvalidOperator\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"ERC721InvalidOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC721InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC721InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ERC721NonexistentToken\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"approved\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"ApprovalForAll\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"erc20s\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"}],\"name\":\"SuccessfulRecovery\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"erc20s\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"}],\"name\":\"SuccessfulWithdrawal\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"newErc20s\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"newAmounts\",\"type\":\"uint256[]\"}],\"name\":\"ThresholdsChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"TryRecover\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"AccountId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Version\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"getApproved\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThresholds\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"isApprovedForAll\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ownerOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"recoverAccount\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"setApprovalForAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"newErc20s\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"newAmounts\",\"type\":\"uint256[]\"}],\"name\":\"setThresholds\",\"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\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"tokenURI\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"erc20s\",\"type\":\"address[]\"}],\"name\":\"withdrawBalances\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"erc20s\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"withdrawBalancesTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
+  "bin": "608060405234801561000f575f5ffd5b50604051611f11380380611f1183398101604081905261002e91610459565b8060405160200161003f9190610520565b6040516020818303038152906040528160405160200161005f9190610568565b60408051601f198184030181529190525f61007a8382610603565b5060016100878282610603565b50505061009b3360016100a160201b60201c565b506106bd565b6001600160a01b0382166100cf57604051633250574960e11b81525f60048201526024015b60405180910390fd5b5f6100db83838361010c565b90506001600160a01b03811615610107576040516339e3563760e11b81525f60048201526024016100c6565b505050565b5f828152600260205260408120546001600160a01b0390811690831615610138576101388184866101fe565b6001600160a01b03811615610172576101535f858180610262565b6001600160a01b0381165f90815260036020526040902080545f190190555b6001600160a01b038516156101a0576001600160a01b0385165f908152600360205260409020805460010190555b5f8481526002602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4949350505050565b610209838383610384565b610107576001600160a01b03831661023757604051637e27328960e01b8152600481018290526024016100c6565b60405163177e802f60e01b81526001600160a01b0383166004820152602481018290526044016100c6565b808061027657506001600160a01b03821615155b15610355575f61028584610407565b90506001600160a01b038316158015906102b15750826001600160a01b0316816001600160a01b031614155b80156102e257506001600160a01b038082165f9081526005602090815260408083209387168352929052205460ff16155b1561030b5760405163a9fbf51f60e01b81526001600160a01b03841660048201526024016100c6565b81156103535783856001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b50505f90815260046020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b5f6001600160a01b038316158015906103ff5750826001600160a01b0316846001600160a01b031614806103dc57506001600160a01b038085165f9081526005602090815260408083209387168352929052205460ff165b806103ff57505f828152600460205260409020546001600160a01b038481169116145b949350505050565b5f818152600260205260408120546001600160a01b03168061043f57604051637e27328960e01b8152600481018490526024016100c6565b92915050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215610469575f5ffd5b81516001600160401b0381111561047e575f5ffd5b8201601f8101841361048e575f5ffd5b80516001600160401b038111156104a7576104a7610445565b604051601f8201601f19908116603f011681016001600160401b03811182821017156104d5576104d5610445565b6040528181528282016020018610156104ec575f5ffd5b8160208401602083015e5f91810160200191909152949350505050565b5f81518060208401855e5f93019283525090919050565b7f616c746865613a2f2f6c69717569642d696e6672617374727563747572652d6181526663636f756e742f60c81b60208201525f6105616027830184610509565b9392505050565b632624a09d60e11b81525f6105616004830184610509565b600181811c9082168061059457607f821691505b6020821081036105b257634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561010757805f5260205f20601f840160051c810160208510156105dd5750805b601f840160051c820191505b818110156105fc575f81556001016105e9565b5050505050565b81516001600160401b0381111561061c5761061c610445565b6106308161062a8454610580565b846105b8565b6020601f821160018114610662575f831561064b5750848201515b5f19600385901b1c1916600184901b1784556105fc565b5f84815260208120601f198516915b828110156106915787850151825560209485019460019092019101610671565b50848210156106ae57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b611847806106ca5f395ff3fe608060405234801561000f575f5ffd5b5060043610610127575f3560e01c80638b702802116100a9578063bb62860d1161006e578063bb62860d146101f4578063bfe9d75714610274578063c87b56dd1461027c578063cb0e095c1461028f578063e985e9c5146102a2575f5ffd5b80638b7028021461021d57806395d89b41146102305780639c3977b514610238578063a22cb4651461024e578063b88d4fde14610261575f5ffd5b806342842e0e116100ef57806342842e0e146101bb578063570fa5bc146101ce5780636352211e146101e15780636cf324ef146101f457806370a082311461020a575f5ffd5b806301ffc9a71461012b57806306fdde0314610153578063081812fc14610168578063095ea7b31461019357806323b872dd146101a8575b5f5ffd5b61013e61013936600461120b565b6102b5565b60405190151581526020015b60405180910390f35b61015b610306565b60405161014a9190611254565b61017b610176366004611266565b610395565b6040516001600160a01b03909116815260200161014a565b6101a66101a1366004611298565b6103bc565b005b6101a66101b63660046112c0565b6103cb565b6101a66101c93660046112c0565b610459565b6101a66101dc366004611342565b610478565b61017b6101ef366004611266565b610672565b6101fc600181565b60405190815260200161014a565b6101fc6102183660046113ae565b61067c565b6101a661022b3660046113c7565b6106c1565b61015b610713565b610240610722565b60405161014a929190611440565b6101a661025c3660046114ad565b6107db565b6101a661026f3660046114f6565b6107e6565b6101a66107fe565b61015b61028a366004611266565b6108a3565b6101a661029d3660046115d3565b610914565b61013e6102b0366004611623565b610950565b5f6001600160e01b031982166380ac58cd60e01b14806102e557506001600160e01b03198216635b5e139f60e01b145b8061030057506301ffc9a760e01b6001600160e01b03198316145b92915050565b60605f805461031490611654565b80601f016020809104026020016040519081016040528092919081815260200182805461034090611654565b801561038b5780601f106103625761010080835404028352916020019161038b565b820191905f5260205f20905b81548152906001019060200180831161036e57829003601f168201915b5050505050905090565b5f61039f8261097d565b505f828152600460205260409020546001600160a01b0316610300565b6103c78282336109b5565b5050565b6001600160a01b0382166103f957604051633250574960e11b81525f60048201526024015b60405180910390fd5b5f6104058383336109c2565b9050836001600160a01b0316816001600160a01b031614610453576040516364283d7b60e01b81526001600160a01b03808616600483015260248201849052821660448201526064016103f0565b50505050565b61047383838360405180602001604052805f8152506107e6565b505050565b60015f6104848261097d565b9050336001600160a01b0382168114806104a457506104a4828285610ab4565b156106055785841461050b5760405162461bcd60e51b815260206004820152602a60248201527f7468726573686f6c642076616c756573206d75737420686176652074686520736044820152690c2daca40d8cadccee8d60b31b60648201526084016103f0565b61051660065f6111c0565b61052160075f6111c0565b5f5b868110156105c257600688888381811061053f5761053f61168c565b905060200201602081019061055491906113ae565b81546001810183555f928352602090922090910180546001600160a01b0319166001600160a01b0390921691909117905560078686838181106105995761059961168c565b8354600180820186555f9586526020958690209290950293909301359201919091555001610523565b507f654ec6a6c41d997f6ceb2748132213f6ee151c84895ef4a08f17b5743754809b878787876040516105f894939291906116dc565b60405180910390a1610669565b60405162461bcd60e51b815260206004820152603360248201527f4f776e61626c65417070726f7661626c653a2063616c6c6572206973206e6f74604482015272081bdddb995c881b9bdc88185c1c1c9bdd9959606a1b60648201526084016103f0565b50505050505050565b5f6103008261097d565b5f6001600160a01b0382166106a6576040516322718ad960e21b81525f60048201526024016103f0565b506001600160a01b03165f9081526003602052604090205490565b60015f6106cd8261097d565b9050336001600160a01b0382168114806106ed57506106ed828285610ab4565b15610605575f6106fd6001610672565b905061070a868683610b18565b505b5050505050565b60606001805461031490611654565b606080600660078180548060200260200160405190810160405280929190818152602001828054801561077c57602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161075e575b50505050509150808054806020026020016040519081016040528092919081815260200182805480156107cc57602002820191905f5260205f20905b8154815260200190600101908083116107b8575b50505050509050915091509091565b6103c7338383610d34565b6107f18484846103cb565b6104533385858585610dd2565b60015f61080a8261097d565b90506001600160a01b03811633146108775760405162461bcd60e51b815260206004820152602a60248201527f4f776e61626c65417070726f7661626c653a2063616c6c6572206973206e6f74604482015269103a34329037bbb732b960b11b60648201526084016103f0565b6040517f9dbe82bd7c9a3230967fa6a4082f47590628e191353da33cef87339fbd749347905f90a15050565b60606108ae8261097d565b505f6108c460408051602081019091525f815290565b90505f8151116108e25760405180602001604052805f81525061090d565b806108ec84610ef1565b6040516020016108fd92919061173d565b6040516020818303038152906040525b9392505050565b60015f6109208261097d565b9050336001600160a01b0382168114806109405750610940828285610ab4565b156106055761070a868686610b18565b6001600160a01b039182165f90815260056020908152604080832093909416825291909152205460ff1690565b5f818152600260205260408120546001600160a01b03168061030057604051637e27328960e01b8152600481018490526024016103f0565b6104738383836001610f81565b5f828152600260205260408120546001600160a01b03908116908316156109ee576109ee818486611085565b6001600160a01b03811615610a2857610a095f855f5f610f81565b6001600160a01b0381165f90815260036020526040902080545f190190555b6001600160a01b03851615610a56576001600160a01b0385165f908152600360205260409020805460010190555b5f8481526002602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4949350505050565b5f6001600160a01b03831615801590610b105750826001600160a01b0316846001600160a01b03161480610aed5750610aed8484610950565b80610b1057505f828152600460205260409020546001600160a01b038481169116145b949350505050565b5f8267ffffffffffffffff811115610b3257610b326114e2565b604051908082528060200260200182016040528015610b5b578160200160208202803683370190505b5090505f5b83811015610cf0575f858583818110610b7b57610b7b61168c565b9050602002016020810190610b9091906113ae565b6040516370a0823160e01b81523060048201529091505f906001600160a01b038316906370a0823190602401602060405180830381865afa158015610bd7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bfb9190611751565b90508015610ce65760405163a9059cbb60e01b81526001600160a01b038681166004830152602482018390525f919084169063a9059cbb906044016020604051808303815f875af1158015610c52573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c769190611768565b905080610cc55760405162461bcd60e51b815260206004820152601760248201527f756e7375636365737366756c207769746864726177616c00000000000000000060448201526064016103f0565b81858581518110610cd857610cd861168c565b602002602001018181525050505b5050600101610b60565b507fd09f7c02747f1be0aa80758e1857fdf675f9cb71ff40e5ef7701758fa1788e3082858584604051610d269493929190611783565b60405180910390a150505050565b6001600160a01b038216610d6657604051630b61174360e31b81526001600160a01b03831660048201526024016103f0565b6001600160a01b038381165f81815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6001600160a01b0383163b1561070c57604051630a85bd0160e11b81526001600160a01b0384169063150b7a0290610e149088908890879087906004016117c4565b6020604051808303815f875af1925050508015610e4e575060408051601f3d908101601f19168201909252610e4b918101906117f6565b60015b610eb5573d808015610e7b576040519150601f19603f3d011682016040523d82523d5f602084013e610e80565b606091505b5080515f03610ead57604051633250574960e11b81526001600160a01b03851660048201526024016103f0565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b1461070a57604051633250574960e11b81526001600160a01b03851660048201526024016103f0565b60605f610efd836110e9565b60010190505f8167ffffffffffffffff811115610f1c57610f1c6114e2565b6040519080825280601f01601f191660200182016040528015610f46576020820181803683370190505b5090508181016020015b5f19016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a8504945084610f5057509392505050565b8080610f9557506001600160a01b03821615155b15611056575f610fa48461097d565b90506001600160a01b03831615801590610fd05750826001600160a01b0316816001600160a01b031614155b8015610fe35750610fe18184610950565b155b1561100c5760405163a9fbf51f60e01b81526001600160a01b03841660048201526024016103f0565b81156110545783856001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b50505f90815260046020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b611090838383610ab4565b610473576001600160a01b0383166110be57604051637e27328960e01b8152600481018290526024016103f0565b60405163177e802f60e01b81526001600160a01b0383166004820152602481018290526044016103f0565b5f8072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106111275772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310611153576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061117157662386f26fc10000830492506010015b6305f5e1008310611189576305f5e100830492506008015b612710831061119d57612710830492506004015b606483106111af576064830492506002015b600a83106103005760010192915050565b5080545f8255905f5260205f20908101906111db91906111de565b50565b5b808211156111f2575f81556001016111df565b5090565b6001600160e01b0319811681146111db575f5ffd5b5f6020828403121561121b575f5ffd5b813561090d816111f6565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f61090d6020830184611226565b5f60208284031215611276575f5ffd5b5035919050565b80356001600160a01b0381168114611293575f5ffd5b919050565b5f5f604083850312156112a9575f5ffd5b6112b28361127d565b946020939093013593505050565b5f5f5f606084860312156112d2575f5ffd5b6112db8461127d565b92506112e96020850161127d565b929592945050506040919091013590565b5f5f83601f84011261130a575f5ffd5b50813567ffffffffffffffff811115611321575f5ffd5b6020830191508360208260051b850101111561133b575f5ffd5b9250929050565b5f5f5f5f60408587031215611355575f5ffd5b843567ffffffffffffffff81111561136b575f5ffd5b611377878288016112fa565b909550935050602085013567ffffffffffffffff811115611396575f5ffd5b6113a2878288016112fa565b95989497509550505050565b5f602082840312156113be575f5ffd5b61090d8261127d565b5f5f602083850312156113d8575f5ffd5b823567ffffffffffffffff8111156113ee575f5ffd5b6113fa858286016112fa565b90969095509350505050565b5f8151808452602084019350602083015f5b82811015611436578151865260209586019590910190600101611418565b5093949350505050565b604080825283519082018190525f9060208501906060840190835b818110156114825783516001600160a01b031683526020938401939092019160010161145b565b505083810360208501526114968186611406565b9695505050505050565b80151581146111db575f5ffd5b5f5f604083850312156114be575f5ffd5b6114c78361127d565b915060208301356114d7816114a0565b809150509250929050565b634e487b7160e01b5f52604160045260245ffd5b5f5f5f5f60808587031215611509575f5ffd5b6115128561127d565b93506115206020860161127d565b925060408501359150606085013567ffffffffffffffff811115611542575f5ffd5b8501601f81018713611552575f5ffd5b803567ffffffffffffffff81111561156c5761156c6114e2565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561159b5761159b6114e2565b6040528181528282016020018910156115b2575f5ffd5b816020840160208301375f6020838301015280935050505092959194509250565b5f5f5f604084860312156115e5575f5ffd5b833567ffffffffffffffff8111156115fb575f5ffd5b611607868287016112fa565b909450925061161a90506020850161127d565b90509250925092565b5f5f60408385031215611634575f5ffd5b61163d8361127d565b915061164b6020840161127d565b90509250929050565b600181811c9082168061166857607f821691505b60208210810361168657634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52603260045260245ffd5b8183526020830192505f815f5b84811015611436576001600160a01b036116c68361127d565b16865260209586019591909101906001016116ad565b604081525f6116ef6040830186886116a0565b82810360208401528381526001600160fb1b0384111561170d575f5ffd5b8360051b80866020840137016020019695505050505050565b5f81518060208401855e5f93019283525090919050565b5f610b1061174b8386611726565b84611726565b5f60208284031215611761575f5ffd5b5051919050565b5f60208284031215611778575f5ffd5b815161090d816114a0565b6001600160a01b03851681526060602082018190525f906117a790830185876116a0565b82810360408401526117b98185611406565b979650505050505050565b6001600160a01b03858116825284166020820152604081018390526080606082018190525f9061149690830184611226565b5f60208284031215611806575f5ffd5b815161090d816111f656fea2646970667358221220ea53137ed0021ccc270585051151aa3679df3a0072d5456e1d6ac52cc3dc6cdd64736f6c634300081c0033"
 }
diff --git a/scripts/compile-contracts-for-go.sh b/scripts/compile-contracts-for-go.sh
index 75bc327a..76ed238b 100755
--- a/scripts/compile-contracts-for-go.sh
+++ b/scripts/compile-contracts-for-go.sh
@@ -6,7 +6,7 @@ FULL_CONTRACTS_SOURCE="${FULL_CONTRACTS_ROOT}/contracts"
 
 mkdir -p $CONTRACTS_GO_OUTPUT
 
-# for f in $(ls ${FULL_CONTRACTS_ARTIFACTS}/ | awk '!/Test/') ; do
+for f in $(ls ${FULL_CONTRACTS_ARTIFACTS}/ | awk '!/Test/') ; do
     # Uncomment to copy the source file to the contracts directory
     # sourceFile="${FULL_CONTRACTS_SOURCE}/$f"
     # sourceCopy="${CONTRACTS_GO_OUTPUT}/$f"
@@ -15,7 +15,6 @@ mkdir -p $CONTRACTS_GO_OUTPUT
     # Make the contracts/compiled directory
 
     # Get the compiled JSON in solidity/artifacts/contracts/*.sol/*.json, format and output at contracts/compiled/
-    f="ERC20Burnable.sol"
     compiledFile="${FULL_CONTRACTS_ARTIFACTS}/$f/${f%.sol}.json"
     outputFile="${CONTRACTS_GO_OUTPUT}/${f%.sol}.json"
     echo "Formatting JSON at [$compiledFile] and copying to [$outputFile]"
@@ -25,9 +24,6 @@ mkdir -p $CONTRACTS_GO_OUTPUT
 
     # We need to escape the ABI input, which jq can do for us if we first isolate it
     ABI=$(cat $compiledFile | jq -c '.abi' )
-    echo "ABI: $ABI"
-    echo "\n\n\n\n"
-    echo "Bytecode: $BYTECODE"
     # and then pass it as a variable for use in the output
     cat $compiledFile | jq --arg abi_escaped "${ABI}" --arg bytecode "${BYTECODE}" '{ contractName: .contractName, abi: $abi_escaped, bin: $bytecode }' > $outputFile
-# done
\ No newline at end of file
+done
\ No newline at end of file

From 75ece9ed4f8f3780bab40dbacdee4fda863643d3 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Tue, 7 Jan 2025 20:22:30 -0500
Subject: [PATCH 41/63] Update upgrade test for Tethys

---
 integration_tests/test_runner/src/bin/main.rs | 14 +++---
 .../test_runner/src/tests/dex.rs              |  7 ++-
 .../test_runner/src/tests/lockup.rs           |  2 +-
 .../test_runner/src/tests/upgrade.rs          | 46 +++++++++++++++++--
 .../manual-upgrade-test-internal.sh           |  1 +
 5 files changed, 56 insertions(+), 14 deletions(-)

diff --git a/integration_tests/test_runner/src/bin/main.rs b/integration_tests/test_runner/src/bin/main.rs
index ed11de52..938b0a1f 100644
--- a/integration_tests/test_runner/src/bin/main.rs
+++ b/integration_tests/test_runner/src/bin/main.rs
@@ -325,11 +325,13 @@ pub async fn main() {
         } else {
             panic!("Unknown test type: {:?}", test_type);
         }
-    }
 
-    // this checks that the chain is continuing at the end of each test.
-    contact
-        .wait_for_next_block(TOTAL_TIMEOUT)
-        .await
-        .expect("Error chain has halted unexpectedly!");
+        // this checks that the chain is continuing at the end of each test (but not for upgrade part 1, which should halt)
+        if test_type != "UPGRADE_PART_1" {
+            contact
+                .wait_for_next_block(TOTAL_TIMEOUT)
+                .await
+                .expect("Error chain has halted unexpectedly!");
+        }
+    }
 }
diff --git a/integration_tests/test_runner/src/tests/dex.rs b/integration_tests/test_runner/src/tests/dex.rs
index b7774159..1052f4bd 100644
--- a/integration_tests/test_runner/src/tests/dex.rs
+++ b/integration_tests/test_runner/src/tests/dex.rs
@@ -7,10 +7,9 @@ use crate::dex_utils::{
     croc_policy_ops_resolution, croc_policy_treasury_resolution, croc_query_curve_tick,
     croc_query_dex, croc_query_pool_params, croc_query_pool_template, croc_query_price,
     croc_query_range_position, dex_authority_transfer, dex_direct_protocol_cmd,
-    dex_mint_ambient_in_amount, dex_mint_ambient_pos, dex_mint_ranged_in_amount,
-    dex_mint_ranged_pos, dex_query_authority, dex_query_safe_mode, dex_swap, dex_user_cmd,
-    size_ambient_liq, OpsResolutionArgs, ProtocolCmdArgs, SwapArgs, UserCmdArgs, BOOT_PATH,
-    COLD_PATH, MAX_PRICE, MIN_PRICE, WARM_PATH,
+    dex_mint_ambient_in_amount, dex_mint_ranged_in_amount, dex_mint_ranged_pos,
+    dex_query_authority, dex_query_safe_mode, dex_swap, dex_user_cmd, OpsResolutionArgs,
+    ProtocolCmdArgs, SwapArgs, UserCmdArgs, BOOT_PATH, COLD_PATH, MAX_PRICE, MIN_PRICE, WARM_PATH,
 };
 use crate::type_urls::{
     COLLECT_TREASURY_PROPOSAL_TYPE_URL, HOT_PATH_OPEN_PROPOSAL_TYPE_URL, OPS_PROPOSAL_TYPE_URL,
diff --git a/integration_tests/test_runner/src/tests/lockup.rs b/integration_tests/test_runner/src/tests/lockup.rs
index 032bb941..4f9b94ea 100644
--- a/integration_tests/test_runner/src/tests/lockup.rs
+++ b/integration_tests/test_runner/src/tests/lockup.rs
@@ -23,7 +23,7 @@ use clarity::PrivateKey as EthPrivateKey;
 use clarity::Uint256;
 use clarity::{Address as EthAddress, Transaction};
 use deep_space::error::CosmosGrpcError;
-use deep_space::{Address, Coin, Contact, EthermintPrivateKey, Msg, PrivateKey};
+use deep_space::{Address, Coin, Contact, Msg, PrivateKey};
 use num_traits::ToPrimitive;
 use web30::client::Web3;
 
diff --git a/integration_tests/test_runner/src/tests/upgrade.rs b/integration_tests/test_runner/src/tests/upgrade.rs
index 69cf62d1..57fc87b4 100644
--- a/integration_tests/test_runner/src/tests/upgrade.rs
+++ b/integration_tests/test_runner/src/tests/upgrade.rs
@@ -1,6 +1,9 @@
 use crate::utils::{
     execute_upgrade_proposal, wait_for_block, UpgradeProposalParams, ValidatorKeys, EVM_USER_KEYS,
 };
+use althea_proto::cosmos_sdk_proto::cosmos::distribution::v1beta1::{
+    query_client::QueryClient as DistributionQueryClient, QueryParamsRequest,
+};
 use clarity::Address as EthAddress;
 use deep_space::client::ChainStatus;
 use deep_space::{Contact, CosmosPrivateKey};
@@ -158,12 +161,49 @@ pub async fn run_all_recoverable_tests(
 #[allow(clippy::too_many_arguments)]
 pub async fn run_upgrade_specific_tests(
     _web30: &Web3,
-    _althea_contact: &Contact,
+    althea_contact: &Contact,
     _ibc_contact: &Contact,
     _keys: Vec<ValidatorKeys>,
     _ibc_keys: Vec<CosmosPrivateKey>,
     _erc20_addresses: Vec<EthAddress>,
-    _post_upgrade: bool,
+    post_upgrade: bool,
 ) {
-    // TODO: Add any tests here
+    let mut distr_grpc = DistributionQueryClient::connect(althea_contact.get_url())
+        .await
+        .expect("Unable to connect distribution query client");
+
+    let params = distr_grpc
+        .params(QueryParamsRequest {})
+        .await
+        .expect("Unable to get params")
+        .into_inner()
+        .params
+        .expect("No params returned");
+
+    info!("Got Params: {:?}", params);
+
+    // The params values are returned as difficult to handle strings:
+    // 50% would be "500000000000000000", which we divide by 10^18 to account for precision to get 0.5
+    let precision = 10f64.powi(18);
+    let base_pr: f64 = params.base_proposer_reward.parse::<f64>().unwrap() / precision;
+    let bonus_pr: f64 = params.bonus_proposer_reward.parse::<f64>().unwrap() / precision;
+    let epsilon = f64::EPSILON;
+    match post_upgrade {
+        false => {
+            // The base reward should not yet be ~0.5 and the bonus reward should not yet be ~0.04
+            info!(
+                "Expecting base reward {} != 0.5 or bonus reward {} != 0.04",
+                base_pr, bonus_pr
+            );
+            assert!(((base_pr - 0.5).abs() > epsilon) || ((bonus_pr - 0.04).abs() > epsilon));
+        }
+        true => {
+            // The base reward should now be ~0.5 and the bonus reward should now be ~0.04
+            info!(
+                "Expecting base reward {} ~= 0.5 and bonus reward {} ~= 0.04",
+                base_pr, bonus_pr
+            );
+            assert!(((base_pr - 0.5).abs() <= epsilon) && ((bonus_pr - 0.04).abs() <= epsilon));
+        }
+    }
 }
diff --git a/tests/container-scripts/manual-upgrade-test-internal.sh b/tests/container-scripts/manual-upgrade-test-internal.sh
index 6d012bee..aee268b9 100755
--- a/tests/container-scripts/manual-upgrade-test-internal.sh
+++ b/tests/container-scripts/manual-upgrade-test-internal.sh
@@ -55,6 +55,7 @@ read -p "Old binary is running, use tests/run-tests.sh to run tests/populate pre
 unset OLD_BINARY_LOCATION
 # Run the new binary
 pkill oldalthea || true # allowed to fail
+pkill gaiad || true # allowed to fail
 tests/container-scripts/run-testnet.sh $NODES
 
 # This allows the tester to run the first part of the test

From 905fbe307d7598be623b3f5895883616c600dc7c Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Wed, 8 Jan 2025 18:31:23 -0500
Subject: [PATCH 42/63] Minor solidity changes

---
 solidity/contracts/OwnableApprovableERC721.sol | 2 ++
 solidity/scripts/contract-deployer.sh          | 3 +--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/solidity/contracts/OwnableApprovableERC721.sol b/solidity/contracts/OwnableApprovableERC721.sol
index e286bc0c..69c12cdc 100644
--- a/solidity/contracts/OwnableApprovableERC721.sol
+++ b/solidity/contracts/OwnableApprovableERC721.sol
@@ -3,6 +3,7 @@ pragma solidity 0.8.28; // Force solidity compliance
 
 import "@openzeppelin/contracts/utils/Context.sol";
 import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
+
 /**
  * An abstract contract which provides onlyOwner(id) and onlyOwnerOrApproved(id) modifiers derived from ERC721's
  * onwerOf, getApproved, and isApprovedForAll functions
@@ -32,6 +33,7 @@ abstract contract OwnableApprovableERC721 is Context, ERC721 {
     modifier onlyOwnerOrApproved(uint256 tokenId) {
         address owner = _requireOwned(tokenId);
         address sender = _msgSender();
+
         // Get approval directly from ERC721's internal method
         if (owner == sender || _isAuthorized(owner, sender, tokenId)) {
             _;
diff --git a/solidity/scripts/contract-deployer.sh b/solidity/scripts/contract-deployer.sh
index 578d79df..4555f305 100644
--- a/solidity/scripts/contract-deployer.sh
+++ b/solidity/scripts/contract-deployer.sh
@@ -2,5 +2,4 @@
 npx ts-node \
 contract-deployer.ts \
 --eth-node="http://localhost:8545" \
---eth-privkey="0x34d97aaf58b1a81d3ed3068a870d8093c6341cf5d1ef7e6efa03fe7f7fc2c3a8" \
---test-mode=true
+--eth-privkey="0x34d97aaf58b1a81d3ed3068a870d8093c6341cf5d1ef7e6efa03fe7f7fc2c3a8"
\ No newline at end of file

From 5212ffe1147443ba476ba9e4bbc22bcf3a0a3e77 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Thu, 9 Jan 2025 11:16:47 -0500
Subject: [PATCH 43/63] Add solidity contract formatting step to make install

---
 Makefile | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Makefile b/Makefile
index 6bda35ae..37282d93 100644
--- a/Makefile
+++ b/Makefile
@@ -58,6 +58,8 @@ install: go.sum install-core
 
 # does not run go mod verify
 install-core:
+		@echo "Formatting solidity contracts for Cosmos use"
+		sh scripts/compile-contracts-for-go.sh
 		export GOFLAGS='-buildmode=pie'
 		export CGO_CPPFLAGS="-D_FORTIFY_SOURCE=2"
 		export CGO_LDFLAGS="-Wl,-z,relro,-z,now -fstack-protector"

From 8d61901b032c157208d5ce00d5098cd087c7d412 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Thu, 9 Jan 2025 11:32:11 -0500
Subject: [PATCH 44/63] Set evm version to paris after solidity compiler update

---
 solidity/hardhat.config.ts | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/solidity/hardhat.config.ts b/solidity/hardhat.config.ts
index 77d3d6e8..8345e49a 100644
--- a/solidity/hardhat.config.ts
+++ b/solidity/hardhat.config.ts
@@ -30,8 +30,9 @@ module.exports = {
   solidity: {
     version: "0.8.28",
     settings: {
+      evmVersion: "paris",
       optimizer: {
-        enabled: true
+        enabled: true,
       }
     }
   },

From 37cd2fd4fd0e2e405b089e21be630d0adcd2155c Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Fri, 10 Jan 2025 11:14:58 -0500
Subject: [PATCH 45/63] Properly handle contract formatting in CI tests

---
 tests/all-up-test-ci.sh | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/tests/all-up-test-ci.sh b/tests/all-up-test-ci.sh
index 91a0b5ac..d89517ca 100755
--- a/tests/all-up-test-ci.sh
+++ b/tests/all-up-test-ci.sh
@@ -7,11 +7,6 @@ NODES=4
 sudo apt-get update
 sudo apt-get install -y git make gcc g++ iproute2 iputils-ping procps vim tmux net-tools htop tar jq npm libssl-dev perl rustc cargo wget
 
-# Setup Althea L1 binary
-GOPROXY=https://proxy.golang.org make
-make install
-sudo cp ~/go/bin/althea /usr/bin/althea
-
 # Download the althea gaia fork as a IBC test chain
 sudo wget https://github.com/althea-net/ibc-test-chain/releases/download/v9.1.2/gaiad-v9.1.2-linux-amd64 -O /usr/bin/gaiad
 
@@ -30,13 +25,14 @@ sudo touch /ibc-relayer-logs/channel-creation
 # Compile the solidity contracts
 pushd solidity/
 HUSKY_SKIP_INSTALL=1 npm install
-npm run typechain
+npx hardhat compile
 ls -lah
 ls -lah artifacts/
 ls -lah artifacts/contracts/
 pwd
 popd
 
+# Compile the DEX contracts
 git clone https://github.com/AltheaFoundation/althea-dex.git solidity-dex/
 pushd solidity-dex/
 HUSKY_SKIP_INSTALL=1 npm install
@@ -46,6 +42,12 @@ ls -lah misc/scripts/
 pwd
 popd
 
+# Setup Althea L1 binary
+GOPROXY=https://proxy.golang.org make
+make install
+sudo cp ~/go/bin/althea /usr/bin/althea
+
+
 sudo bash tests/container-scripts/setup-validators.sh $NODES
 sudo bash tests/container-scripts/setup-ibc-validators.sh $NODES
 sudo bash tests/container-scripts/run-testnet.sh $NODES $TEST_TYPE

From 63d62bdef822d10bcf3cb5674783a9a94a24ca72 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Fri, 10 Jan 2025 11:17:30 -0500
Subject: [PATCH 46/63] Manually add problematic ERC20Burnable JSON file to
 avoid CI issues

---
 contracts/compiled/ERC20Burnable.json | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 contracts/compiled/ERC20Burnable.json

diff --git a/contracts/compiled/ERC20Burnable.json b/contracts/compiled/ERC20Burnable.json
new file mode 100644
index 00000000..1f8f86c8
--- /dev/null
+++ b/contracts/compiled/ERC20Burnable.json
@@ -0,0 +1,5 @@
+{
+  "contractName": "ERC20Burnable",
+  "abi": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"allowance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientAllowance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSpender\",\"type\":\"error\"},{\"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\":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\"},{\"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\":\"value\",\"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\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"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\":\"value\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
+  "bin": ""
+}

From aa2685fc9e8261c2b9156516fa368bcedae3b6ed Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Fri, 10 Jan 2025 11:21:08 -0500
Subject: [PATCH 47/63] Commit template contracts file for go embeds without
 bin code

---
 contracts/compiled/CrocPolicy.json                     | 4 ++--
 contracts/compiled/ERC20DirectBalanceManipulation.json | 5 +++++
 contracts/compiled/ERC20MaliciousDelayed.json          | 5 +++++
 contracts/compiled/ERC20MinterBurnerDecimals.json      | 5 +++++
 contracts/compiled/ERC20PresetMinterPauser.json        | 5 +++++
 contracts/compiled/LiquidInfrastructureNFT.json        | 4 ++--
 contracts/compiled/OwnableApprovableERC721.json        | 5 +++++
 contracts/compiled/WETH9.json                          | 5 +++++
 8 files changed, 34 insertions(+), 4 deletions(-)
 create mode 100644 contracts/compiled/ERC20DirectBalanceManipulation.json
 create mode 100644 contracts/compiled/ERC20MaliciousDelayed.json
 create mode 100644 contracts/compiled/ERC20MinterBurnerDecimals.json
 create mode 100644 contracts/compiled/ERC20PresetMinterPauser.json
 create mode 100644 contracts/compiled/OwnableApprovableERC721.json
 create mode 100644 contracts/compiled/WETH9.json

diff --git a/contracts/compiled/CrocPolicy.json b/contracts/compiled/CrocPolicy.json
index 8fc8d678..6137e7d5 100644
--- a/contracts/compiled/CrocPolicy.json
+++ b/contracts/compiled/CrocPolicy.json
@@ -1,5 +1,5 @@
 {
   "contractName": "CrocPolicy",
   "abi": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"dex\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"minion\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"CrocEmergencyHalt\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"ops\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"treasury\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"emergency\",\"type\":\"address\"}],\"name\":\"CrocGovernAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"CrocPolicyEmergency\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"proxyPath\",\"type\":\"uint16\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"cmdFlags_\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"mandateTime_\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"expiryOffset_\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct CrocPolicy.PolicyRule\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"CrocPolicyForce\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"proxyPath\",\"type\":\"uint16\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"cmdFlags_\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"mandateTime_\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"expiryOffset_\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct CrocPolicy.PolicyRule\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"CrocPolicySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"minion\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"cmd\",\"type\":\"bytes\"}],\"name\":\"CrocResolutionOps\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"minion\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"sudo\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"cmd\",\"type\":\"bytes\"}],\"name\":\"CrocResolutionTreasury\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptsCrocAuthority\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dex_\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emergencyAuthority_\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minion\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"emergencyHalt\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"proxyPath\",\"type\":\"uint16\"},{\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"emergencyReset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"proxyPath\",\"type\":\"uint16\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"cmdFlags_\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"mandateTime_\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"expiryOffset_\",\"type\":\"uint32\"}],\"internalType\":\"struct CrocPolicy.PolicyRule\",\"name\":\"policy\",\"type\":\"tuple\"}],\"name\":\"forcePolicy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minion\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"proxyPath\",\"type\":\"uint16\"},{\"internalType\":\"bytes\",\"name\":\"cmd\",\"type\":\"bytes\"}],\"name\":\"invokePolicy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"opsAuthority_\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minion\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"proxyPath\",\"type\":\"uint16\"},{\"internalType\":\"bytes\",\"name\":\"cmd\",\"type\":\"bytes\"}],\"name\":\"opsResolution\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"cmdFlags_\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"mandateTime_\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"expiryOffset_\",\"type\":\"uint32\"}],\"internalType\":\"struct CrocPolicy.PolicyRule\",\"name\":\"policy\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"protocolCmd\",\"type\":\"bytes\"}],\"name\":\"passesPolicy\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"rules_\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"cmdFlags_\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"mandateTime_\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"expiryOffset_\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"proxyPath\",\"type\":\"uint16\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"cmdFlags_\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"mandateTime_\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"expiryOffset_\",\"type\":\"uint32\"}],\"internalType\":\"struct CrocPolicy.PolicyRule\",\"name\":\"policy\",\"type\":\"tuple\"}],\"name\":\"setPolicy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"ops\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"treasury\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"emergency\",\"type\":\"address\"}],\"name\":\"transferGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"treasuryAuthority_\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minion\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"proxyPath\",\"type\":\"uint16\"},{\"internalType\":\"bytes\",\"name\":\"cmd\",\"type\":\"bytes\"},{\"internalType\":\"bool\",\"name\":\"sudo\",\"type\":\"bool\"}],\"name\":\"treasuryResolution\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
-  "bin": "60a06040523480156200001157600080fd5b506040516200190f3803806200190f83398101604081905262000034916200013c565b6001600160a01b03811615801590620000ad5750806001600160a01b0316637c5196256040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000087573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000ad91906200016e565b620000fe5760405162461bcd60e51b815260206004820152601360248201527f496e76616c69642043726f635377617044657800000000000000000000000000604482015260640160405180910390fd5b6001600160a01b031660805260008054336001600160a01b031991821681179092556001805482168317905560028054909116909117905562000192565b6000602082840312156200014f57600080fd5b81516001600160a01b03811681146200016757600080fd5b9392505050565b6000602082840312156200018157600080fd5b815180151581146200016757600080fd5b608051611761620001ae60003960006101f401526117616000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c806370ba15291161009757806393b890df1161006657806393b890df14610236578063b30017f414610249578063cdb3453c1461025c578063e7db7f9f1461026f57600080fd5b806370ba1529146101d55780637abda6c2146101e857806387834a0e146101ef5780638e6b5b331461021657600080fd5b8063456a3d6f116100d3578063456a3d6f1461016c5780636573b80f1461018f5780636716c1cc146101af578063700a618e146101c257600080fd5b80631882ad62146100fa57806323de6f871461010f5780632dc2eff814610159575b600080fd5b61010d61010836600461115f565b6102ca565b005b60005461012f9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b61010d6101673660046111c4565b6104c1565b61017f61017a3660046112ce565b61061a565b6040519015158152602001610150565b60025461012f9073ffffffffffffffffffffffffffffffffffffffff1681565b61010d6101bd366004611309565b610678565b61010d6101d0366004611387565b6107c7565b61010d6101e33660046111c4565b6109d5565b600161017f565b61012f7f000000000000000000000000000000000000000000000000000000000000000081565b60015461012f9073ffffffffffffffffffffffffffffffffffffffff1681565b61010d6102443660046113ca565b610b68565b61010d6102573660046111c4565b610c8c565b61010d61026a3660046113ca565b610dcd565b6102a861027d366004611437565b6003602052600090815260409020805460019091015463ffffffff8082169164010000000090041683565b6040805193845263ffffffff9283166020850152911690820152606001610150565b60025473ffffffffffffffffffffffffffffffffffffffff163314610350576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f456d657267656e637920417574686f726974790000000000000000000000000060448201526064015b60405180910390fd5b7f9d9040f16fd81fa6850a0fb7af77b009f9cf07f47dd0970258b8f5745b61f93483838360405161038393929190611499565b60405180910390a160006103976000610fe6565b6040517f13fd34f400000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff8516906313fd34f4906103f29060039085906001906004016114c9565b600060405180830381600087803b15801561040c57600080fd5b505af1158015610420573d6000803e3d6000fd5b5050505061042e6001611017565b6040517f13fd34f400000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff8516906313fd34f4906104899060039085906001906004016114c9565b600060405180830381600087803b1580156104a357600080fd5b505af11580156104b7573d6000803e3d6000fd5b5050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314806104fe575060015473ffffffffffffffffffffffffffffffffffffffff1633145b80610520575060025473ffffffffffffffffffffffffffffffffffffffff1633145b610586576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f4f707320417574686f72697479000000000000000000000000000000000000006044820152606401610347565b7f6e93cf8fe62d42e71a2be801878ed6091e4f46d8fa968a2244f1fbecd0323fdf8483836040516105b993929190611499565b60405180910390a16040517f13fd34f400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516906313fd34f4906104899086908690869060009060040161154a565b600061062584611036565b63ffffffff16610633611052565b63ffffffff161061064657506000610671565b60008383601f81811061065b5761065b61157b565b875192013560f81c9250506001821b1615159150505b9392505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146106f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f547265617375727920417574686f7269747900000000000000000000000000006044820152606401610347565b7f41e689c029f5bbbb232f1cc7e727507291edb0a799c4c946c9871d83d0483ccb8582858560405161072e94939291906115aa565b60405180910390a16040517f13fd34f400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8616906313fd34f49061078e90879087908790879060040161154a565b600060405180830381600087803b1580156107a857600080fd5b505af11580156107bc573d6000803e3d6000fd5b505050505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610848576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f547265617375727920417574686f7269747900000000000000000000000000006044820152606401610347565b6000805473ffffffffffffffffffffffffffffffffffffffff8086167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316178355600180548683169084168117909155600280549286169290931691909117909155604080517f0e18b68100000000000000000000000000000000000000000000000000000000815290519192630e18b6819260048084019382900301818387803b1580156108f857600080fd5b505af115801561090c573d6000803e3d6000fd5b505050508273ffffffffffffffffffffffffffffffffffffffff16630e18b6816040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561095857600080fd5b505af115801561096c573d6000803e3d6000fd5b505050508073ffffffffffffffffffffffffffffffffffffffff16630e18b6816040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156109b857600080fd5b505af11580156109cc573d6000803e3d6000fd5b50505050505050565b6040805133602082015261ffff851691810191909152600090606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600081815260038352839020606085018452805485526001015463ffffffff808216938601939093526401000000009004909116918301919091529150610a6f81858561061a565b610ad5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f506f6c69637920617574686f72697479000000000000000000000000000000006044820152606401610347565b6040517f13fd34f400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8716906313fd34f490610b2e9088908890889060009060040161154a565b600060405180830381600087803b158015610b4857600080fd5b505af1158015610b5c573d6000803e3d6000fd5b50505050505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610be9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f547265617375727920417574686f7269747900000000000000000000000000006044820152606401610347565b6040805173ffffffffffffffffffffffffffffffffffffffff851660208083019190915261ffff85168284015282518083038401815260609092018352815191810191909120600081815260039092529190208290610c4882826115ec565b9050507fdfed5e9490fcd70799fe5d612accbb12924f4cc19d4e63b33999b545a75b2bde848484604051610c7e93929190611656565b60405180910390a150505050565b60025473ffffffffffffffffffffffffffffffffffffffff163314610d0d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f456d657267656e637920417574686f72697479000000000000000000000000006044820152606401610347565b6040805173ffffffffffffffffffffffffffffffffffffffff861660208083019190915261ffff86168284015282518083038401815260609092018084528251928201929092206000818152600390925292812090815560010180547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001690557fa84bd1a75985fc693b6bd5f451f205fa38e3a6fab55022536eeea4720bac5e7090610dbe90879086908690611499565b60405180910390a15050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331480610e0a575060015473ffffffffffffffffffffffffffffffffffffffff1633145b80610e2c575060025473ffffffffffffffffffffffffffffffffffffffff1633145b610e92576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f4f707320417574686f72697479000000000000000000000000000000000000006044820152606401610347565b6040805173ffffffffffffffffffffffffffffffffffffffff851660208083019190915261ffff85168284015282518083038401815260608301808552815191830191909120600081815260039093529184902060c0840190945283548152600184015463ffffffff80821660808601526401000000009091041660a0909301929092529190610f3090610f2b368690038601866116c4565b611071565b610f96576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f496c6c6567616c20706f6c6963792075706461746500000000000000000000006044820152606401610347565b60008281526003602052604090208390610fb082826115ec565b9050507f9db47c8c8636da96a1df945f58d55cc519c7084ae7a7d6e0ecfbf1c9fb76a66a858585604051610dbe93929190611656565b60408051601660208201528215159181019190915260609081015b6040516020818303038152906040529050919050565b6040805160176020820152821515918101919091526060908101611001565b60008160400151826020015161104c91906116e0565b92915050565b60004263ffffffff81111561106c5763ffffffff91505090565b919050565b600061107d838361109b565b156110925761108b836110d0565b905061104c565b50600192915050565b80518251602080850151908401516000931990921615159163ffffffff91821691161081806110c75750805b95945050505050565b6000816020015163ffffffff166110e5611052565b63ffffffff161192915050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461106c57600080fd5b60008083601f84011261112857600080fd5b50813567ffffffffffffffff81111561114057600080fd5b60208301915083602082850101111561115857600080fd5b9250929050565b60008060006040848603121561117457600080fd5b61117d846110f2565b9250602084013567ffffffffffffffff81111561119957600080fd5b6111a586828701611116565b9497909650939450505050565b803561ffff8116811461106c57600080fd5b600080600080606085870312156111da57600080fd5b6111e3856110f2565b93506111f1602086016111b2565b9250604085013567ffffffffffffffff81111561120d57600080fd5b61121987828801611116565b95989497509550505050565b63ffffffff8116811461123757600080fd5b50565b60006060828403121561124c57600080fd5b6040516060810181811067ffffffffffffffff82111715611296577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040528235815290508060208301356112ae81611225565b602082015260408301356112c181611225565b6040919091015292915050565b6000806000608084860312156112e357600080fd5b6112ed858561123a565b9250606084013567ffffffffffffffff81111561119957600080fd5b60008060008060006080868803121561132157600080fd5b61132a866110f2565b9450611338602087016111b2565b9350604086013567ffffffffffffffff81111561135457600080fd5b61136088828901611116565b9094509250506060860135801515811461137957600080fd5b809150509295509295909350565b60008060006060848603121561139c57600080fd5b6113a5846110f2565b92506113b3602085016110f2565b91506113c1604085016110f2565b90509250925092565b600080600083850360a08112156113e057600080fd5b6113e9856110f2565b93506113f7602086016111b2565b925060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08201121561142957600080fd5b506040840190509250925092565b60006020828403121561144957600080fd5b5035919050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff841681526040602082015260006110c7604083018486611450565b61ffff8416815260006020606081840152845180606085015260005b81811015611501578681018301518582016080015282016114e5565b5060006080828601015260807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116850101925050508215156040830152949350505050565b61ffff85168152606060208201526000611568606083018587611450565b9050821515604083015295945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff8516815283151560208201526060604082015260006115e2606083018486611450565b9695505050505050565b8135815560018101602083013561160281611225565b8154604085013561161281611225565b67ffffffff000000008160201b1663ffffffff84167fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000841617178455505050505050565b600060a08201905073ffffffffffffffffffffffffffffffffffffffff8516825261ffff8416602083015282356040830152602083013561169681611225565b63ffffffff90811660608401526040840135906116b282611225565b80821660808501525050949350505050565b6000606082840312156116d657600080fd5b610671838361123a565b63ffffffff818116838216019080821115611724577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b509291505056fea2646970667358221220681568049c13c0d9a1a5b7f8890ae591cb367b0a25b9fc7c2b9cad19ac65523464736f6c63430008130033"
-}
+  "bin": ""
+}
\ No newline at end of file
diff --git a/contracts/compiled/ERC20DirectBalanceManipulation.json b/contracts/compiled/ERC20DirectBalanceManipulation.json
new file mode 100644
index 00000000..0a95406e
--- /dev/null
+++ b/contracts/compiled/ERC20DirectBalanceManipulation.json
@@ -0,0 +1,5 @@
+{
+  "contractName": "ERC20DirectBalanceManipulation",
+  "abi": "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"initialSupply\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"allowance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientAllowance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSpender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EnforcedPause\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExpectedPause\",\"type\":\"error\"},{\"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\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"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\":\"value\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"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\"}],\"name\":\"getRoleMembers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"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\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"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\":\"callerConfirmation\",\"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\":\"recipient\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
+  "bin": ""
+}
\ No newline at end of file
diff --git a/contracts/compiled/ERC20MaliciousDelayed.json b/contracts/compiled/ERC20MaliciousDelayed.json
new file mode 100644
index 00000000..2c5ed1aa
--- /dev/null
+++ b/contracts/compiled/ERC20MaliciousDelayed.json
@@ -0,0 +1,5 @@
+{
+  "contractName": "ERC20MaliciousDelayed",
+  "abi": "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"initialSupply\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"allowance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientAllowance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSpender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EnforcedPause\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExpectedPause\",\"type\":\"error\"},{\"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\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"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\":\"value\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"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\"}],\"name\":\"getRoleMembers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"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\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"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\":\"callerConfirmation\",\"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\":\"recipient\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
+  "bin": ""
+}
\ No newline at end of file
diff --git a/contracts/compiled/ERC20MinterBurnerDecimals.json b/contracts/compiled/ERC20MinterBurnerDecimals.json
new file mode 100644
index 00000000..02559a87
--- /dev/null
+++ b/contracts/compiled/ERC20MinterBurnerDecimals.json
@@ -0,0 +1,5 @@
+{
+  "contractName": "ERC20MinterBurnerDecimals",
+  "abi": "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"decimals_\",\"type\":\"uint8\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ECDSAInvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"ECDSAInvalidSignatureLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"ECDSAInvalidSignatureS\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"allowance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientAllowance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSpender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"}],\"name\":\"ERC2612ExpiredSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"ERC2612InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EnforcedPause\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExpectedPause\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"currentNonce\",\"type\":\"uint256\"}],\"name\":\"InvalidAccountNonce\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidShortString\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"str\",\"type\":\"string\"}],\"name\":\"StringTooLong\",\"type\":\"error\"},{\"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\":[],\"name\":\"EIP712DomainChanged\",\"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\":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\":\"DOMAIN_SEPARATOR\",\"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\":\"value\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"eip712Domain\",\"outputs\":[{\"internalType\":\"bytes1\",\"name\":\"fields\",\"type\":\"bytes1\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"verifyingContract\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"salt\",\"type\":\"bytes32\"},{\"internalType\":\"uint256[]\",\"name\":\"extensions\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"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\"}],\"name\":\"getRoleMembers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"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\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"nonces\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"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\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"permit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"callerConfirmation\",\"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\":\"value\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
+  "bin": ""
+}
\ No newline at end of file
diff --git a/contracts/compiled/ERC20PresetMinterPauser.json b/contracts/compiled/ERC20PresetMinterPauser.json
new file mode 100644
index 00000000..b4b63efd
--- /dev/null
+++ b/contracts/compiled/ERC20PresetMinterPauser.json
@@ -0,0 +1,5 @@
+{
+  "contractName": "ERC20PresetMinterPauser",
+  "abi": "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"allowance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientAllowance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSpender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EnforcedPause\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExpectedPause\",\"type\":\"error\"},{\"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\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"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\":\"value\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"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\"}],\"name\":\"getRoleMembers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"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\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"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\":\"callerConfirmation\",\"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\":\"value\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
+  "bin": ""
+}
\ No newline at end of file
diff --git a/contracts/compiled/LiquidInfrastructureNFT.json b/contracts/compiled/LiquidInfrastructureNFT.json
index 2ee1b3f3..f6aaba80 100644
--- a/contracts/compiled/LiquidInfrastructureNFT.json
+++ b/contracts/compiled/LiquidInfrastructureNFT.json
@@ -1,5 +1,5 @@
 {
   "contractName": "LiquidInfrastructureNFT",
   "abi": "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"accountName\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"ERC721IncorrectOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ERC721InsufficientApproval\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC721InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"ERC721InvalidOperator\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"ERC721InvalidOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC721InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC721InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ERC721NonexistentToken\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"approved\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"ApprovalForAll\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"erc20s\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"}],\"name\":\"SuccessfulRecovery\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"erc20s\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"}],\"name\":\"SuccessfulWithdrawal\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"newErc20s\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"newAmounts\",\"type\":\"uint256[]\"}],\"name\":\"ThresholdsChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"TryRecover\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"AccountId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Version\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"getApproved\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThresholds\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"isApprovedForAll\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ownerOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"recoverAccount\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"setApprovalForAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"newErc20s\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"newAmounts\",\"type\":\"uint256[]\"}],\"name\":\"setThresholds\",\"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\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"tokenURI\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"erc20s\",\"type\":\"address[]\"}],\"name\":\"withdrawBalances\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"erc20s\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"withdrawBalancesTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
-  "bin": "608060405234801561000f575f5ffd5b50604051611f11380380611f1183398101604081905261002e91610459565b8060405160200161003f9190610520565b6040516020818303038152906040528160405160200161005f9190610568565b60408051601f198184030181529190525f61007a8382610603565b5060016100878282610603565b50505061009b3360016100a160201b60201c565b506106bd565b6001600160a01b0382166100cf57604051633250574960e11b81525f60048201526024015b60405180910390fd5b5f6100db83838361010c565b90506001600160a01b03811615610107576040516339e3563760e11b81525f60048201526024016100c6565b505050565b5f828152600260205260408120546001600160a01b0390811690831615610138576101388184866101fe565b6001600160a01b03811615610172576101535f858180610262565b6001600160a01b0381165f90815260036020526040902080545f190190555b6001600160a01b038516156101a0576001600160a01b0385165f908152600360205260409020805460010190555b5f8481526002602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4949350505050565b610209838383610384565b610107576001600160a01b03831661023757604051637e27328960e01b8152600481018290526024016100c6565b60405163177e802f60e01b81526001600160a01b0383166004820152602481018290526044016100c6565b808061027657506001600160a01b03821615155b15610355575f61028584610407565b90506001600160a01b038316158015906102b15750826001600160a01b0316816001600160a01b031614155b80156102e257506001600160a01b038082165f9081526005602090815260408083209387168352929052205460ff16155b1561030b5760405163a9fbf51f60e01b81526001600160a01b03841660048201526024016100c6565b81156103535783856001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b50505f90815260046020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b5f6001600160a01b038316158015906103ff5750826001600160a01b0316846001600160a01b031614806103dc57506001600160a01b038085165f9081526005602090815260408083209387168352929052205460ff165b806103ff57505f828152600460205260409020546001600160a01b038481169116145b949350505050565b5f818152600260205260408120546001600160a01b03168061043f57604051637e27328960e01b8152600481018490526024016100c6565b92915050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215610469575f5ffd5b81516001600160401b0381111561047e575f5ffd5b8201601f8101841361048e575f5ffd5b80516001600160401b038111156104a7576104a7610445565b604051601f8201601f19908116603f011681016001600160401b03811182821017156104d5576104d5610445565b6040528181528282016020018610156104ec575f5ffd5b8160208401602083015e5f91810160200191909152949350505050565b5f81518060208401855e5f93019283525090919050565b7f616c746865613a2f2f6c69717569642d696e6672617374727563747572652d6181526663636f756e742f60c81b60208201525f6105616027830184610509565b9392505050565b632624a09d60e11b81525f6105616004830184610509565b600181811c9082168061059457607f821691505b6020821081036105b257634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561010757805f5260205f20601f840160051c810160208510156105dd5750805b601f840160051c820191505b818110156105fc575f81556001016105e9565b5050505050565b81516001600160401b0381111561061c5761061c610445565b6106308161062a8454610580565b846105b8565b6020601f821160018114610662575f831561064b5750848201515b5f19600385901b1c1916600184901b1784556105fc565b5f84815260208120601f198516915b828110156106915787850151825560209485019460019092019101610671565b50848210156106ae57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b611847806106ca5f395ff3fe608060405234801561000f575f5ffd5b5060043610610127575f3560e01c80638b702802116100a9578063bb62860d1161006e578063bb62860d146101f4578063bfe9d75714610274578063c87b56dd1461027c578063cb0e095c1461028f578063e985e9c5146102a2575f5ffd5b80638b7028021461021d57806395d89b41146102305780639c3977b514610238578063a22cb4651461024e578063b88d4fde14610261575f5ffd5b806342842e0e116100ef57806342842e0e146101bb578063570fa5bc146101ce5780636352211e146101e15780636cf324ef146101f457806370a082311461020a575f5ffd5b806301ffc9a71461012b57806306fdde0314610153578063081812fc14610168578063095ea7b31461019357806323b872dd146101a8575b5f5ffd5b61013e61013936600461120b565b6102b5565b60405190151581526020015b60405180910390f35b61015b610306565b60405161014a9190611254565b61017b610176366004611266565b610395565b6040516001600160a01b03909116815260200161014a565b6101a66101a1366004611298565b6103bc565b005b6101a66101b63660046112c0565b6103cb565b6101a66101c93660046112c0565b610459565b6101a66101dc366004611342565b610478565b61017b6101ef366004611266565b610672565b6101fc600181565b60405190815260200161014a565b6101fc6102183660046113ae565b61067c565b6101a661022b3660046113c7565b6106c1565b61015b610713565b610240610722565b60405161014a929190611440565b6101a661025c3660046114ad565b6107db565b6101a661026f3660046114f6565b6107e6565b6101a66107fe565b61015b61028a366004611266565b6108a3565b6101a661029d3660046115d3565b610914565b61013e6102b0366004611623565b610950565b5f6001600160e01b031982166380ac58cd60e01b14806102e557506001600160e01b03198216635b5e139f60e01b145b8061030057506301ffc9a760e01b6001600160e01b03198316145b92915050565b60605f805461031490611654565b80601f016020809104026020016040519081016040528092919081815260200182805461034090611654565b801561038b5780601f106103625761010080835404028352916020019161038b565b820191905f5260205f20905b81548152906001019060200180831161036e57829003601f168201915b5050505050905090565b5f61039f8261097d565b505f828152600460205260409020546001600160a01b0316610300565b6103c78282336109b5565b5050565b6001600160a01b0382166103f957604051633250574960e11b81525f60048201526024015b60405180910390fd5b5f6104058383336109c2565b9050836001600160a01b0316816001600160a01b031614610453576040516364283d7b60e01b81526001600160a01b03808616600483015260248201849052821660448201526064016103f0565b50505050565b61047383838360405180602001604052805f8152506107e6565b505050565b60015f6104848261097d565b9050336001600160a01b0382168114806104a457506104a4828285610ab4565b156106055785841461050b5760405162461bcd60e51b815260206004820152602a60248201527f7468726573686f6c642076616c756573206d75737420686176652074686520736044820152690c2daca40d8cadccee8d60b31b60648201526084016103f0565b61051660065f6111c0565b61052160075f6111c0565b5f5b868110156105c257600688888381811061053f5761053f61168c565b905060200201602081019061055491906113ae565b81546001810183555f928352602090922090910180546001600160a01b0319166001600160a01b0390921691909117905560078686838181106105995761059961168c565b8354600180820186555f9586526020958690209290950293909301359201919091555001610523565b507f654ec6a6c41d997f6ceb2748132213f6ee151c84895ef4a08f17b5743754809b878787876040516105f894939291906116dc565b60405180910390a1610669565b60405162461bcd60e51b815260206004820152603360248201527f4f776e61626c65417070726f7661626c653a2063616c6c6572206973206e6f74604482015272081bdddb995c881b9bdc88185c1c1c9bdd9959606a1b60648201526084016103f0565b50505050505050565b5f6103008261097d565b5f6001600160a01b0382166106a6576040516322718ad960e21b81525f60048201526024016103f0565b506001600160a01b03165f9081526003602052604090205490565b60015f6106cd8261097d565b9050336001600160a01b0382168114806106ed57506106ed828285610ab4565b15610605575f6106fd6001610672565b905061070a868683610b18565b505b5050505050565b60606001805461031490611654565b606080600660078180548060200260200160405190810160405280929190818152602001828054801561077c57602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161075e575b50505050509150808054806020026020016040519081016040528092919081815260200182805480156107cc57602002820191905f5260205f20905b8154815260200190600101908083116107b8575b50505050509050915091509091565b6103c7338383610d34565b6107f18484846103cb565b6104533385858585610dd2565b60015f61080a8261097d565b90506001600160a01b03811633146108775760405162461bcd60e51b815260206004820152602a60248201527f4f776e61626c65417070726f7661626c653a2063616c6c6572206973206e6f74604482015269103a34329037bbb732b960b11b60648201526084016103f0565b6040517f9dbe82bd7c9a3230967fa6a4082f47590628e191353da33cef87339fbd749347905f90a15050565b60606108ae8261097d565b505f6108c460408051602081019091525f815290565b90505f8151116108e25760405180602001604052805f81525061090d565b806108ec84610ef1565b6040516020016108fd92919061173d565b6040516020818303038152906040525b9392505050565b60015f6109208261097d565b9050336001600160a01b0382168114806109405750610940828285610ab4565b156106055761070a868686610b18565b6001600160a01b039182165f90815260056020908152604080832093909416825291909152205460ff1690565b5f818152600260205260408120546001600160a01b03168061030057604051637e27328960e01b8152600481018490526024016103f0565b6104738383836001610f81565b5f828152600260205260408120546001600160a01b03908116908316156109ee576109ee818486611085565b6001600160a01b03811615610a2857610a095f855f5f610f81565b6001600160a01b0381165f90815260036020526040902080545f190190555b6001600160a01b03851615610a56576001600160a01b0385165f908152600360205260409020805460010190555b5f8481526002602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4949350505050565b5f6001600160a01b03831615801590610b105750826001600160a01b0316846001600160a01b03161480610aed5750610aed8484610950565b80610b1057505f828152600460205260409020546001600160a01b038481169116145b949350505050565b5f8267ffffffffffffffff811115610b3257610b326114e2565b604051908082528060200260200182016040528015610b5b578160200160208202803683370190505b5090505f5b83811015610cf0575f858583818110610b7b57610b7b61168c565b9050602002016020810190610b9091906113ae565b6040516370a0823160e01b81523060048201529091505f906001600160a01b038316906370a0823190602401602060405180830381865afa158015610bd7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bfb9190611751565b90508015610ce65760405163a9059cbb60e01b81526001600160a01b038681166004830152602482018390525f919084169063a9059cbb906044016020604051808303815f875af1158015610c52573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c769190611768565b905080610cc55760405162461bcd60e51b815260206004820152601760248201527f756e7375636365737366756c207769746864726177616c00000000000000000060448201526064016103f0565b81858581518110610cd857610cd861168c565b602002602001018181525050505b5050600101610b60565b507fd09f7c02747f1be0aa80758e1857fdf675f9cb71ff40e5ef7701758fa1788e3082858584604051610d269493929190611783565b60405180910390a150505050565b6001600160a01b038216610d6657604051630b61174360e31b81526001600160a01b03831660048201526024016103f0565b6001600160a01b038381165f81815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6001600160a01b0383163b1561070c57604051630a85bd0160e11b81526001600160a01b0384169063150b7a0290610e149088908890879087906004016117c4565b6020604051808303815f875af1925050508015610e4e575060408051601f3d908101601f19168201909252610e4b918101906117f6565b60015b610eb5573d808015610e7b576040519150601f19603f3d011682016040523d82523d5f602084013e610e80565b606091505b5080515f03610ead57604051633250574960e11b81526001600160a01b03851660048201526024016103f0565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b1461070a57604051633250574960e11b81526001600160a01b03851660048201526024016103f0565b60605f610efd836110e9565b60010190505f8167ffffffffffffffff811115610f1c57610f1c6114e2565b6040519080825280601f01601f191660200182016040528015610f46576020820181803683370190505b5090508181016020015b5f19016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a8504945084610f5057509392505050565b8080610f9557506001600160a01b03821615155b15611056575f610fa48461097d565b90506001600160a01b03831615801590610fd05750826001600160a01b0316816001600160a01b031614155b8015610fe35750610fe18184610950565b155b1561100c5760405163a9fbf51f60e01b81526001600160a01b03841660048201526024016103f0565b81156110545783856001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b50505f90815260046020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b611090838383610ab4565b610473576001600160a01b0383166110be57604051637e27328960e01b8152600481018290526024016103f0565b60405163177e802f60e01b81526001600160a01b0383166004820152602481018290526044016103f0565b5f8072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106111275772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310611153576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061117157662386f26fc10000830492506010015b6305f5e1008310611189576305f5e100830492506008015b612710831061119d57612710830492506004015b606483106111af576064830492506002015b600a83106103005760010192915050565b5080545f8255905f5260205f20908101906111db91906111de565b50565b5b808211156111f2575f81556001016111df565b5090565b6001600160e01b0319811681146111db575f5ffd5b5f6020828403121561121b575f5ffd5b813561090d816111f6565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f61090d6020830184611226565b5f60208284031215611276575f5ffd5b5035919050565b80356001600160a01b0381168114611293575f5ffd5b919050565b5f5f604083850312156112a9575f5ffd5b6112b28361127d565b946020939093013593505050565b5f5f5f606084860312156112d2575f5ffd5b6112db8461127d565b92506112e96020850161127d565b929592945050506040919091013590565b5f5f83601f84011261130a575f5ffd5b50813567ffffffffffffffff811115611321575f5ffd5b6020830191508360208260051b850101111561133b575f5ffd5b9250929050565b5f5f5f5f60408587031215611355575f5ffd5b843567ffffffffffffffff81111561136b575f5ffd5b611377878288016112fa565b909550935050602085013567ffffffffffffffff811115611396575f5ffd5b6113a2878288016112fa565b95989497509550505050565b5f602082840312156113be575f5ffd5b61090d8261127d565b5f5f602083850312156113d8575f5ffd5b823567ffffffffffffffff8111156113ee575f5ffd5b6113fa858286016112fa565b90969095509350505050565b5f8151808452602084019350602083015f5b82811015611436578151865260209586019590910190600101611418565b5093949350505050565b604080825283519082018190525f9060208501906060840190835b818110156114825783516001600160a01b031683526020938401939092019160010161145b565b505083810360208501526114968186611406565b9695505050505050565b80151581146111db575f5ffd5b5f5f604083850312156114be575f5ffd5b6114c78361127d565b915060208301356114d7816114a0565b809150509250929050565b634e487b7160e01b5f52604160045260245ffd5b5f5f5f5f60808587031215611509575f5ffd5b6115128561127d565b93506115206020860161127d565b925060408501359150606085013567ffffffffffffffff811115611542575f5ffd5b8501601f81018713611552575f5ffd5b803567ffffffffffffffff81111561156c5761156c6114e2565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561159b5761159b6114e2565b6040528181528282016020018910156115b2575f5ffd5b816020840160208301375f6020838301015280935050505092959194509250565b5f5f5f604084860312156115e5575f5ffd5b833567ffffffffffffffff8111156115fb575f5ffd5b611607868287016112fa565b909450925061161a90506020850161127d565b90509250925092565b5f5f60408385031215611634575f5ffd5b61163d8361127d565b915061164b6020840161127d565b90509250929050565b600181811c9082168061166857607f821691505b60208210810361168657634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52603260045260245ffd5b8183526020830192505f815f5b84811015611436576001600160a01b036116c68361127d565b16865260209586019591909101906001016116ad565b604081525f6116ef6040830186886116a0565b82810360208401528381526001600160fb1b0384111561170d575f5ffd5b8360051b80866020840137016020019695505050505050565b5f81518060208401855e5f93019283525090919050565b5f610b1061174b8386611726565b84611726565b5f60208284031215611761575f5ffd5b5051919050565b5f60208284031215611778575f5ffd5b815161090d816114a0565b6001600160a01b03851681526060602082018190525f906117a790830185876116a0565b82810360408401526117b98185611406565b979650505050505050565b6001600160a01b03858116825284166020820152604081018390526080606082018190525f9061149690830184611226565b5f60208284031215611806575f5ffd5b815161090d816111f656fea2646970667358221220ea53137ed0021ccc270585051151aa3679df3a0072d5456e1d6ac52cc3dc6cdd64736f6c634300081c0033"
-}
+  "bin": ""
+}
\ No newline at end of file
diff --git a/contracts/compiled/OwnableApprovableERC721.json b/contracts/compiled/OwnableApprovableERC721.json
new file mode 100644
index 00000000..3a3f9a34
--- /dev/null
+++ b/contracts/compiled/OwnableApprovableERC721.json
@@ -0,0 +1,5 @@
+{
+  "contractName": "OwnableApprovableERC721",
+  "abi": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"ERC721IncorrectOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ERC721InsufficientApproval\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC721InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"ERC721InvalidOperator\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"ERC721InvalidOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC721InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC721InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ERC721NonexistentToken\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"approved\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"ApprovalForAll\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"getApproved\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"isApprovedForAll\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ownerOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"setApprovalForAll\",\"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\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"tokenURI\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
+  "bin": ""
+}
diff --git a/contracts/compiled/WETH9.json b/contracts/compiled/WETH9.json
new file mode 100644
index 00000000..5f81ea0f
--- /dev/null
+++ b/contracts/compiled/WETH9.json
@@ -0,0 +1,5 @@
+{
+  "contractName": "WETH9",
+  "abi": "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"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\":\"dst\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]",
+  "bin": ""
+}
\ No newline at end of file

From 14583fc3655631951949783a40bf8b5a20ee1502 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Fri, 10 Jan 2025 11:27:50 -0500
Subject: [PATCH 48/63] WIP: Set problematic contracts to have a single 0 byte
 as bincode

---
 contracts/compiled/CrocPolicy.json                     | 2 +-
 contracts/compiled/ERC20Burnable.json                  | 4 ++--
 contracts/compiled/ERC20DirectBalanceManipulation.json | 2 +-
 contracts/compiled/ERC20MaliciousDelayed.json          | 2 +-
 contracts/compiled/ERC20MinterBurnerDecimals.json      | 2 +-
 contracts/compiled/ERC20PresetMinterPauser.json        | 2 +-
 contracts/compiled/LiquidInfrastructureNFT.json        | 2 +-
 contracts/compiled/OwnableApprovableERC721.json        | 4 ++--
 contracts/compiled/WETH9.json                          | 2 +-
 9 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/contracts/compiled/CrocPolicy.json b/contracts/compiled/CrocPolicy.json
index 6137e7d5..7a1a415b 100644
--- a/contracts/compiled/CrocPolicy.json
+++ b/contracts/compiled/CrocPolicy.json
@@ -1,5 +1,5 @@
 {
   "contractName": "CrocPolicy",
   "abi": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"dex\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"minion\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"CrocEmergencyHalt\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"ops\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"treasury\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"emergency\",\"type\":\"address\"}],\"name\":\"CrocGovernAuthority\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"CrocPolicyEmergency\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"proxyPath\",\"type\":\"uint16\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"cmdFlags_\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"mandateTime_\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"expiryOffset_\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct CrocPolicy.PolicyRule\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"CrocPolicyForce\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"proxyPath\",\"type\":\"uint16\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"cmdFlags_\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"mandateTime_\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"expiryOffset_\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"struct CrocPolicy.PolicyRule\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"CrocPolicySet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"minion\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"cmd\",\"type\":\"bytes\"}],\"name\":\"CrocResolutionOps\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"minion\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"sudo\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"cmd\",\"type\":\"bytes\"}],\"name\":\"CrocResolutionTreasury\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptsCrocAuthority\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dex_\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emergencyAuthority_\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minion\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"emergencyHalt\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"proxyPath\",\"type\":\"uint16\"},{\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"emergencyReset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"proxyPath\",\"type\":\"uint16\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"cmdFlags_\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"mandateTime_\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"expiryOffset_\",\"type\":\"uint32\"}],\"internalType\":\"struct CrocPolicy.PolicyRule\",\"name\":\"policy\",\"type\":\"tuple\"}],\"name\":\"forcePolicy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minion\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"proxyPath\",\"type\":\"uint16\"},{\"internalType\":\"bytes\",\"name\":\"cmd\",\"type\":\"bytes\"}],\"name\":\"invokePolicy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"opsAuthority_\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minion\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"proxyPath\",\"type\":\"uint16\"},{\"internalType\":\"bytes\",\"name\":\"cmd\",\"type\":\"bytes\"}],\"name\":\"opsResolution\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"cmdFlags_\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"mandateTime_\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"expiryOffset_\",\"type\":\"uint32\"}],\"internalType\":\"struct CrocPolicy.PolicyRule\",\"name\":\"policy\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"protocolCmd\",\"type\":\"bytes\"}],\"name\":\"passesPolicy\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"rules_\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"cmdFlags_\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"mandateTime_\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"expiryOffset_\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"proxyPath\",\"type\":\"uint16\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"cmdFlags_\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"mandateTime_\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"expiryOffset_\",\"type\":\"uint32\"}],\"internalType\":\"struct CrocPolicy.PolicyRule\",\"name\":\"policy\",\"type\":\"tuple\"}],\"name\":\"setPolicy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"ops\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"treasury\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"emergency\",\"type\":\"address\"}],\"name\":\"transferGovernance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"treasuryAuthority_\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minion\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"proxyPath\",\"type\":\"uint16\"},{\"internalType\":\"bytes\",\"name\":\"cmd\",\"type\":\"bytes\"},{\"internalType\":\"bool\",\"name\":\"sudo\",\"type\":\"bool\"}],\"name\":\"treasuryResolution\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
-  "bin": ""
+  "bin": "00"
 }
\ No newline at end of file
diff --git a/contracts/compiled/ERC20Burnable.json b/contracts/compiled/ERC20Burnable.json
index 1f8f86c8..790692c8 100644
--- a/contracts/compiled/ERC20Burnable.json
+++ b/contracts/compiled/ERC20Burnable.json
@@ -1,5 +1,5 @@
 {
   "contractName": "ERC20Burnable",
   "abi": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"allowance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientAllowance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSpender\",\"type\":\"error\"},{\"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\":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\"},{\"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\":\"value\",\"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\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"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\":\"value\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
-  "bin": ""
-}
+  "bin": "00"
+}
\ No newline at end of file
diff --git a/contracts/compiled/ERC20DirectBalanceManipulation.json b/contracts/compiled/ERC20DirectBalanceManipulation.json
index 0a95406e..6dbcaa44 100644
--- a/contracts/compiled/ERC20DirectBalanceManipulation.json
+++ b/contracts/compiled/ERC20DirectBalanceManipulation.json
@@ -1,5 +1,5 @@
 {
   "contractName": "ERC20DirectBalanceManipulation",
   "abi": "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"initialSupply\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"allowance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientAllowance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSpender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EnforcedPause\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExpectedPause\",\"type\":\"error\"},{\"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\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"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\":\"value\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"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\"}],\"name\":\"getRoleMembers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"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\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"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\":\"callerConfirmation\",\"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\":\"recipient\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
-  "bin": ""
+  "bin": "00"
 }
\ No newline at end of file
diff --git a/contracts/compiled/ERC20MaliciousDelayed.json b/contracts/compiled/ERC20MaliciousDelayed.json
index 2c5ed1aa..ecda75b4 100644
--- a/contracts/compiled/ERC20MaliciousDelayed.json
+++ b/contracts/compiled/ERC20MaliciousDelayed.json
@@ -1,5 +1,5 @@
 {
   "contractName": "ERC20MaliciousDelayed",
   "abi": "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"initialSupply\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"allowance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientAllowance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSpender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EnforcedPause\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExpectedPause\",\"type\":\"error\"},{\"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\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"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\":\"value\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"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\"}],\"name\":\"getRoleMembers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"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\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"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\":\"callerConfirmation\",\"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\":\"recipient\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
-  "bin": ""
+  "bin": "00"
 }
\ No newline at end of file
diff --git a/contracts/compiled/ERC20MinterBurnerDecimals.json b/contracts/compiled/ERC20MinterBurnerDecimals.json
index 02559a87..a0c57df7 100644
--- a/contracts/compiled/ERC20MinterBurnerDecimals.json
+++ b/contracts/compiled/ERC20MinterBurnerDecimals.json
@@ -1,5 +1,5 @@
 {
   "contractName": "ERC20MinterBurnerDecimals",
   "abi": "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"decimals_\",\"type\":\"uint8\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ECDSAInvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"ECDSAInvalidSignatureLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"ECDSAInvalidSignatureS\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"allowance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientAllowance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSpender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"}],\"name\":\"ERC2612ExpiredSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"ERC2612InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EnforcedPause\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExpectedPause\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"currentNonce\",\"type\":\"uint256\"}],\"name\":\"InvalidAccountNonce\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidShortString\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"str\",\"type\":\"string\"}],\"name\":\"StringTooLong\",\"type\":\"error\"},{\"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\":[],\"name\":\"EIP712DomainChanged\",\"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\":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\":\"DOMAIN_SEPARATOR\",\"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\":\"value\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"eip712Domain\",\"outputs\":[{\"internalType\":\"bytes1\",\"name\":\"fields\",\"type\":\"bytes1\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"verifyingContract\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"salt\",\"type\":\"bytes32\"},{\"internalType\":\"uint256[]\",\"name\":\"extensions\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"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\"}],\"name\":\"getRoleMembers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"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\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"nonces\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"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\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"permit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"callerConfirmation\",\"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\":\"value\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
-  "bin": ""
+  "bin": "00"
 }
\ No newline at end of file
diff --git a/contracts/compiled/ERC20PresetMinterPauser.json b/contracts/compiled/ERC20PresetMinterPauser.json
index b4b63efd..c17bab92 100644
--- a/contracts/compiled/ERC20PresetMinterPauser.json
+++ b/contracts/compiled/ERC20PresetMinterPauser.json
@@ -1,5 +1,5 @@
 {
   "contractName": "ERC20PresetMinterPauser",
   "abi": "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"allowance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientAllowance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSpender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EnforcedPause\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExpectedPause\",\"type\":\"error\"},{\"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\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"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\":\"value\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"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\"}],\"name\":\"getRoleMembers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"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\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"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\":\"callerConfirmation\",\"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\":\"value\",\"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\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
-  "bin": ""
+  "bin": "00"
 }
\ No newline at end of file
diff --git a/contracts/compiled/LiquidInfrastructureNFT.json b/contracts/compiled/LiquidInfrastructureNFT.json
index f6aaba80..8630b196 100644
--- a/contracts/compiled/LiquidInfrastructureNFT.json
+++ b/contracts/compiled/LiquidInfrastructureNFT.json
@@ -1,5 +1,5 @@
 {
   "contractName": "LiquidInfrastructureNFT",
   "abi": "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"accountName\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"ERC721IncorrectOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ERC721InsufficientApproval\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC721InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"ERC721InvalidOperator\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"ERC721InvalidOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC721InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC721InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ERC721NonexistentToken\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"approved\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"ApprovalForAll\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"erc20s\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"}],\"name\":\"SuccessfulRecovery\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"erc20s\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"}],\"name\":\"SuccessfulWithdrawal\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"newErc20s\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"newAmounts\",\"type\":\"uint256[]\"}],\"name\":\"ThresholdsChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"TryRecover\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"AccountId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Version\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"getApproved\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThresholds\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"isApprovedForAll\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ownerOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"recoverAccount\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"setApprovalForAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"newErc20s\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"newAmounts\",\"type\":\"uint256[]\"}],\"name\":\"setThresholds\",\"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\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"tokenURI\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"erc20s\",\"type\":\"address[]\"}],\"name\":\"withdrawBalances\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"erc20s\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"withdrawBalancesTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
-  "bin": ""
+  "bin": "00"
 }
\ No newline at end of file
diff --git a/contracts/compiled/OwnableApprovableERC721.json b/contracts/compiled/OwnableApprovableERC721.json
index 3a3f9a34..cc8e7a88 100644
--- a/contracts/compiled/OwnableApprovableERC721.json
+++ b/contracts/compiled/OwnableApprovableERC721.json
@@ -1,5 +1,5 @@
 {
   "contractName": "OwnableApprovableERC721",
   "abi": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"ERC721IncorrectOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ERC721InsufficientApproval\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC721InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"ERC721InvalidOperator\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"ERC721InvalidOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC721InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC721InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ERC721NonexistentToken\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"approved\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"ApprovalForAll\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"getApproved\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"isApprovedForAll\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ownerOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"setApprovalForAll\",\"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\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"tokenURI\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
-  "bin": ""
-}
+  "bin": "00"
+}
\ No newline at end of file
diff --git a/contracts/compiled/WETH9.json b/contracts/compiled/WETH9.json
index 5f81ea0f..5b58042c 100644
--- a/contracts/compiled/WETH9.json
+++ b/contracts/compiled/WETH9.json
@@ -1,5 +1,5 @@
 {
   "contractName": "WETH9",
   "abi": "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"guy\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"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\":\"dst\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"wad\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]",
-  "bin": ""
+  "bin": "00"
 }
\ No newline at end of file

From 7fe31fe4d3bbc253b1b17508c2c790205209cbab Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Fri, 10 Jan 2025 12:30:56 -0500
Subject: [PATCH 49/63] Add missing Ops proposal registration to nativedex
 init()

---
 x/nativedex/types/proposal.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/x/nativedex/types/proposal.go b/x/nativedex/types/proposal.go
index 70118850..45d699c9 100644
--- a/x/nativedex/types/proposal.go
+++ b/x/nativedex/types/proposal.go
@@ -45,6 +45,7 @@ func init() {
 	govv1beta1.RegisterProposalType(ProposalTypeHotPathOpen)
 	govv1beta1.RegisterProposalType(ProposalTypeSetSafeMode)
 	govv1beta1.RegisterProposalType(ProposalTypeTransferGovernance)
+	govv1beta1.RegisterProposalType(ProposalTypeOps)
 }
 
 func NewUpgradeProxyProposal(title, description string, md UpgradeProxyMetadata) govv1beta1.Content {

From ac6ebbfdf1a370dbe3bdb71382af5bb44ae59859 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Fri, 10 Jan 2025 14:39:57 -0500
Subject: [PATCH 50/63] Fix DEX test by minting large ambient position

---
 integration_tests/test_runner/src/tests/dex.rs | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/integration_tests/test_runner/src/tests/dex.rs b/integration_tests/test_runner/src/tests/dex.rs
index 1052f4bd..20911732 100644
--- a/integration_tests/test_runner/src/tests/dex.rs
+++ b/integration_tests/test_runner/src/tests/dex.rs
@@ -131,6 +131,22 @@ pub async fn populate_pool_basic(
         .await
         .expect("Unable to approve erc20");
     }
+
+    let ambient_qty = one_eth() * 100000u32.into();
+    dex_mint_ambient_in_amount(
+        web3,
+        dex_contracts.dex,
+        dex_contracts.query,
+        evm_user.eth_privkey,
+        evm_user.eth_address,
+        base,
+        quote,
+        *POOL_IDX,
+        ambient_qty,
+        false,
+    )
+    .await;
+
     let tick = Int256::zero();
 
     let bid_tick = tick - 75u8.into();
@@ -154,7 +170,7 @@ pub async fn populate_pool_basic(
     if range_pos.liq > 0u8.into() {
         info!("Range position already exists: {:?}", range_pos);
     } else {
-        let qty: Uint256 = 1024000000000000000u128.into(); // 1.024 eth
+        let qty: Uint256 = one_eth() * 1024u32.into(); // 1024 eth
         let bb = web3
             .get_erc20_balance(base, evm_user.eth_address)
             .await

From 3b0e0cbd4fe3377a763e27512df84bf0b02d6551 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Fri, 10 Jan 2025 15:04:29 -0500
Subject: [PATCH 51/63] Update IBC test chain in CI tests

---
 tests/all-up-test-ci.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/all-up-test-ci.sh b/tests/all-up-test-ci.sh
index d89517ca..ede1bf3e 100755
--- a/tests/all-up-test-ci.sh
+++ b/tests/all-up-test-ci.sh
@@ -8,7 +8,7 @@ sudo apt-get update
 sudo apt-get install -y git make gcc g++ iproute2 iputils-ping procps vim tmux net-tools htop tar jq npm libssl-dev perl rustc cargo wget
 
 # Download the althea gaia fork as a IBC test chain
-sudo wget https://github.com/althea-net/ibc-test-chain/releases/download/v9.1.2/gaiad-v9.1.2-linux-amd64 -O /usr/bin/gaiad
+sudo wget https://github.com/althea-net/ibc-test-chain/releases/download/v9.1.5/gaiad-v9.1.5-linux-amd64 -O /usr/bin/gaiad
 
 # Setup Hermes for IBC connections between chains
 pushd /tmp/

From 35a4a959a1cce4ae215b8fa43b2993b85860fe96 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Fri, 10 Jan 2025 21:25:35 -0500
Subject: [PATCH 52/63] Make ERC20_CONVERSION more idempotent

---
 integration_tests/test_runner/src/utils.rs | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/integration_tests/test_runner/src/utils.rs b/integration_tests/test_runner/src/utils.rs
index 69dabc14..dbffe071 100644
--- a/integration_tests/test_runner/src/utils.rs
+++ b/integration_tests/test_runner/src/utils.rs
@@ -392,6 +392,21 @@ pub async fn execute_register_erc20_proposal(
     timeout: Option<Duration>,
     erc20_params: RegisterErc20ProposalParams,
 ) {
+    let mut erc20_qc = Erc20QueryClient::connect(contact.get_url())
+        .await
+        .expect("Unable to connect to erc20 query client");
+    let pair = erc20_qc
+        .token_pair(QueryTokenPairRequest {
+            token: erc20_params.erc20_address.clone(),
+        })
+        .await;
+
+    if let Ok(pair) = pair {
+        if pair.into_inner().token_pair.is_some() {
+            info!("ERC20 token already has a token pair registered");
+            return;
+        }
+    }
     let duration = match timeout {
         Some(dur) => dur,
         None => OPERATION_TIMEOUT,

From 226b9150e35067e1ce63d27e6ed5c6b4a95809a5 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Fri, 10 Jan 2025 21:26:10 -0500
Subject: [PATCH 53/63] Improve test start logging

---
 integration_tests/test_runner/src/bin/main.rs    | 16 ----------------
 integration_tests/test_runner/src/tests/dex.rs   |  6 ++++++
 .../test_runner/src/tests/erc20_conversion.rs    |  1 +
 .../test_runner/src/tests/evm_fee_burning.rs     |  1 +
 .../test_runner/src/tests/ica_host.rs            |  1 +
 .../test_runner/src/tests/lockup.rs              |  1 +
 .../test_runner/src/tests/microtx_fees.rs        |  1 +
 .../test_runner/src/tests/onboarding.rs          |  4 ++++
 8 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/integration_tests/test_runner/src/bin/main.rs b/integration_tests/test_runner/src/bin/main.rs
index 938b0a1f..370622a5 100644
--- a/integration_tests/test_runner/src/bin/main.rs
+++ b/integration_tests/test_runner/src/bin/main.rs
@@ -117,7 +117,6 @@ pub async fn main() {
     info!("Starting tests with {:?}", test_type);
     if let Ok(test_type) = test_type {
         if test_type == "LOCKUP" {
-            info!("Starting Lockup test");
             lockup_test(
                 &contact,
                 keys,
@@ -128,11 +127,9 @@ pub async fn main() {
             .await;
             return;
         } else if test_type == "MICROTX_FEES" {
-            info!("Starting microtx fees test");
             microtx_fees_test(&contact, keys).await;
             return;
         } else if test_type == "ERC20_CONVERSION" {
-            info!("Starting erc20 conversion test");
             erc20_conversion_test(
                 &contact,
                 &web30,
@@ -143,11 +140,9 @@ pub async fn main() {
             .await;
             return;
         } else if test_type == "NATIVE_TOKEN" {
-            info!("Starting native token test");
             native_token_test(&contact, &web30, keys).await;
             return;
         } else if test_type == "LIQUID_ACCOUNTS" {
-            info!("Start Liquid Infrastructure Accounts test");
             liquid_accounts_test(
                 &contact,
                 &web30,
@@ -159,12 +154,10 @@ pub async fn main() {
             return;
         } else if test_type == "ICA_HOST" {
             start_ibc_relayer(&contact, &ibc_contact, &keys, &ibc_keys).await;
-            info!("Start ICA Host test");
             ica_host_happy_path(&contact, &ibc_contact, keys, ibc_keys).await;
             return;
         } else if test_type == "ONBOARDING_DEFAULT_PARAMS" {
             start_ibc_relayer(&contact, &ibc_contact, &keys, &ibc_keys).await;
-            info!("Start onboarding default params test");
             onboarding_default_params(
                 &contact,
                 &ibc_contact,
@@ -177,7 +170,6 @@ pub async fn main() {
             return;
         } else if test_type == "ONBOARDING_DISABLED_WHITELISTED" {
             start_ibc_relayer(&contact, &ibc_contact, &keys, &ibc_keys).await;
-            info!("Start onboarding disabled yet whitelisted test");
             onboarding_disabled_whitelisted(
                 &contact,
                 &ibc_contact,
@@ -190,7 +182,6 @@ pub async fn main() {
             return;
         } else if test_type == "ONBOARDING_DISABLE_AFTER" {
             start_ibc_relayer(&contact, &ibc_contact, &keys, &ibc_keys).await;
-            info!("Start onboarding disable after test");
             onboarding_disable_after(
                 &contact,
                 &ibc_contact,
@@ -203,7 +194,6 @@ pub async fn main() {
             return;
         } else if test_type == "ONBOARDING_DELIST_AFTER" {
             start_ibc_relayer(&contact, &ibc_contact, &keys, &ibc_keys).await;
-            info!("Start onboarding delist after test");
             onboarding_delist_after(
                 &contact,
                 &ibc_contact,
@@ -215,7 +205,6 @@ pub async fn main() {
             .await;
             return;
         } else if test_type == "DEX" {
-            info!("Start dex test");
             basic_dex_test(
                 &contact,
                 &web30,
@@ -228,7 +217,6 @@ pub async fn main() {
             .await;
             return;
         } else if test_type == "DEX_ADVANCED" {
-            info!("Start advanced dex test");
             advanced_dex_test(
                 &contact,
                 &web30,
@@ -250,7 +238,6 @@ pub async fn main() {
             .await;
             return;
         } else if test_type == "DEX_UPGRADE" {
-            info!("Start dex upgrade test");
             dex_upgrade_test(
                 &contact,
                 &web30,
@@ -263,7 +250,6 @@ pub async fn main() {
             .await;
             return;
         } else if test_type == "DEX_SAFE_MODE" {
-            info!("Start dex safe mode test");
             dex_safe_mode_test(
                 &contact,
                 &web30,
@@ -276,7 +262,6 @@ pub async fn main() {
             .await;
             return;
         } else if test_type == "DEX_OPS_PROPOSAL" {
-            info!("Start dex OpsProposal test");
             dex_ops_proposal_test(
                 &contact,
                 &web30,
@@ -292,7 +277,6 @@ pub async fn main() {
             || test_type == "EVM_FEE_BURNING"
             || test_type == "EVM_FEE_BURN"
         {
-            info!("Start evm fee burning test");
             evm_fee_burning_test(
                 &contact,
                 &web30,
diff --git a/integration_tests/test_runner/src/tests/dex.rs b/integration_tests/test_runner/src/tests/dex.rs
index 20911732..2ebba377 100644
--- a/integration_tests/test_runner/src/tests/dex.rs
+++ b/integration_tests/test_runner/src/tests/dex.rs
@@ -53,6 +53,7 @@ pub async fn basic_dex_test(
     dex_contracts: DexAddresses,
     walthea: EthAddress,
 ) {
+    info!("Start dex test");
     let DexTestParams {
         evm_user,
         caller: _,
@@ -222,6 +223,7 @@ pub async fn advanced_dex_test(
     dex_contracts: DexAddresses,
     walthea: EthAddress,
 ) {
+    info!("Start advanced dex test");
     let DexTestParams {
         evm_user,
         caller: _,
@@ -302,6 +304,7 @@ pub async fn dex_swap_many(
     erc20_contracts: Vec<EthAddress>,
     dex_contracts: DexAddresses,
 ) {
+    info!("Start dex swap test");
     let DexTestParams {
         evm_user,
         caller: _,
@@ -373,6 +376,7 @@ pub async fn dex_upgrade_test(
     dex_contracts: DexAddresses,
     walthea: EthAddress,
 ) {
+    info!("Start dex upgrade test");
     let evm_user = evm_user_keys.first().unwrap();
     let emergency_user = evm_user_keys.last().unwrap();
     let (pool_base, pool_quote) = pool_tokens(erc20_contracts.clone());
@@ -530,6 +534,7 @@ pub async fn dex_safe_mode_test(
     dex_contracts: DexAddresses,
     walthea: EthAddress,
 ) {
+    info!("Start dex safe mode test");
     let emergency_user = evm_user_keys.last().unwrap();
     let evm_user = evm_user_keys.first().unwrap();
     let (pool_base, pool_quote) = pool_tokens(erc20_contracts.clone());
@@ -647,6 +652,7 @@ pub async fn dex_ops_proposal_test(
     dex_contracts: DexAddresses,
     walthea: EthAddress,
 ) {
+    info!("Start dex OpsProposal test");
     let evm_user = evm_user_keys.first().unwrap();
     let (pool_base, pool_quote) = pool_tokens(erc20_contracts.clone());
 
diff --git a/integration_tests/test_runner/src/tests/erc20_conversion.rs b/integration_tests/test_runner/src/tests/erc20_conversion.rs
index e5a6e684..2a692013 100644
--- a/integration_tests/test_runner/src/tests/erc20_conversion.rs
+++ b/integration_tests/test_runner/src/tests/erc20_conversion.rs
@@ -32,6 +32,7 @@ pub async fn erc20_conversion_test(
     evm_user_keys: Vec<EthermintUserKey>,
     erc20_contracts: Vec<EthAddress>,
 ) {
+    info!("Starting erc20 conversion test");
     erc20_register_and_round_trip_test(
         contact,
         web3,
diff --git a/integration_tests/test_runner/src/tests/evm_fee_burning.rs b/integration_tests/test_runner/src/tests/evm_fee_burning.rs
index 91eabf06..d7e60313 100644
--- a/integration_tests/test_runner/src/tests/evm_fee_burning.rs
+++ b/integration_tests/test_runner/src/tests/evm_fee_burning.rs
@@ -24,6 +24,7 @@ pub async fn evm_fee_burning_test(
     evm_user_keys: Vec<EthermintUserKey>,
     erc20_contracts: Vec<EthAddress>,
 ) {
+    info!("Start evm fee burning test");
     info!("Set inflation to 0");
     set_inflation_to_zero(contact, &validator_keys).await;
 
diff --git a/integration_tests/test_runner/src/tests/ica_host.rs b/integration_tests/test_runner/src/tests/ica_host.rs
index 73820cbd..4ecaacf3 100644
--- a/integration_tests/test_runner/src/tests/ica_host.rs
+++ b/integration_tests/test_runner/src/tests/ica_host.rs
@@ -48,6 +48,7 @@ pub async fn ica_host_happy_path(
     keys: Vec<ValidatorKeys>,
     ibc_keys: Vec<CosmosPrivateKey>,
 ) {
+    info!("Start ICA Host test");
     let althea_channel_qc = IbcChannelQueryClient::connect(COSMOS_NODE_GRPC.as_str())
         .await
         .expect("Could not connect channel query client");
diff --git a/integration_tests/test_runner/src/tests/lockup.rs b/integration_tests/test_runner/src/tests/lockup.rs
index 4f9b94ea..a7a70f97 100644
--- a/integration_tests/test_runner/src/tests/lockup.rs
+++ b/integration_tests/test_runner/src/tests/lockup.rs
@@ -43,6 +43,7 @@ pub async fn lockup_test(
     evm_user_keys: Vec<EthermintUserKey>,
     erc20_addresses: Vec<EthAddress>,
 ) {
+    info!("Starting Lockup test");
     let lock_exempt = get_user_key(None);
     let msg_send_authorized = get_user_key(None);
     let msg_multi_send_authorized = get_user_key(None);
diff --git a/integration_tests/test_runner/src/tests/microtx_fees.rs b/integration_tests/test_runner/src/tests/microtx_fees.rs
index 64bd5528..d13763ff 100644
--- a/integration_tests/test_runner/src/tests/microtx_fees.rs
+++ b/integration_tests/test_runner/src/tests/microtx_fees.rs
@@ -18,6 +18,7 @@ pub const MICROTX_FEE_BASIS_POINTS_PARAM_KEY: &str = "MicrotxFeeBasisPoints";
 /// Simulates activity of automated peer-to-peer transactions on Althea networks,
 /// asserting that the correct fees are deducted and transfers succeed
 pub async fn microtx_fees_test(contact: &Contact, validator_keys: Vec<ValidatorKeys>) {
+    info!("Starting microtx fees test");
     let num_users = 64;
     // Make users who will send tokens
     let senders = bulk_get_user_keys(None, num_users);
diff --git a/integration_tests/test_runner/src/tests/onboarding.rs b/integration_tests/test_runner/src/tests/onboarding.rs
index c7859f0b..67eabe0e 100644
--- a/integration_tests/test_runner/src/tests/onboarding.rs
+++ b/integration_tests/test_runner/src/tests/onboarding.rs
@@ -59,6 +59,7 @@ pub async fn onboarding_default_params(
     erc20_contracts: Vec<EthAddress>,
     evm_user_keys: Vec<EthermintUserKey>,
 ) {
+    info!("Start onboarding default params test");
     onboarding_test(
         althea_contact,
         ibc_contact,
@@ -84,6 +85,7 @@ pub async fn onboarding_disabled_whitelisted(
     erc20_contracts: Vec<EthAddress>,
     evm_user_keys: Vec<EthermintUserKey>,
 ) {
+    info!("Start onboarding disabled yet whitelisted test");
     onboarding_test(
         althea_contact,
         ibc_contact,
@@ -109,6 +111,7 @@ pub async fn onboarding_disable_after(
     erc20_contracts: Vec<EthAddress>,
     evm_user_keys: Vec<EthermintUserKey>,
 ) {
+    info!("Start onboarding disable after test");
     onboarding_test(
         althea_contact,
         ibc_contact,
@@ -135,6 +138,7 @@ pub async fn onboarding_delist_after(
     erc20_contracts: Vec<EthAddress>,
     evm_user_keys: Vec<EthermintUserKey>,
 ) {
+    info!("Start onboarding delist after test");
     onboarding_test(
         althea_contact,
         ibc_contact,

From effe3469bb74c6db9085cd90d120f09f133c1376 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Fri, 10 Jan 2025 22:49:25 -0500
Subject: [PATCH 54/63] Update solidity-dex

---
 solidity-dex | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/solidity-dex b/solidity-dex
index 4c64755f..746605f3 160000
--- a/solidity-dex
+++ b/solidity-dex
@@ -1 +1 @@
-Subproject commit 4c64755f78e2fbbccc4c34fde3707755a39413e9
+Subproject commit 746605f3d054340932b89c4e54b5328e165b71f3

From 332adb931b295c5e8f1e8e641744b466a5573f53 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Sat, 11 Jan 2025 12:49:52 -0500
Subject: [PATCH 55/63] WIP: test modifications do not include

---
 tests/container-scripts/integration-tests.sh     |  2 +-
 tests/container-scripts/setup-validators.sh      |  2 +-
 tests/container-scripts/upgrade-test-internal.sh | 11 +++++++++++
 tests/run-upgrade-test.sh                        |  2 +-
 4 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/tests/container-scripts/integration-tests.sh b/tests/container-scripts/integration-tests.sh
index d0340a1d..bb897b9b 100755
--- a/tests/container-scripts/integration-tests.sh
+++ b/tests/container-scripts/integration-tests.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 NODES=$1
 TEST_TYPE=$2
-set -eu
+set -u
 
 echo "Waiting for /althea/test-ready-to-run to exist before starting the test"
 while [ ! -f /althea/test-ready-to-run ];
diff --git a/tests/container-scripts/setup-validators.sh b/tests/container-scripts/setup-validators.sh
index 7674cda3..ed1a3a57 100755
--- a/tests/container-scripts/setup-validators.sh
+++ b/tests/container-scripts/setup-validators.sh
@@ -70,7 +70,7 @@ jq ".app_state.evm.params.evm_denom=\"${STAKING_TOKEN}\"" /genesis.json > tmp_ge
 jq '.app_state.feemarket.params.min_gas_price = "0.000000000000000000"' /genesis.json > tmp_genesis.json && mv tmp_genesis.json /genesis.json
 
 # a 120 second voting period to allow us to pass governance proposals in the tests
-jq '.app_state.gov.voting_params.voting_period = "120s"' /genesis.json > tmp_genesis.json && mv tmp_genesis.json /genesis.json
+jq '.app_state.gov.voting_params.voting_period = "20s"' /genesis.json > tmp_genesis.json && mv tmp_genesis.json /genesis.json
 
 # rename base denom to aalthea
 sed -i 's/stake/aalthea/g' /genesis.json
diff --git a/tests/container-scripts/upgrade-test-internal.sh b/tests/container-scripts/upgrade-test-internal.sh
index 103fab27..0138dc7b 100755
--- a/tests/container-scripts/upgrade-test-internal.sh
+++ b/tests/container-scripts/upgrade-test-internal.sh
@@ -50,8 +50,19 @@ tests/container-scripts/integration-tests.sh $NODES UPGRADE_PART_1
 unset OLD_BINARY_LOCATION
 # Run the new binary
 pkill oldalthea || true # allowed to fail
+pkill althea || true # allowed to fail
+pkill gaiad || true # allowed to fail
 tests/container-scripts/run-testnet.sh $NODES
 
+sleep 20
+
 # Run the post-upgrade test
+set +e
 tests/container-scripts/integration-tests.sh $NODES UPGRADE_PART_2
 popd
+set -e
+
+echo "Upgrade part 2 ran but may have failed, check the logs for more information"
+
+sleep 6000000
+echo "You waited too long, closing the container"
\ No newline at end of file
diff --git a/tests/run-upgrade-test.sh b/tests/run-upgrade-test.sh
index f4e3a43f..894cfdda 100755
--- a/tests/run-upgrade-test.sh
+++ b/tests/run-upgrade-test.sh
@@ -34,4 +34,4 @@ fi
 
 # Run new test container instance
 PORTS="-p 9090:9090 -p 26657:26657 -p 1317:1317 -p 8545:8545"
-docker run --name althea_all_up_test_instance $PLATFORM_CMD --cap-add=NET_ADMIN $PORTS althea-base /bin/bash /althea/tests/container-scripts/upgrade-test-internal.sh $NODES $OLD_VERSION
+docker run --name althea_all_up_test_instance $PLATFORM_CMD --cap-add=NET_ADMIN $PORTS althea-base /bin/bash /althea/tests/container-scripts/upgrade-test-internal.sh $NODES $OLD_VERSION
\ No newline at end of file

From cec8a3210462b68d98bebb3a474eac6f6f1f3dbf Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Sat, 11 Jan 2025 15:14:42 -0500
Subject: [PATCH 56/63] Compile solidity folder for go CI tests

---
 .github/workflows/go.yml | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 61d4eef1..d5045ebd 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -14,6 +14,14 @@ jobs:
       matrix:
         node-version: [ 18.x ]
     steps:
+      - uses: actions/checkout@v2
+      - name: Build and test Solidity contracts
+        uses: actions/setup-node@v1
+        with:
+          node-version: ${{ matrix.node-version }}
+      - run: cd solidity && npm ci
+      - run: cd solidity && npm run typechain
+      - run: cd solidity && npm run test
       - name: Set up Go 1.x
         uses: actions/setup-go@v2
         with:

From e65754649c8448368b8b3982de31db5e12079125 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Sat, 11 Jan 2025 15:21:54 -0500
Subject: [PATCH 57/63] Remove contract formatting for Go step in Makefile

---
 Makefile | 2 --
 1 file changed, 2 deletions(-)

diff --git a/Makefile b/Makefile
index 37282d93..6bda35ae 100644
--- a/Makefile
+++ b/Makefile
@@ -58,8 +58,6 @@ install: go.sum install-core
 
 # does not run go mod verify
 install-core:
-		@echo "Formatting solidity contracts for Cosmos use"
-		sh scripts/compile-contracts-for-go.sh
 		export GOFLAGS='-buildmode=pie'
 		export CGO_CPPFLAGS="-D_FORTIFY_SOURCE=2"
 		export CGO_LDFLAGS="-Wl,-z,relro,-z,now -fstack-protector"

From 459bd80736d176c678f8ff5b2d1c50c0137611b2 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Sun, 12 Jan 2025 14:35:43 -0500
Subject: [PATCH 58/63] Cargo clippy + audit fixes

---
 integration_tests/Cargo.lock                  |  6 ++---
 .../test_runner/src/bootstrapping.rs          | 24 +++++++++----------
 2 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/integration_tests/Cargo.lock b/integration_tests/Cargo.lock
index 72a4e750..9d4b5b1f 100644
--- a/integration_tests/Cargo.lock
+++ b/integration_tests/Cargo.lock
@@ -1,6 +1,6 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-version = 3
+version = 4
 
 [[package]]
 name = "actix"
@@ -2385,9 +2385,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
 
 [[package]]
 name = "url"
-version = "2.5.0"
+version = "2.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
 dependencies = [
  "form_urlencoded",
  "idna",
diff --git a/integration_tests/test_runner/src/bootstrapping.rs b/integration_tests/test_runner/src/bootstrapping.rs
index 2f75350f..1ce04327 100644
--- a/integration_tests/test_runner/src/bootstrapping.rs
+++ b/integration_tests/test_runner/src/bootstrapping.rs
@@ -59,14 +59,6 @@ pub fn parse_validator_keys() -> (Vec<CosmosPrivateKey>, Vec<String>) {
     parse_phrases(filename)
 }
 
-/// The same as parse_validator_keys() except for a second chain accessed
-/// over IBC for testing purposes
-// pub fn parse_ibc_validator_keys() -> (Vec<CosmosPrivateKey>, Vec<String>) {
-//     let filename = "/ibc-validator-phrases";
-//     info!("Reading mnemonics from {}", filename);
-//     parse_phrases(filename)
-// }
-
 pub fn get_keys() -> Vec<ValidatorKeys> {
     let (cosmos_keys, cosmos_phrases) = parse_validator_keys();
     let mut ret = Vec::new();
@@ -416,7 +408,8 @@ pub fn setup_relayer_keys() -> Result<(), Box<dyn std::error::Error>> {
             mnemonic_path,
         ])
         .spawn()
-        .expect("Failed to add althea key");
+        .expect("Failed to add althea key")
+        .wait();
     info!("Added altheakey to hermes keybase");
 
     let mut command = hermes_base();
@@ -432,7 +425,8 @@ pub fn setup_relayer_keys() -> Result<(), Box<dyn std::error::Error>> {
             mnemonic_path,
         ])
         .spawn()
-        .expect("Failed to add ibc key");
+        .expect("Failed to add ibc key")
+        .wait();
     info!("Added ibckey to hermes keybase");
 
     Ok(())
@@ -473,7 +467,11 @@ pub fn create_ibc_channel(hermes_base: Command) {
         let create_channel = create_channel
             .stdout(Stdio::from_raw_fd(out_file))
             .stderr(Stdio::from_raw_fd(out_file));
-        create_channel.spawn().expect("Could not create channel");
+        create_channel
+            .spawn()
+            .expect("Could not create channel")
+            .wait()
+            .expect("Child exited with error");
     }
 }
 
@@ -500,7 +498,9 @@ pub fn run_ibc_relayer(hermes_base: Command, full_scan: bool) {
             .stdout(Stdio::from_raw_fd(out_file))
             .stderr(Stdio::from_raw_fd(out_file))
             .spawn()
-            .expect("Could not run hermes");
+            .expect("Could not run hermes")
+            .wait()
+            .expect("Child exited with error");
     }
 }
 

From 651dcdf2028ae9f69920471eadbec4f8d4a2cea3 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Sun, 12 Jan 2025 15:23:59 -0500
Subject: [PATCH 59/63] Rework go CI tests

The binary and go testing depends on the solidity folder contents
because we embed contracts for use by the x/erc20 module. To fix the go
CI tasks they must all install node.js and compile the solidity folder
before formatting the output for use by the go files in contracts. After
this the build, lint, and test tasks can all run.

Because they are no longer dependent on the build step, test and lint
are now completely parallel with build.
---
 .github/workflows/go.yml | 45 ++++++++++++++++++++++++++--------------
 1 file changed, 30 insertions(+), 15 deletions(-)

diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index d5045ebd..b68eeb9b 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -14,33 +14,40 @@ jobs:
       matrix:
         node-version: [ 18.x ]
     steps:
-      - uses: actions/checkout@v2
-      - name: Build and test Solidity contracts
-        uses: actions/setup-node@v1
-        with:
-          node-version: ${{ matrix.node-version }}
-      - run: cd solidity && npm ci
-      - run: cd solidity && npm run typechain
-      - run: cd solidity && npm run test
+      - name: Check out code
+        uses: actions/checkout@v2
       - name: Set up Go 1.x
         uses: actions/setup-go@v2
         with:
           go-version: ^1.16
-      - name: Check out code
-        uses: actions/checkout@v2
+      - name: Setup Node.js
+        uses: actions/setup-node@v1
+        with:
+          node-version: ${{ matrix.node-version }}
+      - name: Build and test solidity contracts
+        run: cd solidity && npm ci && npm run typechain && npm run test
+      - name: Format compiled solidity contracts for go
+        run: scripts/compile-contracts-for-go.sh
       - name: Build the Althea-L1 binary
         run: make
   lint:
     name: Lint
     runs-on: ubuntu-latest
-    needs: build
     steps:
+      - name: Check out code
+        uses: actions/checkout@v2
       - name: Set up Go 1.x
         uses: actions/setup-go@v2
         with:
           go-version: ^1.16
-      - name: Check out code
-        uses: actions/checkout@v2
+      - name: Setup Node.js
+        uses: actions/setup-node@v1
+        with:
+          node-version: ${{ matrix.node-version }}
+      - name: Build and test solidity contracts
+        run: cd solidity && npm ci && npm run typechain && npm run test
+      - name: Format compiled solidity contracts for go
+        run: scripts/compile-contracts-for-go.sh
       - name: Install golangci-lint 
         run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
       - name: Run golangci-lint
@@ -49,12 +56,20 @@ jobs:
   test:
     name: Test
     runs-on: ubuntu-latest
-    needs: build
     steps:
+      - name: Check out code
+        uses: actions/checkout@v2
       - name: Set up Go 1.x
         uses: actions/setup-go@v2
         with:
           go-version: ^1.16
-      - uses: actions/checkout@v2
+      - name: Setup Node.js
+        uses: actions/setup-node@v1
+        with:
+          node-version: ${{ matrix.node-version }}
+      - name: Build and test solidity contracts
+        run: cd solidity && npm ci && npm run typechain && npm run test
+      - name: Format compiled solidity contracts for go
+        run: scripts/compile-contracts-for-go.sh
       - name: Test
         run: go test ./... -mod=readonly -timeout 30m -coverprofile=profile.out -covermode=atomic -race

From 1dd932ec34b34004c81eea90c2e8f7e545ebb188 Mon Sep 17 00:00:00 2001
From: Christian Borst <christianoborst@gmail.com>
Date: Sun, 12 Jan 2025 16:47:43 -0500
Subject: [PATCH 60/63] Lint fixes

---
 app/ante/account_type.go                  | 10 ++-
 app/ante/ante.go                          |  6 +-
 app/ante/charge_gasfree_fees.go           |  7 +-
 app/ante/charge_gasfree_fees_test.go      |  7 +-
 app/ante/comission.go                     |  8 +-
 app/ante/cosmos_handler_test.go           | 33 +++++----
 app/ante/eth_account_verification.go      | 14 ++--
 app/ante/eth_set_pubkey.go                | 14 ++--
 app/ante/handler_options.go               |  2 +
 app/ante/utils_test.go                    | 12 ++-
 app/app.go                                |  5 +-
 app/export.go                             | 15 +++-
 app/pubkey_replacement.go                 | 11 +--
 app/sigverify.go                          |  1 +
 app/testutil.go                           | 14 ++--
 cmd/althea/genaccounts.go                 |  6 +-
 ibcutils/testing/app.go                   |  6 +-
 ibcutils/testing/chain.go                 |  5 +-
 ibcutils/testing/events.go                |  4 +-
 ibcutils/utils.go                         | 18 +++--
 testutil/network/network.go               | 23 ++++--
 testutil/network/util.go                  |  1 +
 x/erc20/client/cli/query.go               |  4 +
 x/erc20/client/cli/tx.go                  | 28 ++++++-
 x/erc20/client/cli/utils.go               |  5 +-
 x/erc20/genesis_test.go                   |  4 +
 x/erc20/handler.go                        |  4 +-
 x/erc20/keeper/evm.go                     | 26 ++++---
 x/erc20/keeper/evm_hooks.go               |  1 +
 x/erc20/keeper/evm_hooks_test.go          | 30 +++++++-
 x/erc20/keeper/evm_test.go                | 32 ++++++--
 x/erc20/keeper/grpc_query.go              |  1 +
 x/erc20/keeper/grpc_query_test.go         | 12 +++
 x/erc20/keeper/integration_test.go        | 17 +++--
 x/erc20/keeper/keeper_test.go             | 19 +++++
 x/erc20/keeper/migrations.go              |  1 +
 x/erc20/keeper/mint.go                    | 15 ++--
 x/erc20/keeper/msg_server.go              | 42 ++++++-----
 x/erc20/keeper/msg_server_test.go         | 89 +++++++++++++++++++----
 x/erc20/keeper/proposals.go               | 32 ++++----
 x/erc20/keeper/proposals_test.go          | 20 +++--
 x/erc20/keeper/token_pairs.go             |  3 +
 x/erc20/module.go                         |  7 +-
 x/erc20/proposal_handler.go               |  4 +-
 x/erc20/types/codec.go                    |  7 ++
 x/erc20/types/genesis.go                  |  2 +
 x/erc20/types/genesis_test.go             | 17 ++++-
 x/erc20/types/msg.go                      | 22 ++++--
 x/erc20/types/msg_test.go                 |  8 +-
 x/erc20/types/params.go                   |  2 +
 x/erc20/types/params_test.go              |  1 +
 x/erc20/types/proposal.go                 |  8 +-
 x/erc20/types/proposal_test.go            |  8 ++
 x/erc20/types/utils_test.go               |  2 +
 x/gasfree/ante.go                         |  7 +-
 x/gasfree/keeper/keeper.go                |  6 +-
 x/gasfree/module_test.go                  |  2 +
 x/gasfree/types/errors.go                 |  2 +-
 x/gasfree/types/genesis.go                |  5 +-
 x/lockup/ante.go                          | 24 +++---
 x/lockup/keeper/keeper.go                 |  4 +-
 x/lockup/types/errors.go                  |  2 +-
 x/lockup/types/genesis.go                 |  9 ++-
 x/microtx/client/cli/query.go             |  5 +-
 x/microtx/client/cli/tx.go                | 15 ++--
 x/microtx/handler.go                      |  4 +-
 x/microtx/keeper/grpc_query.go            |  4 +-
 x/microtx/keeper/keeper.go                |  6 +-
 x/microtx/keeper/liquid_account.go        | 46 ++++++------
 x/microtx/keeper/msg_server.go            | 29 ++++----
 x/microtx/module.go                       |  4 +-
 x/microtx/types/errors.go                 | 16 ++--
 x/microtx/types/genesis.go                |  7 +-
 x/microtx/types/liquid_account.go         |  5 +-
 x/microtx/types/msgs.go                   | 13 ++--
 x/nativedex/client/cli/tx.go              | 46 ++++++------
 x/nativedex/client/cli/utils.go           |  9 ++-
 x/nativedex/proposal_handler.go           |  4 +-
 x/nativedex/types/errors.go               |  2 +-
 x/nativedex/types/genesis.go              | 13 ++--
 x/nativedex/types/proposal.go             | 16 ++--
 x/onboarding/keeper/ibc_callbacks_test.go | 14 ++--
 x/onboarding/keeper/keeper_test.go        |  4 +-
 x/onboarding/module.go                    |  4 +-
 x/onboarding/types/errors.go              |  2 +-
 85 files changed, 685 insertions(+), 339 deletions(-)

diff --git a/app/ante/account_type.go b/app/ante/account_type.go
index 29b408ae..c282977e 100644
--- a/app/ante/account_type.go
+++ b/app/ante/account_type.go
@@ -1,6 +1,8 @@
 package ante
 
 import (
+	errorsmod "cosmossdk.io/errors"
+
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
@@ -34,7 +36,7 @@ func NewSetAccountTypeDecorator(ak AccountKeeper, ethAccountProto func(sdk.AccAd
 func (satd SetAccountTypeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
 	sigTx, ok := tx.(SignedTx)
 	if !ok {
-		return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid tx type")
+		return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "invalid tx type")
 	}
 
 	signers := sigTx.GetSigners()
@@ -56,13 +58,13 @@ func (satd SetAccountTypeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul
 
 			// Copy over the BaseAccount values
 			if err = replacement.SetAccountNumber(baseAccount.GetAccountNumber()); err != nil {
-				return ctx, sdkerrors.Wrap(err, "unable to set account number on replacement EthAccount")
+				return ctx, errorsmod.Wrap(err, "unable to set account number on replacement EthAccount")
 			}
 			if err = replacement.SetSequence(baseAccount.GetSequence()); err != nil {
-				return ctx, sdkerrors.Wrap(err, "unable to set sequence on replacement EthAccount")
+				return ctx, errorsmod.Wrap(err, "unable to set sequence on replacement EthAccount")
 			}
 			if err = replacement.SetPubKey(storedPubKey); err != nil {
-				return ctx, sdkerrors.Wrap(err, "unable to set pubkey on replacement EthAccount")
+				return ctx, errorsmod.Wrap(err, "unable to set pubkey on replacement EthAccount")
 			}
 
 			satd.ak.SetAccount(ctx, replacement)
diff --git a/app/ante/ante.go b/app/ante/ante.go
index afe7e5ed..33df00c4 100644
--- a/app/ante/ante.go
+++ b/app/ante/ante.go
@@ -1,6 +1,8 @@
 package ante
 
 import (
+	errorsmod "cosmossdk.io/errors"
+
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
@@ -32,7 +34,7 @@ func NewAnteHandler(options HandlerOptions) sdk.AnteHandler {
 					// handle as normal Cosmos SDK tx, except signature is checked for EIP712 representation
 					anteHandler = newCosmosAnteHandlerEip712(options)
 				default:
-					return ctx, sdkerrors.Wrapf(
+					return ctx, errorsmod.Wrapf(
 						sdkerrors.ErrUnknownExtensionOptions,
 						"rejecting tx with unsupported extension option: %s", typeURL,
 					)
@@ -47,7 +49,7 @@ func NewAnteHandler(options HandlerOptions) sdk.AnteHandler {
 		case sdk.Tx:
 			anteHandler = newCosmosAnteHandler(options)
 		default:
-			return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx)
+			return ctx, errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx)
 		}
 
 		return anteHandler(ctx, tx, sim)
diff --git a/app/ante/charge_gasfree_fees.go b/app/ante/charge_gasfree_fees.go
index adc3c3f2..74d51a63 100644
--- a/app/ante/charge_gasfree_fees.go
+++ b/app/ante/charge_gasfree_fees.go
@@ -1,8 +1,9 @@
 package ante
 
 import (
+	errorsmod "cosmossdk.io/errors"
+
 	sdk "github.com/cosmos/cosmos-sdk/types"
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 
 	gasfreekeeper "github.com/AltheaFoundation/althea-L1/x/gasfree/keeper"
 	microtxkeeper "github.com/AltheaFoundation/althea-L1/x/microtx/keeper"
@@ -32,7 +33,7 @@ func (satd ChargeGasfreeFeesDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, si
 	// Handle any microtxs individually
 	err := satd.DeductAnyMicrotxFees(ctx, tx)
 	if err != nil {
-		return ctx, sdkerrors.Wrap(err, "failed to deduct microtx fees")
+		return ctx, errorsmod.Wrap(err, "failed to deduct microtx fees")
 	}
 
 	return next(ctx, tx, simulate)
@@ -49,7 +50,7 @@ func (satd ChargeGasfreeFeesDecorator) DeductAnyMicrotxFees(ctx sdk.Context, tx
 		if isMicrotx {
 			feeCollected, err := satd.microtxKeeper.DeductMsgMicrotxFee(ctx, msgMicrotx)
 			if err != nil {
-				return sdkerrors.Wrap(err, "unable to collect microtx fee prior to msg execution")
+				return errorsmod.Wrap(err, "unable to collect microtx fee prior to msg execution")
 			}
 			ctx.EventManager().EmitEvent(microtxtypes.NewEventMicrotxFeeCollected(msgMicrotx.Sender, *feeCollected))
 		}
diff --git a/app/ante/charge_gasfree_fees_test.go b/app/ante/charge_gasfree_fees_test.go
index 6f502ab4..257a44ba 100644
--- a/app/ante/charge_gasfree_fees_test.go
+++ b/app/ante/charge_gasfree_fees_test.go
@@ -5,6 +5,8 @@ import (
 	"math/big"
 	"strings"
 
+	sdkmath "cosmossdk.io/math"
+
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
@@ -91,7 +93,7 @@ func runGasfreeTest(suite *AnteTestSuite, expPass bool, ctx sdk.Context, tx sdk.
 	shouldChangeBalanceAfter := suite.app.BankKeeper.GetBalance(cached, addr, shouldChangeDenom)
 	shouldNotChangeBalanceAfter := suite.app.BankKeeper.GetBalance(cached, addr, shouldNotChangeDenom)
 
-	var shouldChangeDiff, shouldNotChangeDiff sdk.Int
+	var shouldChangeDiff, shouldNotChangeDiff sdkmath.Int
 	if shouldChangeBalance.Amount.GT(shouldChangeBalanceAfter.Amount) {
 		shouldChangeDiff = shouldChangeBalance.Amount.Sub(shouldChangeBalanceAfter.Amount)
 	} else {
@@ -128,7 +130,7 @@ func runNoChangeGasfreeTest(suite *AnteTestSuite, expPass bool, ctx sdk.Context,
 
 	shouldNotChangeBalanceAfter := suite.app.BankKeeper.GetBalance(cached, addr, shouldNotChangeDenom)
 
-	var shouldNotChangeDiff sdk.Int
+	var shouldNotChangeDiff sdkmath.Int
 
 	if shouldNotChangeBalance.Amount.GT(shouldNotChangeBalanceAfter.Amount) {
 		shouldNotChangeDiff = shouldNotChangeBalance.Amount.Sub(shouldNotChangeBalanceAfter.Amount)
@@ -149,6 +151,7 @@ func (suite *AnteTestSuite) TestChargeGasfreeFeesDecorator() {
 
 	testDenom := "test"
 	suite.Require().NoError(suite.app.BankKeeper.MintCoins(suite.ctx, evmtypes.ModuleName, sdk.NewCoins(sdk.NewCoin(testDenom, sdk.NewInt(10000000000)))))
+	//nolint: exhaustruct
 	metadata := banktypes.Metadata{
 		Base:        testDenom,
 		Display:     testDenom,
diff --git a/app/ante/comission.go b/app/ante/comission.go
index 06a366ae..782c918c 100644
--- a/app/ante/comission.go
+++ b/app/ante/comission.go
@@ -1,6 +1,8 @@
 package ante
 
 import (
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/cosmos/cosmos-sdk/codec"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@@ -53,7 +55,7 @@ func (vcd ValidatorCommissionDecorator) validateAuthz(ctx sdk.Context, execMsg *
 		var innerMsg sdk.Msg
 		err := vcd.cdc.UnpackAny(v, &innerMsg)
 		if err != nil {
-			return sdkerrors.Wrap(err, "cannot unmarshal authz exec msgs")
+			return errorsmod.Wrap(err, "cannot unmarshal authz exec msgs")
 		}
 
 		if err := vcd.validateMsg(ctx, innerMsg); err != nil {
@@ -69,13 +71,13 @@ func (vcd ValidatorCommissionDecorator) validateMsg(_ sdk.Context, msg sdk.Msg)
 	switch msg := msg.(type) {
 	case *stakingtypes.MsgCreateValidator:
 		if msg.Commission.Rate.LT(minCommission) {
-			return sdkerrors.Wrapf(
+			return errorsmod.Wrapf(
 				sdkerrors.ErrInvalidRequest,
 				"validator commission %s be lower than minimum of %s", msg.Commission.Rate, minCommission)
 		}
 	case *stakingtypes.MsgEditValidator:
 		if msg.CommissionRate != nil && msg.CommissionRate.LT(minCommission) {
-			return sdkerrors.Wrapf(
+			return errorsmod.Wrapf(
 				sdkerrors.ErrInvalidRequest,
 				"validator commission %s be lower than minimum of %s", msg.CommissionRate, minCommission)
 		}
diff --git a/app/ante/cosmos_handler_test.go b/app/ante/cosmos_handler_test.go
index 5edaab43..84bdd810 100644
--- a/app/ante/cosmos_handler_test.go
+++ b/app/ante/cosmos_handler_test.go
@@ -4,8 +4,9 @@ import (
 	"math/big"
 	"strings"
 
+	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"
 
 	altheaconfig "github.com/AltheaFoundation/althea-L1/config"
@@ -24,65 +25,65 @@ func runBypassTest(suite *AnteTestSuite, expErrorStr string, gasfreeMicrotxCtx,
 	// Expect bypass for the Microtx tx
 	cached, _ := gasfreeMicrotxCtx.CacheContext()
 	if _, err := suite.anteHandler(cached, msgMicrotxTx, false); err != nil {
-		return sdkerrors.Wrap(err, "microtx gasfree expected no error")
+		return errorsmod.Wrap(err, "microtx gasfree expected no error")
 	}
 	// Expect failure for the Send tx
 	cached, _ = gasfreeMicrotxCtx.CacheContext()
 	if _, err := suite.anteHandler(cached, msgSendTx, false); !strings.Contains(err.Error(), expErrorStr) {
-		return sdkerrors.Wrap(err, "microtx gasfree sent send - expected error")
+		return errorsmod.Wrap(err, "microtx gasfree sent send - expected error")
 	}
 	// Expect failure for both msg tx
 	cached, _ = gasfreeMicrotxCtx.CacheContext()
 	if _, err := suite.anteHandler(cached, bothTx, false); !strings.Contains(err.Error(), expErrorStr) {
-		return sdkerrors.Wrap(err, "microtx gasfree sent send and microtx - expected error")
+		return errorsmod.Wrap(err, "microtx gasfree sent send and microtx - expected error")
 	}
 
 	// --- Send is the only Gasfree Msg type ---
 	cached, _ = gasfreeSendCtx.CacheContext()
 	if _, err := suite.anteHandler(cached, msgMicrotxTx, false); !strings.Contains(err.Error(), expErrorStr) {
-		return sdkerrors.Wrap(err, "send gasfree sent microtx - expected error")
+		return errorsmod.Wrap(err, "send gasfree sent microtx - expected error")
 	}
 	// Expect failure for the Send tx
 	cached, _ = gasfreeSendCtx.CacheContext()
 	if _, err := suite.anteHandler(cached, msgSendTx, false); err != nil {
-		return sdkerrors.Wrap(err, "send gasfree expected no error")
+		return errorsmod.Wrap(err, "send gasfree expected no error")
 	}
 	// Expect failure for both msg tx
 	cached, _ = gasfreeSendCtx.CacheContext()
 	if _, err := suite.anteHandler(cached, bothTx, false); !strings.Contains(err.Error(), expErrorStr) {
-		return sdkerrors.Wrap(err, "send gasfree sent send and microtx - expected error")
+		return errorsmod.Wrap(err, "send gasfree sent send and microtx - expected error")
 	}
 
 	// --- No Gasfree Msg types ---
 	// Expect failure for the Microtx tx
 	cached, _ = noGasfreeCtx.CacheContext()
 	if _, err := suite.anteHandler(cached, msgMicrotxTx, false); !strings.Contains(err.Error(), expErrorStr) {
-		return sdkerrors.Wrap(err, "no gasfree msgs sent microtx - expected error")
+		return errorsmod.Wrap(err, "no gasfree msgs sent microtx - expected error")
 	}
 	// Expect failure for the Send tx
 	cached, _ = noGasfreeCtx.CacheContext()
 	if _, err := suite.anteHandler(cached, msgSendTx, false); !strings.Contains(err.Error(), expErrorStr) {
-		return sdkerrors.Wrap(err, "no gasfree msgs sent send - expected error")
+		return errorsmod.Wrap(err, "no gasfree msgs sent send - expected error")
 	}
 	// Expect failure for both msg tx
 	cached, _ = noGasfreeCtx.CacheContext()
 	if _, err := suite.anteHandler(cached, bothTx, false); !strings.Contains(err.Error(), expErrorStr) {
-		return sdkerrors.Wrap(err, "no gasfree msgs sent send and microtx - expected error")
+		return errorsmod.Wrap(err, "no gasfree msgs sent send and microtx - expected error")
 	}
 
 	// --- Send and Microtx are gasfree Msg types ---
 	// Expect success on all txs
 	cached, _ = bothGasfreeCtx.CacheContext()
 	if _, err := suite.anteHandler(cached, msgMicrotxTx, false); err != nil {
-		return sdkerrors.Wrap(err, "send + microtx gasfree expected no error")
+		return errorsmod.Wrap(err, "send + microtx gasfree expected no error")
 	}
 	cached, _ = bothGasfreeCtx.CacheContext()
 	if _, err := suite.anteHandler(cached, msgSendTx, false); err != nil {
-		return sdkerrors.Wrap(err, "send + microtx gasfree expected no error")
+		return errorsmod.Wrap(err, "send + microtx gasfree expected no error")
 	}
 	cached, _ = bothGasfreeCtx.CacheContext()
 	if _, err := suite.anteHandler(cached, bothTx, false); err != nil {
-		return sdkerrors.Wrap(err, "send + microtx gasfree expected no error")
+		return errorsmod.Wrap(err, "send + microtx gasfree expected no error")
 	}
 	return nil
 
@@ -126,7 +127,8 @@ func (suite *AnteTestSuite) TestCosmosAnteHandlerMinGasPricesBypass() {
 	suite.ctx = suite.ctx.WithIsCheckTx(false) // use checkTx false to avoid triggering mempool fee decorator
 	feemarketParams := suite.app.FeemarketKeeper.GetParams(suite.ctx)
 	feemarketParams.MinGasPrice = sdk.NewDec(100)
-	suite.app.FeemarketKeeper.SetParams(suite.ctx, feemarketParams) // Set the min gas price to trigger failure in the MinGasPricesDecorator
+	err := suite.app.FeemarketKeeper.SetParams(suite.ctx, feemarketParams) // Set the min gas price to trigger failure in the MinGasPricesDecorator
+	suite.Require().NoError(err)
 	privKey := suite.NewCosmosPrivkey()
 	addr := sdk.AccAddress(privKey.PubKey().Address().Bytes())
 	suite.FundAccount(suite.ctx, addr, big.NewInt(10000000000))
@@ -158,7 +160,8 @@ func (suite *AnteTestSuite) TestCosmosAnteHandlerDeductFeeBypass() {
 	suite.ctx = suite.ctx.WithIsCheckTx(false) // use checkTx false to avoid triggering mempool fee decorator
 	feemarketParams := suite.app.FeemarketKeeper.GetParams(suite.ctx)
 	feemarketParams.MinGasPrice = sdk.NewDec(0)
-	suite.app.FeemarketKeeper.SetParams(suite.ctx, feemarketParams) // Set the min gas price to avoid failure from MinGasPricesDecorator
+	err := suite.app.FeemarketKeeper.SetParams(suite.ctx, feemarketParams) // Set the min gas price to avoid failure from MinGasPricesDecorator
+	suite.Require().NoError(err)
 	privKey := suite.NewCosmosPrivkey()
 	addr := sdk.AccAddress(privKey.PubKey().Address().Bytes())
 	suite.FundAccount(suite.ctx, addr, big.NewInt(1))
diff --git a/app/ante/eth_account_verification.go b/app/ante/eth_account_verification.go
index 668b3c25..38cf6d53 100644
--- a/app/ante/eth_account_verification.go
+++ b/app/ante/eth_account_verification.go
@@ -3,6 +3,8 @@ package ante
 import (
 	"github.com/ethereum/go-ethereum/common"
 
+	errorsmod "cosmossdk.io/errors"
+
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 
@@ -47,18 +49,18 @@ func (avd EthAccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx
 	for i, msg := range tx.GetMsgs() {
 		msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
 		if !ok {
-			return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
+			return ctx, errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
 		}
 
 		txData, err := evmtypes.UnpackTxData(msgEthTx.Data)
 		if err != nil {
-			return ctx, sdkerrors.Wrapf(err, "failed to unpack tx data any for tx %d", i)
+			return ctx, errorsmod.Wrapf(err, "failed to unpack tx data any for tx %d", i)
 		}
 
 		// sender address should be in the tx cache from the previous AnteHandle call
 		from := msgEthTx.GetFrom()
 		if from.Empty() {
-			return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "from address cannot be empty")
+			return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "from address cannot be empty")
 		}
 
 		// check whether the sender address is EOA
@@ -69,17 +71,17 @@ func (avd EthAccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx
 			acc := ethtypes.ProtoAccount()
 			err := acc.SetAddress(from)
 			if err != nil {
-				return ctx, sdkerrors.Wrap(err, "failed to set address")
+				return ctx, errorsmod.Wrap(err, "failed to set address")
 			}
 			avd.ak.NewAccount(ctx, acc)
 			acct = statedb.NewEmptyAccount()
 		} else if acct.IsContract() {
-			return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidType,
+			return ctx, errorsmod.Wrapf(sdkerrors.ErrInvalidType,
 				"the sender is not EOA: address %s, codeHash <%s>", fromAddr, acct.CodeHash)
 		}
 
 		if err := evmkeeper.CheckSenderBalance(sdk.NewIntFromBigInt(acct.Balance), txData); err != nil {
-			return ctx, sdkerrors.Wrap(err, "failed to check sender balance")
+			return ctx, errorsmod.Wrap(err, "failed to check sender balance")
 		}
 
 	}
diff --git a/app/ante/eth_set_pubkey.go b/app/ante/eth_set_pubkey.go
index ac46023e..e323d0b9 100644
--- a/app/ante/eth_set_pubkey.go
+++ b/app/ante/eth_set_pubkey.go
@@ -5,6 +5,8 @@ import (
 	"errors"
 	"math/big"
 
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/ethereum/go-ethereum/common"
 	ethtypes "github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/crypto"
@@ -51,24 +53,24 @@ func (espd EthSetPubkeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulat
 	for _, msg := range tx.GetMsgs() {
 		msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
 		if !ok {
-			return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
+			return ctx, errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
 		}
 
 		// sender address should be in the tx cache from the previous AnteHandle call
 		from := msgEthTx.GetFrom()
 		if from == nil || from.Empty() {
-			return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "from address cannot be empty")
+			return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "from address cannot be empty")
 		}
 
 		fromAddr := common.BytesToAddress(from)
 		acct := espd.evmKeeper.GetAccount(ctx, fromAddr)
 		if acct.IsContract() {
-			return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "tx submitted by a contract account: %s", fromAddr.Hex())
+			return ctx, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "tx submitted by a contract account: %s", fromAddr.Hex())
 		}
 
 		fromCosmosAcc := espd.ak.GetAccount(ctx, from)
 		if fromCosmosAcc == nil {
-			return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", from)
+			return ctx, errorsmod.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", from)
 		}
 
 		if fromCosmosAcc.GetPubKey() != nil {
@@ -81,7 +83,7 @@ func (espd EthSetPubkeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulat
 
 		pubkey, err := recoverPubKey(ethCfg, blockNum, ethTx)
 		if err != nil {
-			return ctx, sdkerrors.Wrapf(
+			return ctx, errorsmod.Wrapf(
 				sdkerrors.ErrorInvalidSigner,
 				"couldn't retrieve sender pubkey from the ethereum transaction: %s",
 				err.Error(),
@@ -90,7 +92,7 @@ func (espd EthSetPubkeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulat
 
 		// Set the pubkey on the account
 		if err := fromCosmosAcc.SetPubKey(pubkey); err != nil {
-			return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidPubKey, "failed to set pubkey on account: %s", err.Error())
+			return ctx, errorsmod.Wrapf(sdkerrors.ErrInvalidPubKey, "failed to set pubkey on account: %s", err.Error())
 		}
 		espd.ak.SetAccount(ctx, fromCosmosAcc)
 	}
diff --git a/app/ante/handler_options.go b/app/ante/handler_options.go
index 226c15cb..cce3bc81 100644
--- a/app/ante/handler_options.go
+++ b/app/ante/handler_options.go
@@ -149,6 +149,8 @@ func newCosmosAnteHandlerEip712(options HandlerOptions) sdk.AnteHandler {
 		ante.NewValidateSigCountDecorator(options.AccountKeeper),
 		ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer),
 		// Note: signature verification uses EIP instead of the cosmos signature validator
+		// Ethermint has deprecated this initial EIP-712 verifier, but we still want to use it for the time being, so we disable the linter for now
+		// nolint: staticcheck
 		ethante.NewLegacyEip712SigVerificationDecorator(options.AccountKeeper, options.SignModeHandler, ""), // Pass no chain id to have it parsed from the Cosmos chain id
 		ante.NewIncrementSequenceDecorator(options.AccountKeeper),
 		ibcante.NewRedundantRelayDecorator(options.IBCKeeper),
diff --git a/app/ante/utils_test.go b/app/ante/utils_test.go
index be0eab38..317e9b37 100644
--- a/app/ante/utils_test.go
+++ b/app/ante/utils_test.go
@@ -13,6 +13,8 @@ import (
 	apitypes "github.com/ethereum/go-ethereum/signer/core/apitypes"
 	"github.com/stretchr/testify/suite"
 
+	sdkmath "cosmossdk.io/math"
+
 	client "github.com/cosmos/cosmos-sdk/client"
 	"github.com/cosmos/cosmos-sdk/client/tx"
 	"github.com/cosmos/cosmos-sdk/codec"
@@ -242,6 +244,7 @@ func (suite *AnteTestSuite) CreateTestEVMTxBuilder(
 
 		// Second round: all signer infos are set, so each signer can sign.
 
+		//nolint: exhaustruct
 		signerData := authsigning.SignerData{
 			ChainID:       suite.ctx.ChainID(),
 			AccountNumber: accNum,
@@ -262,21 +265,21 @@ func (suite *AnteTestSuite) CreateTestEVMTxBuilder(
 	return txBuilder
 }
 
-func (suite *AnteTestSuite) CreateTestCosmosMsgSend(gasPrice sdk.Int, denom string, amount sdk.Int, from sdk.AccAddress, to sdk.AccAddress) client.TxBuilder {
+func (suite *AnteTestSuite) CreateTestCosmosMsgSend(gasPrice sdkmath.Int, denom string, amount sdkmath.Int, from sdk.AccAddress, to sdk.AccAddress) client.TxBuilder {
 	return suite.CreateTestCosmosTxBuilder(gasPrice, denom, banktypes.NewMsgSend(from, to, sdk.NewCoins(sdk.NewCoin(denom, amount))))
 }
 
-func (suite *AnteTestSuite) CreateTestCosmosMsgMicrotx(gasPrice sdk.Int, denom string, amount sdk.Int, from sdk.AccAddress, to sdk.AccAddress) client.TxBuilder {
+func (suite *AnteTestSuite) CreateTestCosmosMsgMicrotx(gasPrice sdkmath.Int, denom string, amount sdkmath.Int, from sdk.AccAddress, to sdk.AccAddress) client.TxBuilder {
 	return suite.CreateTestCosmosTxBuilder(gasPrice, denom, microtxtypes.NewMsgMicrotx(from.String(), to.String(), sdk.NewCoin(denom, amount)))
 }
 
-func (suite *AnteTestSuite) CreateTestCosmosMsgMicrotxMsgSend(gasPrice sdk.Int, denom string, amount sdk.Int, from sdk.AccAddress, to sdk.AccAddress) client.TxBuilder {
+func (suite *AnteTestSuite) CreateTestCosmosMsgMicrotxMsgSend(gasPrice sdkmath.Int, denom string, amount sdkmath.Int, from sdk.AccAddress, to sdk.AccAddress) client.TxBuilder {
 	msgMicrotx := microtxtypes.NewMsgMicrotx(from.String(), to.String(), sdk.NewCoin(denom, amount))
 	msgSend := banktypes.NewMsgSend(from, to, sdk.NewCoins(sdk.NewCoin(denom, amount)))
 	return suite.CreateTestCosmosTxBuilder(gasPrice, denom, msgMicrotx, msgSend)
 }
 
-func (suite *AnteTestSuite) CreateTestCosmosTxBuilder(gasPrice sdk.Int, denom string, msgs ...sdk.Msg) client.TxBuilder {
+func (suite *AnteTestSuite) CreateTestCosmosTxBuilder(gasPrice sdkmath.Int, denom string, msgs ...sdk.Msg) client.TxBuilder {
 	txBuilder := suite.clientCtx.TxConfig.NewTxBuilder()
 
 	txBuilder.SetGasLimit(TestGasLimit)
@@ -290,6 +293,7 @@ func (suite *AnteTestSuite) CreateTestCosmosTxBuilder(gasPrice sdk.Int, denom st
 func (suite *AnteTestSuite) SignTestCosmosTx(chainId string, txBuilder client.TxBuilder, privkey cryptotypes.PrivKey, accNum uint64, sequence uint64) client.TxBuilder {
 	signMode := suite.clientCtx.TxConfig.SignModeHandler().DefaultMode()
 	pubKey := privkey.PubKey()
+	//nolint: exhaustruct
 	signerData := authsigning.SignerData{
 		ChainID:       chainId,
 		AccountNumber: accNum,
diff --git a/app/app.go b/app/app.go
index 4635a828..a5e923a1 100644
--- a/app/app.go
+++ b/app/app.go
@@ -1278,7 +1278,8 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino
 	paramsKeeper.Subspace(lockuptypes.ModuleName)
 	paramsKeeper.Subspace(microtxtypes.ModuleName)
 	paramsKeeper.Subspace(gasfreetypes.ModuleName)
-	paramsKeeper.Subspace(evmtypes.ModuleName).WithKeyTable(evmtypes.ParamKeyTable())
+	// Ethermint marks evmtypes.ParamKeyTable() as deprecated but does not have a documented replacement. For now, disable the linting error.
+	paramsKeeper.Subspace(evmtypes.ModuleName).WithKeyTable(evmtypes.ParamKeyTable()) //nolint: staticcheck
 	paramsKeeper.Subspace(erc20types.ModuleName)
 	paramsKeeper.Subspace(feemarkettypes.ModuleName).WithKeyTable(feemarkettypes.ParamKeyTable())
 	paramsKeeper.Subspace(icahosttypes.SubModuleName)
@@ -1338,7 +1339,9 @@ func (app *AltheaApp) NewAnteHandlerOptions(appOpts servertypes.AppOptions) ante
 		ExtensionOptionChecker: ante.CosmosExtensionOptionChecker,
 		TxFeeChecker:           ethante.NewDynamicFeeChecker(app.EvmKeeper),
 		DisabledAuthzMsgs: []string{
+			//nolint: exhaustruct
 			sdk.MsgTypeURL((&evmtypes.MsgEthereumTx{})),
+			//nolint: exhaustruct
 			sdk.MsgTypeURL((&vestingtypes.MsgCreateVestingAccount{})),
 		},
 		Cdc:           app.AppCodec(),
diff --git a/app/export.go b/app/export.go
index f63b295f..ac0364f4 100644
--- a/app/export.go
+++ b/app/export.go
@@ -113,7 +113,10 @@ func (app *AltheaApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs
 		feePool.CommunityPool = feePool.CommunityPool.Add(scraps...)
 		app.DistrKeeper.SetFeePool(ctx, feePool)
 
-		app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator())
+		err := app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator())
+		if err != nil {
+			panic(err)
+		}
 		return false
 	})
 
@@ -127,8 +130,14 @@ func (app *AltheaApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs
 		if err != nil {
 			panic(err)
 		}
-		app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr)
-		app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr)
+		err = app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr)
+		if err != nil {
+			panic(err)
+		}
+		err = app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr)
+		if err != nil {
+			panic(err)
+		}
 	}
 
 	// reset context height
diff --git a/app/pubkey_replacement.go b/app/pubkey_replacement.go
index d1ac529d..7164c04f 100644
--- a/app/pubkey_replacement.go
+++ b/app/pubkey_replacement.go
@@ -6,11 +6,12 @@ import (
 	"log"
 	"os"
 
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/cosmos/cosmos-sdk/client"
 	"github.com/cosmos/cosmos-sdk/codec/legacy"
 	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	"github.com/cosmos/cosmos-sdk/x/genutil/types"
 	slashing "github.com/cosmos/cosmos-sdk/x/slashing/types"
 	staking "github.com/cosmos/cosmos-sdk/x/staking/types"
@@ -71,7 +72,7 @@ func loadKeydataFromFile(clientCtx client.Context, replacementrJSON string, genD
 
 			toReplaceValConsAddress, err := val.GetConsAddr()
 			if err != nil {
-				panic(sdkerrors.Wrapf(err, "unable to get validator cons addr for validator %v", val.Description))
+				panic(errorsmod.Wrapf(err, "unable to get validator cons addr for validator %v", val.Description))
 			}
 
 			consPubKeyBz, err := sdk.GetFromBech32(replacement.ConsensusPubkey, sdk.GetConfig().GetBech32ConsensusPubPrefix())
@@ -91,18 +92,18 @@ func loadKeydataFromFile(clientCtx client.Context, replacementrJSON string, genD
 
 			replaceValConsAddress, err := val.GetConsAddr()
 			if err != nil {
-				panic(sdkerrors.Wrapf(err, "unable to get new validator cons addr for pubkey %v", consPubKey.String()))
+				panic(errorsmod.Wrapf(err, "unable to get new validator cons addr for pubkey %v", consPubKey.String()))
 			}
 
 			// nolint: errcheck
 			protoReplaceValConsPubKey, err := val.TmConsPublicKey()
 			if err != nil {
-				panic(sdkerrors.Wrapf(err, "unable to get validator tm cons public key for  validator %v", val.Description))
+				panic(errorsmod.Wrapf(err, "unable to get validator tm cons public key for  validator %v", val.Description))
 			}
 			// nolint: errcheck
 			replaceValConsPubKey, err := cryptocodec.PubKeyFromProto(protoReplaceValConsPubKey)
 			if err != nil {
-				panic(sdkerrors.Wrapf(err, "unable to decode pubkey %v", protoReplaceValConsPubKey.String()))
+				panic(errorsmod.Wrapf(err, "unable to decode pubkey %v", protoReplaceValConsPubKey.String()))
 			}
 
 			for i, signingInfo := range slashingGenesis.SigningInfos {
diff --git a/app/sigverify.go b/app/sigverify.go
index cdf75cce..a8e08c86 100644
--- a/app/sigverify.go
+++ b/app/sigverify.go
@@ -80,6 +80,7 @@ func CantoSigVerificationGasConsumer(
 		// Multisig keys
 		multisignature, ok := sig.Data.(*signing.MultiSignatureData)
 		if !ok {
+			//nolint: exhaustruct
 			return fmt.Errorf("expected %T, got, %T", &signing.MultiSignatureData{}, sig.Data)
 		}
 		return ConsumeMultisignatureVerificationGas(meter, multisignature, pubkey, params, sig.Sequence)
diff --git a/app/testutil.go b/app/testutil.go
index a3a7f875..154e9d72 100644
--- a/app/testutil.go
+++ b/app/testutil.go
@@ -114,6 +114,7 @@ func SetupWithDB(isCheckTx bool, patchGenesis func(*AltheaApp, simapp.GenesisSta
 
 		// Initialize the chain
 		app.InitChain(
+			//nolint: exhaustruct
 			abci.RequestInitChain{
 				ChainId:         "althea_7357-1",
 				Validators:      []abci.ValidatorUpdate{},
@@ -191,12 +192,13 @@ func genesisStateWithValSet(codec codec.Codec, genesisState simapp.GenesisState,
 			panic(err)
 		}
 		validator := stakingtypes.Validator{
-			OperatorAddress:   sdk.ValAddress(val.Address).String(),
-			ConsensusPubkey:   pkAny,
-			Jailed:            false,
-			Status:            stakingtypes.Bonded,
-			Tokens:            bondAmt,
-			DelegatorShares:   sdk.OneDec(),
+			OperatorAddress: sdk.ValAddress(val.Address).String(),
+			ConsensusPubkey: pkAny,
+			Jailed:          false,
+			Status:          stakingtypes.Bonded,
+			Tokens:          bondAmt,
+			DelegatorShares: sdk.OneDec(),
+			//nolint: exhaustruct
 			Description:       stakingtypes.Description{},
 			UnbondingHeight:   int64(0),
 			UnbondingTime:     time.Unix(0, 0).UTC(),
diff --git a/cmd/althea/genaccounts.go b/cmd/althea/genaccounts.go
index 1ce21ef4..5635fdbb 100644
--- a/cmd/althea/genaccounts.go
+++ b/cmd/althea/genaccounts.go
@@ -10,7 +10,6 @@ import (
 
 	"github.com/cosmos/cosmos-sdk/client"
 	"github.com/cosmos/cosmos-sdk/client/flags"
-	"github.com/cosmos/cosmos-sdk/codec"
 	"github.com/cosmos/cosmos-sdk/crypto/keyring"
 	"github.com/cosmos/cosmos-sdk/server"
 	sdk "github.com/cosmos/cosmos-sdk/types"
@@ -40,8 +39,7 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa
 		Args: cobra.ExactArgs(2),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			clientCtx := client.GetClientContextFromCmd(cmd)
-			depCdc := clientCtx.Codec
-			cdc := depCdc.(codec.Codec)
+			cdc := clientCtx.Codec
 
 			serverCtx := server.GetServerContextFromCmd(cmd)
 			config := serverCtx.Config
@@ -163,7 +161,7 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa
 
 			appState[authtypes.ModuleName] = authGenStateBz
 
-			bankGenState := banktypes.GetGenesisStateFromAppState(depCdc, appState)
+			bankGenState := banktypes.GetGenesisStateFromAppState(cdc, appState)
 			bankGenState.Balances = append(bankGenState.Balances, balances)
 			bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances)
 			bankGenState.Supply = bankGenState.Supply.Add(balances.Coins...)
diff --git a/ibcutils/testing/app.go b/ibcutils/testing/app.go
index c41e9156..3fcff996 100644
--- a/ibcutils/testing/app.go
+++ b/ibcutils/testing/app.go
@@ -13,6 +13,8 @@ import (
 	tmtypes "github.com/tendermint/tendermint/types"
 	dbm "github.com/tendermint/tm-db"
 
+	sdkmath "cosmossdk.io/math"
+
 	"github.com/cosmos/cosmos-sdk/baseapp"
 	"github.com/cosmos/cosmos-sdk/client"
 	"github.com/cosmos/cosmos-sdk/codec"
@@ -75,7 +77,7 @@ func SetupTestingAltheaApp() (TestingApp, map[string]json.RawMessage) {
 	return app, althea.NewDefaultGenesisState()
 }
 
-func initializeValsAndDelegations(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, bondAmt sdk.Int) ([]stakingtypes.Validator, []stakingtypes.Delegation) {
+func initializeValsAndDelegations(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, bondAmt sdkmath.Int) ([]stakingtypes.Validator, []stakingtypes.Delegation) {
 	validators := make([]stakingtypes.Validator, 0, len(valSet.Validators))
 	delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators))
 
@@ -105,7 +107,7 @@ func initializeValsAndDelegations(t *testing.T, valSet *tmtypes.ValidatorSet, ge
 	return validators, delegations
 }
 
-func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, chainID string, powerReduction sdk.Int, balances ...banktypes.Balance) TestingApp {
+func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, chainID string, powerReduction sdkmath.Int, balances ...banktypes.Balance) TestingApp {
 	app, genesisState := DefaultTestingAppInit()
 
 	// set genesis accounts
diff --git a/ibcutils/testing/chain.go b/ibcutils/testing/chain.go
index 426726d4..5d86eaf6 100644
--- a/ibcutils/testing/chain.go
+++ b/ibcutils/testing/chain.go
@@ -7,13 +7,14 @@ import (
 
 	"github.com/stretchr/testify/require"
 
+	errorsmod "cosmossdk.io/errors"
+
 	bam "github.com/cosmos/cosmos-sdk/baseapp"
 	"github.com/cosmos/cosmos-sdk/client"
 	"github.com/cosmos/cosmos-sdk/codec"
 	"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
 	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
 	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 	"github.com/cosmos/cosmos-sdk/x/staking/teststaking"
@@ -441,7 +442,7 @@ func (chain *TestChain) ConstructUpdateTMClientHeaderWithTrustedHeight(counterpa
 		// NextValidatorsHash
 		tmTrustedVals, ok = counterparty.GetValsAtHeight(int64(trustedHeight.RevisionHeight + 1))
 		if !ok {
-			return nil, sdkerrors.Wrapf(ibctmtypes.ErrInvalidHeaderHeight, "could not retrieve trusted validators at trustedHeight: %d", trustedHeight)
+			return nil, errorsmod.Wrapf(ibctmtypes.ErrInvalidHeaderHeight, "could not retrieve trusted validators at trustedHeight: %d", trustedHeight)
 		}
 	}
 	// inject trusted fields into last header
diff --git a/ibcutils/testing/events.go b/ibcutils/testing/events.go
index 8df9f000..c1abb98e 100644
--- a/ibcutils/testing/events.go
+++ b/ibcutils/testing/events.go
@@ -67,7 +67,7 @@ func ParsePacketFromEvents(events sdk.Events) (channeltypes.Packet, error) {
 			for _, attr := range ev.Attributes {
 
 				switch string(attr.Key) {
-				case channeltypes.AttributeKeyData:
+				case channeltypes.AttributeKeyDataHex:
 					packet.Data = attr.Value
 
 				case channeltypes.AttributeKeySequence:
@@ -123,7 +123,7 @@ func ParseAckFromEvents(events sdk.Events) ([]byte, error) {
 	for _, ev := range events {
 		if ev.Type == channeltypes.EventTypeWriteAck {
 			for _, attr := range ev.Attributes {
-				if string(attr.Key) == channeltypes.AttributeKeyAck {
+				if string(attr.Key) == channeltypes.AttributeKeyAckHex {
 					return attr.Value, nil
 				}
 			}
diff --git a/ibcutils/utils.go b/ibcutils/utils.go
index 364726f1..741ba25f 100644
--- a/ibcutils/utils.go
+++ b/ibcutils/utils.go
@@ -3,6 +3,8 @@ package ibcutils
 import (
 	"strings"
 
+	errorsmod "cosmossdk.io/errors"
+
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 
@@ -24,21 +26,21 @@ func GetTransferSenderRecipient(packet channeltypes.Packet) (
 	// unmarshal packet data to obtain the sender and recipient
 	var data transfertypes.FungibleTokenPacketData
 	if err := transfertypes.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil {
-		return nil, nil, "", "", sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data")
+		return nil, nil, "", "", errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data")
 	}
 
 	// validate the sender bech32 address from the counterparty chain
 	// and change the bech32 human readable prefix (HRP) of the sender to `althea`
 	sender, err = GetAltheaAddressFromBech32(data.Sender)
 	if err != nil {
-		return nil, nil, "", "", sdkerrors.Wrap(err, "invalid sender")
+		return nil, nil, "", "", errorsmod.Wrap(err, "invalid sender")
 	}
 
 	// validate the recipient bech32 address from the counterparty chain
 	// and change the bech32 human readable prefix (HRP) of the recipient to `althea`
 	recipient, err = GetAltheaAddressFromBech32(data.Receiver)
 	if err != nil {
-		return nil, nil, "", "", sdkerrors.Wrap(err, "invalid recipient")
+		return nil, nil, "", "", errorsmod.Wrap(err, "invalid recipient")
 	}
 
 	return sender, recipient, data.Sender, data.Receiver, nil
@@ -49,15 +51,15 @@ func GetTransferAmount(packet channeltypes.Packet) (string, error) {
 	// unmarshal packet data to obtain the sender and recipient
 	var data transfertypes.FungibleTokenPacketData
 	if err := transfertypes.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil {
-		return "", sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data")
+		return "", errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data")
 	}
 
 	if data.Amount == "" {
-		return "", sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "empty amount")
+		return "", errorsmod.Wrapf(sdkerrors.ErrInvalidCoins, "empty amount")
 	}
 
 	if _, ok := sdk.NewIntFromString(data.Amount); !ok {
-		return "", sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "invalid amount")
+		return "", errorsmod.Wrapf(sdkerrors.ErrInvalidCoins, "invalid amount")
 	}
 
 	return data.Amount, nil
@@ -115,12 +117,12 @@ func GetReceivedCoin(srcPort, srcChannel, dstPort, dstChannel, rawDenom, rawAmt
 func GetAltheaAddressFromBech32(address string) (sdk.AccAddress, error) {
 	bech32Prefix := strings.SplitN(address, "1", 2)[0]
 	if bech32Prefix == address {
-		return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid bech32 address: %s", address)
+		return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid bech32 address: %s", address)
 	}
 
 	addressBz, err := sdk.GetFromBech32(address, bech32Prefix)
 	if err != nil {
-		return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid address %s, %s", address, err.Error())
+		return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid address %s, %s", address, err.Error())
 	}
 
 	// safety check: shouldn't happen
diff --git a/testutil/network/network.go b/testutil/network/network.go
index 269d161e..78e8e5a4 100644
--- a/testutil/network/network.go
+++ b/testutil/network/network.go
@@ -27,6 +27,8 @@ import (
 	dbm "github.com/tendermint/tm-db"
 	"google.golang.org/grpc"
 
+	sdkmath "cosmossdk.io/math"
+
 	"github.com/cosmos/cosmos-sdk/baseapp"
 	"github.com/cosmos/cosmos-sdk/client"
 	"github.com/cosmos/cosmos-sdk/client/tx"
@@ -75,9 +77,9 @@ type Config struct {
 	AppConstructor    AppConstructor      // the ABCI application constructor
 	GenesisState      simapp.GenesisState // custom gensis state to provide
 	TimeoutCommit     time.Duration       // the consensus commitment timeout
-	AccountTokens     sdk.Int             // the amount of unique validator tokens (e.g. 1000node0)
-	StakingTokens     sdk.Int             // the amount of tokens each validator has available to stake
-	BondedTokens      sdk.Int             // the amount of tokens each validator stakes
+	AccountTokens     sdkmath.Int         // the amount of unique validator tokens (e.g. 1000node0)
+	StakingTokens     sdkmath.Int         // the amount of tokens each validator has available to stake
+	BondedTokens      sdkmath.Int         // the amount of tokens each validator stakes
 	NumValidators     int                 // the total number of validators to create and bond
 	ChainID           string              // the network chain-id
 	BondDenom         string              // the staking bond denomination
@@ -98,6 +100,7 @@ type Config struct {
 func DefaultConfig() Config {
 	encCfg := althea.MakeEncodingConfig()
 
+	//nolint: exhaustruct
 	return Config{
 		Codec:             encCfg.Codec,
 		TxConfig:          encCfg.TxConfig,
@@ -211,6 +214,7 @@ func NewCLILogger(cmd *cobra.Command) CLILogger {
 }
 
 // New creates a new Network for integration tests or in-process testnets run via the CLI
+// nolint: gocyclo
 func New(l Logger, baseDir string, cfg Config) (*Network, error) {
 	// only one caller/test can create and use a network at a time
 	l.Log("acquiring test network lock")
@@ -324,7 +328,11 @@ func New(l Logger, baseDir string, cfg Config) (*Network, error) {
 		logger := log.NewNopLogger()
 		if cfg.EnableTMLogging {
 			logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout))
-			logger, _ = tmflags.ParseLogLevel("info", logger, tmcfg.DefaultLogLevel)
+			var err error
+			logger, err = tmflags.ParseLogLevel("info", logger, tmcfg.DefaultLogLevel)
+			if err != nil {
+				panic(err)
+			}
 		}
 
 		ctx.Logger = logger
@@ -479,6 +487,7 @@ func New(l Logger, baseDir string, cfg Config) (*Network, error) {
 			return nil, err
 		}
 
+		//nolint: exhaustruct
 		clientCtx := client.Context{}.
 			WithKeyringDir(clientDir).
 			WithKeyring(kb).
@@ -490,6 +499,7 @@ func New(l Logger, baseDir string, cfg Config) (*Network, error) {
 			WithTxConfig(cfg.TxConfig).
 			WithAccountRetriever(cfg.AccountRetriever)
 
+		//nolint: exhaustruct
 		network.Validators[i] = &Validator{
 			AppConfig:  appCfg,
 			ClientCtx:  clientCtx,
@@ -614,7 +624,10 @@ func (n *Network) Cleanup() {
 
 	for _, v := range n.Validators {
 		if v.tmNode != nil && v.tmNode.IsRunning() {
-			_ = v.tmNode.Stop()
+			err := v.tmNode.Stop()
+			if err != nil {
+				panic(err)
+			}
 		}
 
 		if v.api != nil {
diff --git a/testutil/network/util.go b/testutil/network/util.go
index 928ab49c..8ddd9d87 100644
--- a/testutil/network/util.go
+++ b/testutil/network/util.go
@@ -228,6 +228,7 @@ func initGenFiles(cfg Config, genAccounts []authtypes.GenesisAccount, genBalance
 		return err
 	}
 
+	//nolint: exhaustruct
 	genDoc := types.GenesisDoc{
 		ChainID:    cfg.ChainID,
 		AppState:   appGenStateJSON,
diff --git a/x/erc20/client/cli/query.go b/x/erc20/client/cli/query.go
index a99ebbb1..0624249d 100644
--- a/x/erc20/client/cli/query.go
+++ b/x/erc20/client/cli/query.go
@@ -12,6 +12,7 @@ import (
 
 // GetQueryCmd returns the parent command for all erc20 CLI query commands
 func GetQueryCmd() *cobra.Command {
+	//nolint: exhaustruct
 	cmd := &cobra.Command{
 		Use:                        types.ModuleName,
 		Short:                      "Querying commands for the erc20 module",
@@ -30,6 +31,7 @@ func GetQueryCmd() *cobra.Command {
 
 // GetTokenPairsCmd queries all registered token pairs
 func GetTokenPairsCmd() *cobra.Command {
+	//nolint: exhaustruct
 	cmd := &cobra.Command{
 		Use:   "token-pairs",
 		Short: "Gets registered token pairs",
@@ -67,6 +69,7 @@ func GetTokenPairsCmd() *cobra.Command {
 
 // GetTokenPairsCmd queries a registered token pair
 func GetTokenPairCmd() *cobra.Command {
+	//nolint: exhaustruct
 	cmd := &cobra.Command{
 		Use:   "token-pair [token]",
 		Short: "Get a registered token pair",
@@ -99,6 +102,7 @@ func GetTokenPairCmd() *cobra.Command {
 
 // GetParamsCmd queries erc20 module params
 func GetParamsCmd() *cobra.Command {
+	//nolint: exhaustruct
 	cmd := &cobra.Command{
 		Use:   "params",
 		Short: "Gets erc20 params",
diff --git a/x/erc20/client/cli/tx.go b/x/erc20/client/cli/tx.go
index a41a7166..1287256e 100644
--- a/x/erc20/client/cli/tx.go
+++ b/x/erc20/client/cli/tx.go
@@ -22,6 +22,7 @@ import (
 
 // NewTxCmd returns a root CLI command handler for erc20 transaction commands
 func NewTxCmd() *cobra.Command {
+	//nolint: exhaustruct
 	txCmd := &cobra.Command{
 		Use:                        types.ModuleName,
 		Short:                      "erc20 subcommands",
@@ -39,6 +40,7 @@ func NewTxCmd() *cobra.Command {
 
 // NewConvertCoinCmd returns a CLI command handler for converting a Cosmos coin
 func NewConvertCoinCmd() *cobra.Command {
+	//nolint: exhaustruct
 	cmd := &cobra.Command{
 		Use:   "convert-coin [coin] [receiver_hex]",
 		Short: "Convert a Cosmos coin to ERC20. When the receiver [optional] is omitted, the ERC20 tokens are transferred to the sender.",
@@ -86,6 +88,7 @@ func NewConvertCoinCmd() *cobra.Command {
 
 // NewConvertERC20Cmd returns a CLI command handler for converting an ERC20
 func NewConvertERC20Cmd() *cobra.Command {
+	//nolint: exhaustruct
 	cmd := &cobra.Command{
 		Use:   "convert-erc20 [contract-address] [amount] [receiver]",
 		Short: "Convert an ERC20 token to Cosmos coin.  When the receiver [optional] is omitted, the Cosmos coins are transferred to the sender.",
@@ -137,6 +140,7 @@ func NewConvertERC20Cmd() *cobra.Command {
 
 // NewRegisterCoinProposalCmd implements the command to submit a community-pool-spend proposal
 func NewRegisterCoinProposalCmd() *cobra.Command {
+	//nolint: exhaustruct
 	cmd := &cobra.Command{
 		Use:   "register-coin [metadata]",
 		Args:  cobra.ExactArgs(1),
@@ -172,12 +176,13 @@ Where metadata.json contains (example):
 			if err != nil {
 				return err
 			}
-
+			// We have yet to replace the use of legacy proposals, disable the linting errors for now.
+			//nolint: staticcheck
 			title, err := cmd.Flags().GetString(cli.FlagTitle)
 			if err != nil {
 				return err
 			}
-
+			//nolint: staticcheck
 			description, err := cmd.Flags().GetString(cli.FlagDescription)
 			if err != nil {
 				return err
@@ -215,12 +220,17 @@ Where metadata.json contains (example):
 		},
 	}
 
+	// We have yet to replace the use of legacy proposals, disable the linting errors for now.
+	//nolint: staticcheck
 	cmd.Flags().String(cli.FlagTitle, "", "title of proposal")
+	//nolint: staticcheck
 	cmd.Flags().String(cli.FlagDescription, "", "description of proposal")
 	cmd.Flags().String(cli.FlagDeposit, "1acanto", "deposit of proposal")
+	//nolint: staticcheck
 	if err := cmd.MarkFlagRequired(cli.FlagTitle); err != nil {
 		panic(err)
 	}
+	//nolint: staticcheck
 	if err := cmd.MarkFlagRequired(cli.FlagDescription); err != nil {
 		panic(err)
 	}
@@ -232,6 +242,7 @@ Where metadata.json contains (example):
 
 // NewRegisterERC20ProposalCmd implements the command to submit a community-pool-spend proposal
 func NewRegisterERC20ProposalCmd() *cobra.Command {
+	//nolint: exhaustruct
 	cmd := &cobra.Command{
 		Use:     "register-erc20 [erc20-address]",
 		Args:    cobra.ExactArgs(1),
@@ -244,11 +255,13 @@ func NewRegisterERC20ProposalCmd() *cobra.Command {
 				return err
 			}
 
+			//nolint: staticcheck
 			title, err := cmd.Flags().GetString(cli.FlagTitle)
 			if err != nil {
 				return err
 			}
 
+			//nolint: staticcheck
 			description, err := cmd.Flags().GetString(cli.FlagDescription)
 			if err != nil {
 				return err
@@ -281,12 +294,16 @@ func NewRegisterERC20ProposalCmd() *cobra.Command {
 		},
 	}
 
+	//nolint: staticcheck
 	cmd.Flags().String(cli.FlagTitle, "", "title of proposal")
+	//nolint: staticcheck
 	cmd.Flags().String(cli.FlagDescription, "", "description of proposal")
 	cmd.Flags().String(cli.FlagDeposit, "1acanto", "deposit of proposal")
+	//nolint: staticcheck
 	if err := cmd.MarkFlagRequired(cli.FlagTitle); err != nil {
 		panic(err)
 	}
+	//nolint: staticcheck
 	if err := cmd.MarkFlagRequired(cli.FlagDescription); err != nil {
 		panic(err)
 	}
@@ -298,6 +315,7 @@ func NewRegisterERC20ProposalCmd() *cobra.Command {
 
 // NewToggleTokenConversionProposalCmd implements the command to submit a community-pool-spend proposal
 func NewToggleTokenConversionProposalCmd() *cobra.Command {
+	//nolint: exhaustruct
 	cmd := &cobra.Command{
 		Use:     "toggle-token-conversion [token]",
 		Args:    cobra.ExactArgs(1),
@@ -310,11 +328,13 @@ func NewToggleTokenConversionProposalCmd() *cobra.Command {
 				return err
 			}
 
+			//nolint: staticcheck
 			title, err := cmd.Flags().GetString(cli.FlagTitle)
 			if err != nil {
 				return err
 			}
 
+			//nolint: staticcheck
 			description, err := cmd.Flags().GetString(cli.FlagDescription)
 			if err != nil {
 				return err
@@ -347,12 +367,16 @@ func NewToggleTokenConversionProposalCmd() *cobra.Command {
 		},
 	}
 
+	//nolint: staticcheck
 	cmd.Flags().String(cli.FlagTitle, "", "title of proposal")
+	//nolint: staticcheck
 	cmd.Flags().String(cli.FlagDescription, "", "description of proposal")
 	cmd.Flags().String(cli.FlagDeposit, "1acanto", "deposit of proposal")
+	//nolint: staticcheck
 	if err := cmd.MarkFlagRequired(cli.FlagTitle); err != nil {
 		panic(err)
 	}
+	//nolint: staticcheck
 	if err := cmd.MarkFlagRequired(cli.FlagDescription); err != nil {
 		panic(err)
 	}
diff --git a/x/erc20/client/cli/utils.go b/x/erc20/client/cli/utils.go
index f9f77de3..43f7af74 100644
--- a/x/erc20/client/cli/utils.go
+++ b/x/erc20/client/cli/utils.go
@@ -1,7 +1,7 @@
 package cli
 
 import (
-	"io/ioutil"
+	"os"
 	"path/filepath"
 
 	"github.com/cosmos/cosmos-sdk/codec"
@@ -10,9 +10,10 @@ import (
 
 // ParseRegisterCoinProposal reads and parses a ParseRegisterCoinProposal from a file.
 func ParseMetadata(cdc codec.JSONCodec, metadataFile string) (banktypes.Metadata, error) {
+	//nolint: exhaustruct
 	metadata := banktypes.Metadata{}
 
-	contents, err := ioutil.ReadFile(filepath.Clean(metadataFile))
+	contents, err := os.ReadFile(filepath.Clean(metadataFile))
 	if err != nil {
 		return metadata, err
 	}
diff --git a/x/erc20/genesis_test.go b/x/erc20/genesis_test.go
index bebe536b..3973f86c 100644
--- a/x/erc20/genesis_test.go
+++ b/x/erc20/genesis_test.go
@@ -42,12 +42,14 @@ func (suite *GenesisTestSuite) SetupTest() {
 		return gs
 	})
 
+	//nolint: exhaustruct
 	suite.ctx = suite.app.BaseApp.NewContext(false, tmproto.Header{
 		Height:          1,
 		ChainID:         "althea_7357-1",
 		Time:            time.Now().UTC(),
 		ProposerAddress: althea.ValidatorPubKey.Address().Bytes(),
 
+		//nolint: exhaustruct
 		Version: tmversion.Consensus{
 			Block: version.BlockProtocol,
 		},
@@ -75,6 +77,7 @@ func (suite *GenesisTestSuite) TestERC20InitGenesis() {
 	}{
 		{
 			"empty genesis",
+			//nolint: exhaustruct
 			types.GenesisState{},
 		},
 		{
@@ -120,6 +123,7 @@ func (suite *GenesisTestSuite) TestErc20ExportGenesis() {
 	}{
 		{
 			"empty genesis",
+			//nolint: exhaustruct
 			types.GenesisState{},
 		},
 		{
diff --git a/x/erc20/handler.go b/x/erc20/handler.go
index 0ad2c3ca..e5b96b12 100644
--- a/x/erc20/handler.go
+++ b/x/erc20/handler.go
@@ -1,6 +1,8 @@
 package erc20
 
 import (
+	errorsmod "cosmossdk.io/errors"
+
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 
@@ -20,7 +22,7 @@ func NewHandler(server types.MsgServer) sdk.Handler {
 			res, err := server.ConvertERC20(sdk.WrapSDKContext(ctx), msg)
 			return sdk.WrapServiceResult(ctx, res, err)
 		default:
-			err := sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg)
+			err := errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg)
 			return nil, err
 		}
 	}
diff --git a/x/erc20/keeper/evm.go b/x/erc20/keeper/evm.go
index 5ded21dd..8c0bbfdb 100644
--- a/x/erc20/keeper/evm.go
+++ b/x/erc20/keeper/evm.go
@@ -4,6 +4,8 @@ import (
 	"encoding/json"
 	"math/big"
 
+	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"
@@ -38,7 +40,7 @@ func (k Keeper) DeployERC20Contract(
 		decimals,
 	)
 	if err != nil {
-		return common.Address{}, sdkerrors.Wrapf(types.ErrABIPack, "coin metadata is invalid %s: %s", coinMetadata.Name, err.Error())
+		return common.Address{}, errorsmod.Wrapf(types.ErrABIPack, "coin metadata is invalid %s: %s", coinMetadata.Name, err.Error())
 	}
 
 	data := make([]byte, len(contracts.ERC20MinterBurnerDecimalsContract.Bin)+len(ctorArgs))
@@ -53,7 +55,7 @@ func (k Keeper) DeployERC20Contract(
 	contractAddr := crypto.CreateAddress(types.ModuleAddress, nonce)
 	_, err = k.CallEVMWithData(ctx, types.ModuleAddress, nil, data, true)
 	if err != nil {
-		return common.Address{}, sdkerrors.Wrapf(err, "failed to deploy contract for %s", coinMetadata.Name)
+		return common.Address{}, errorsmod.Wrapf(err, "failed to deploy contract for %s", coinMetadata.Name)
 	}
 
 	return contractAddr, nil
@@ -79,7 +81,7 @@ func (k Keeper) QueryERC20(
 	}
 
 	if err := erc20.UnpackIntoInterface(&nameRes, "name", res.Ret); err != nil {
-		return types.ERC20Data{}, sdkerrors.Wrapf(
+		return types.ERC20Data{}, errorsmod.Wrapf(
 			types.ErrABIUnpack, "failed to unpack name: %s", err.Error(),
 		)
 	}
@@ -91,7 +93,7 @@ func (k Keeper) QueryERC20(
 	}
 
 	if err := erc20.UnpackIntoInterface(&symbolRes, "symbol", res.Ret); err != nil {
-		return types.ERC20Data{}, sdkerrors.Wrapf(
+		return types.ERC20Data{}, errorsmod.Wrapf(
 			types.ErrABIUnpack, "failed to unpack symbol: %s", err.Error(),
 		)
 	}
@@ -103,7 +105,7 @@ func (k Keeper) QueryERC20(
 	}
 
 	if err := erc20.UnpackIntoInterface(&decimalRes, "decimals", res.Ret); err != nil {
-		return types.ERC20Data{}, sdkerrors.Wrapf(
+		return types.ERC20Data{}, errorsmod.Wrapf(
 			types.ErrABIUnpack, "failed to unpack decimals: %s", err.Error(),
 		)
 	}
@@ -146,15 +148,15 @@ func (k Keeper) CallEVM(
 ) (*evmtypes.MsgEthereumTxResponse, error) {
 	data, err := abi.Pack(method, args...)
 	if err != nil {
-		return nil, sdkerrors.Wrap(
+		return nil, errorsmod.Wrap(
 			types.ErrABIPack,
-			sdkerrors.Wrap(err, "failed to create transaction data").Error(),
+			errorsmod.Wrap(err, "failed to create transaction data").Error(),
 		)
 	}
 
 	resp, err := k.CallEVMWithData(ctx, from, &contract, data, commit)
 	if err != nil {
-		return nil, sdkerrors.Wrapf(err, "contract call failed: method '%s', contract '%s'", method, contract)
+		return nil, errorsmod.Wrapf(err, "contract call failed: method '%s', contract '%s'", method, contract)
 	}
 	return resp, nil
 }
@@ -174,15 +176,17 @@ func (k Keeper) CallEVMWithData(
 
 	gasCap := config.DefaultGasCap
 	if commit {
+		//nolint: exhaustruct
 		args, err := json.Marshal(evmtypes.TransactionArgs{
 			From: &from,
 			To:   contract,
 			Data: (*hexutil.Bytes)(&data),
 		})
 		if err != nil {
-			return nil, sdkerrors.Wrapf(sdkerrors.ErrJSONMarshal, "failed to marshal tx args: %s", err.Error())
+			return nil, errorsmod.Wrapf(sdkerrors.ErrJSONMarshal, "failed to marshal tx args: %s", err.Error())
 		}
 
+		//nolint: exhaustruct
 		gasRes, err := k.evmKeeper.EstimateGas(sdk.WrapSDKContext(ctx), &evmtypes.EthCallRequest{
 			Args:   args,
 			GasCap: config.DefaultGasCap,
@@ -213,7 +217,7 @@ func (k Keeper) CallEVMWithData(
 	}
 
 	if res.Failed() {
-		return nil, sdkerrors.Wrap(evmtypes.ErrVMExecution, res.VmError)
+		return nil, errorsmod.Wrap(evmtypes.ErrVMExecution, res.VmError)
 	}
 
 	return res, nil
@@ -231,7 +235,7 @@ func (k Keeper) monitorApprovalEvent(res *evmtypes.MsgEthereumTxResponse) error
 
 	for _, log := range res.Logs {
 		if log.Topics[0] == logApprovalSigHash.Hex() {
-			return sdkerrors.Wrapf(
+			return errorsmod.Wrapf(
 				types.ErrUnexpectedEvent, "unexpected Approval event",
 			)
 		}
diff --git a/x/erc20/keeper/evm_hooks.go b/x/erc20/keeper/evm_hooks.go
index 2736224d..53b5e52a 100644
--- a/x/erc20/keeper/evm_hooks.go
+++ b/x/erc20/keeper/evm_hooks.go
@@ -15,6 +15,7 @@ import (
 	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
 )
 
+// nolint: exhaustruct
 var _ evmtypes.EvmHooks = Hooks{}
 
 // Hooks wrapper struct for erc20 keeper
diff --git a/x/erc20/keeper/evm_hooks_test.go b/x/erc20/keeper/evm_hooks_test.go
index 174dc2e8..0e826830 100644
--- a/x/erc20/keeper/evm_hooks_test.go
+++ b/x/erc20/keeper/evm_hooks_test.go
@@ -157,8 +157,11 @@ func (suite *KeeperTestSuite) TestEvmHooksRegisteredCoin() {
 			contractAddr := common.HexToAddress(pair.Erc20Address)
 
 			coins := sdk.NewCoins(sdk.NewCoin(cosmosTokenBase, sdk.NewInt(tc.mint)))
-			suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
-			suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			var err error
+			err = suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
+			suite.Require().NoError(err)
+			err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			suite.Require().NoError(err)
 
 			convertCoin := types.NewMsgConvertCoin(
 				sdk.NewCoin(cosmosTokenBase, sdk.NewInt(tc.burn)),
@@ -167,7 +170,7 @@ func (suite *KeeperTestSuite) TestEvmHooksRegisteredCoin() {
 			)
 
 			ctx := sdk.WrapSDKContext(suite.ctx)
-			_, err := suite.app.Erc20Keeper.ConvertCoin(ctx, convertCoin)
+			_, err = suite.app.Erc20Keeper.ConvertCoin(ctx, convertCoin)
 			suite.Require().NoError(err, tc.name)
 			suite.Commit()
 
@@ -179,7 +182,6 @@ func (suite *KeeperTestSuite) TestEvmHooksRegisteredCoin() {
 			// Burn the 10 tokens of suite.address (owner)
 			_ = suite.TransferERC20TokenToModule(contractAddr, suite.address, big.NewInt(tc.reconvert))
 
-			balance = suite.BalanceOf(common.HexToAddress(pair.Erc20Address), suite.address)
 			cosmosBalance = suite.app.BankKeeper.GetBalance(suite.ctx, sender, metadata.Base)
 
 			if tc.result {
@@ -232,7 +234,9 @@ func (suite *KeeperTestSuite) TestPostTxProcessing() {
 		{
 			"Empty logs",
 			func() {
+				//nolint: exhaustruct
 				log := ethtypes.Log{}
+				//nolint: exhaustruct
 				receipt = &ethtypes.Receipt{
 					Logs: []*ethtypes.Log{&log},
 				}
@@ -243,9 +247,11 @@ func (suite *KeeperTestSuite) TestPostTxProcessing() {
 			"No log data",
 			func() {
 				topics := []common.Hash{transferEvent.ID, account.Hash(), types.ModuleAddress.Hash()}
+				//nolint: exhaustruct
 				log := ethtypes.Log{
 					Topics: topics,
 				}
+				//nolint: exhaustruct
 				receipt = &ethtypes.Receipt{
 					Logs: []*ethtypes.Log{&log},
 				}
@@ -256,10 +262,12 @@ func (suite *KeeperTestSuite) TestPostTxProcessing() {
 			"Non recognized event",
 			func() {
 				topics := []common.Hash{{}, account.Hash(), account.Hash()}
+				//nolint: exhaustruct
 				log := ethtypes.Log{
 					Topics: topics,
 					Data:   transferData,
 				}
+				//nolint: exhaustruct
 				receipt = &ethtypes.Receipt{
 					Logs: []*ethtypes.Log{&log},
 				}
@@ -271,10 +279,12 @@ func (suite *KeeperTestSuite) TestPostTxProcessing() {
 			func() {
 				aprovalEvent := erc20.Events["Approval"]
 				topics := []common.Hash{aprovalEvent.ID, account.Hash(), account.Hash()}
+				//nolint: exhaustruct
 				log := ethtypes.Log{
 					Topics: topics,
 					Data:   transferData,
 				}
+				//nolint: exhaustruct
 				receipt = &ethtypes.Receipt{
 					Logs: []*ethtypes.Log{&log},
 				}
@@ -285,10 +295,12 @@ func (suite *KeeperTestSuite) TestPostTxProcessing() {
 			"No log address",
 			func() {
 				topics := []common.Hash{transferEvent.ID, account.Hash(), types.ModuleAddress.Hash()}
+				//nolint: exhaustruct
 				log := ethtypes.Log{
 					Topics: topics,
 					Data:   transferData,
 				}
+				//nolint: exhaustruct
 				receipt = &ethtypes.Receipt{
 					Logs: []*ethtypes.Log{&log},
 				}
@@ -299,10 +311,12 @@ func (suite *KeeperTestSuite) TestPostTxProcessing() {
 			"No data on topic",
 			func() {
 				topics := []common.Hash{transferEvent.ID}
+				//nolint: exhaustruct
 				log := ethtypes.Log{
 					Topics: topics,
 					Data:   transferData,
 				}
+				//nolint: exhaustruct
 				receipt = &ethtypes.Receipt{
 					Logs: []*ethtypes.Log{&log},
 				}
@@ -320,11 +334,13 @@ func (suite *KeeperTestSuite) TestPostTxProcessing() {
 				suite.Require().NoError(err)
 
 				topics := []common.Hash{transferEvent.ID, account.Hash(), account.Hash()}
+				//nolint: exhaustruct
 				log := ethtypes.Log{
 					Topics:  topics,
 					Data:    transferData,
 					Address: contractAddr,
 				}
+				//nolint: exhaustruct
 				receipt = &ethtypes.Receipt{
 					Logs: []*ethtypes.Log{&log},
 				}
@@ -342,11 +358,13 @@ func (suite *KeeperTestSuite) TestPostTxProcessing() {
 				suite.Require().NoError(err)
 
 				topics := []common.Hash{transferEvent.ID, account.Hash(), types.ModuleAddress.Hash()}
+				//nolint: exhaustruct
 				log := ethtypes.Log{
 					Topics:  topics,
 					Data:    transferData,
 					Address: contractAddr,
 				}
+				//nolint: exhaustruct
 				receipt = &ethtypes.Receipt{
 					Logs: []*ethtypes.Log{&log},
 				}
@@ -367,11 +385,13 @@ func (suite *KeeperTestSuite) TestPostTxProcessing() {
 				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, *pair)
 
 				topics := []common.Hash{transferEvent.ID, account.Hash(), types.ModuleAddress.Hash()}
+				//nolint: exhaustruct
 				log := ethtypes.Log{
 					Topics:  topics,
 					Data:    transferData,
 					Address: contractAddr,
 				}
+				//nolint: exhaustruct
 				receipt = &ethtypes.Receipt{
 					Logs: []*ethtypes.Log{&log},
 				}
@@ -392,11 +412,13 @@ func (suite *KeeperTestSuite) TestPostTxProcessing() {
 				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, *pair)
 
 				topics := []common.Hash{transferEvent.ID, account.Hash(), types.ModuleAddress.Hash()}
+				//nolint: exhaustruct
 				log := ethtypes.Log{
 					Topics:  topics,
 					Data:    transferData,
 					Address: contractAddr,
 				}
+				//nolint: exhaustruct
 				receipt = &ethtypes.Receipt{
 					Logs: []*ethtypes.Log{&log},
 				}
diff --git a/x/erc20/keeper/evm_test.go b/x/erc20/keeper/evm_test.go
index 7cc7391e..7665032a 100644
--- a/x/erc20/keeper/evm_test.go
+++ b/x/erc20/keeper/evm_test.go
@@ -13,6 +13,7 @@ import (
 	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
 )
 
+// nolint: exhaustruct
 func (suite *KeeperTestSuite) TestQueryERC20() {
 	var contract common.Address
 	testCases := []struct {
@@ -27,7 +28,11 @@ func (suite *KeeperTestSuite) TestQueryERC20() {
 		},
 		{
 			"ok",
-			func() { contract, _ = suite.DeployContract("coin", "token", erc20Decimals) },
+			func() {
+				var err error
+				contract, err = suite.DeployContract("coin", "token", erc20Decimals)
+				suite.Require().NoError(err)
+			},
 			true,
 		},
 	}
@@ -49,6 +54,7 @@ func (suite *KeeperTestSuite) TestQueryERC20() {
 	}
 }
 
+// nolint: exhaustruct
 func (suite *KeeperTestSuite) TestBalanceOf() {
 	var mockEVMKeeper *MockEVMKeeper
 	contract := tests.GenerateAddress()
@@ -105,6 +111,7 @@ func (suite *KeeperTestSuite) TestBalanceOf() {
 	}
 }
 
+// nolint: exhaustruct
 func (suite *KeeperTestSuite) TestCallEVM() {
 	testCases := []struct {
 		name    string
@@ -140,6 +147,7 @@ func (suite *KeeperTestSuite) TestCallEVM() {
 	}
 }
 
+// nolint: exhaustruct
 func (suite *KeeperTestSuite) TestCallEVMWithData() {
 	erc20 := contracts.ERC20MinterBurnerDecimalsContract.ABI
 	testCases := []struct {
@@ -155,7 +163,8 @@ func (suite *KeeperTestSuite) TestCallEVMWithData() {
 				contract, err := suite.DeployContract("coin", "token", erc20Decimals)
 				suite.Require().NoError(err)
 				account := tests.GenerateAddress()
-				data, _ := erc20.Pack("", account)
+				data, err := erc20.Pack("", account)
+				suite.Require().NoError(err)
 				return data, &contract
 			},
 			false,
@@ -167,7 +176,8 @@ func (suite *KeeperTestSuite) TestCallEVMWithData() {
 				contract, err := suite.DeployContract("coin", "token", erc20Decimals)
 				suite.Require().NoError(err)
 				account := tests.GenerateAddress()
-				data, _ := erc20.Pack("balanceOf", account)
+				data, err := erc20.Pack("balanceOf", account)
+				suite.Require().NoError(err)
 				return data, &contract
 			},
 			true,
@@ -197,7 +207,8 @@ func (suite *KeeperTestSuite) TestCallEVMWithData() {
 			"deploy",
 			types.ModuleAddress,
 			func() ([]byte, *common.Address) {
-				ctorArgs, _ := contracts.ERC20MinterBurnerDecimalsContract.ABI.Pack("", "test", "test", uint8(18))
+				ctorArgs, err := contracts.ERC20MinterBurnerDecimalsContract.ABI.Pack("", "test", "test", uint8(18))
+				suite.Require().NoError(err)
 				data := append(contracts.ERC20MinterBurnerDecimalsContract.Bin, ctorArgs...)
 				return data, nil
 			},
@@ -207,10 +218,13 @@ func (suite *KeeperTestSuite) TestCallEVMWithData() {
 			"fail deploy",
 			types.ModuleAddress,
 			func() ([]byte, *common.Address) {
+				var err error
 				params := suite.app.EvmKeeper.GetParams(suite.ctx)
 				params.EnableCreate = false
-				suite.app.EvmKeeper.SetParams(suite.ctx, params)
-				ctorArgs, _ := contracts.ERC20MinterBurnerDecimalsContract.ABI.Pack("", "test", "test", uint8(18))
+				err = suite.app.EvmKeeper.SetParams(suite.ctx, params)
+				suite.Require().NoError(err)
+				ctorArgs, err := contracts.ERC20MinterBurnerDecimalsContract.ABI.Pack("", "test", "test", uint8(18))
+				suite.Require().NoError(err)
 				data := append(contracts.ERC20MinterBurnerDecimalsContract.Bin, ctorArgs...)
 				return data, nil
 			},
@@ -235,6 +249,7 @@ func (suite *KeeperTestSuite) TestCallEVMWithData() {
 	}
 }
 
+// nolint: exhaustruct
 func (suite *KeeperTestSuite) TestForceFail() {
 	var mockEVMKeeper *MockEVMKeeper
 	erc20 := contracts.ERC20MinterBurnerDecimalsContract.ABI
@@ -286,7 +301,8 @@ func (suite *KeeperTestSuite) TestForceFail() {
 			contract, err := suite.DeployContract("coin", "token", erc20Decimals)
 			suite.Require().NoError(err)
 			account := tests.GenerateAddress()
-			data, _ := erc20.Pack("balanceOf", account)
+			data, err := erc20.Pack("balanceOf", account)
+			suite.Require().NoError(err)
 
 			res, err := suite.app.Erc20Keeper.CallEVMWithData(suite.ctx, types.ModuleAddress, &contract, data, tc.commit)
 			if tc.expPass {
@@ -299,6 +315,7 @@ func (suite *KeeperTestSuite) TestForceFail() {
 	}
 }
 
+// nolint: exhaustruct
 func (suite *KeeperTestSuite) TestQueryERC20ForceFail() {
 	var mockEVMKeeper *MockEVMKeeper
 	contract := tests.GenerateAddress()
@@ -364,6 +381,7 @@ func (suite *KeeperTestSuite) TestQueryERC20ForceFail() {
 	}
 	for _, tc := range testCases {
 		suite.SetupTest() // reset
+		//nolint: exhaustruct
 		mockEVMKeeper = &MockEVMKeeper{}
 		sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
 		suite.Require().True(found)
diff --git a/x/erc20/keeper/grpc_query.go b/x/erc20/keeper/grpc_query.go
index 4c318be7..cba68f6f 100644
--- a/x/erc20/keeper/grpc_query.go
+++ b/x/erc20/keeper/grpc_query.go
@@ -14,6 +14,7 @@ import (
 	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
 )
 
+// nolint: exhaustruct
 var _ types.QueryServer = Keeper{}
 
 // TokenPairs returns all registered pairs
diff --git a/x/erc20/keeper/grpc_query_test.go b/x/erc20/keeper/grpc_query_test.go
index 9da65f57..052b7cda 100644
--- a/x/erc20/keeper/grpc_query_test.go
+++ b/x/erc20/keeper/grpc_query_test.go
@@ -24,7 +24,9 @@ func (suite *KeeperTestSuite) TestTokenPairs() {
 		{
 			"no pairs registered",
 			func() {
+				//nolint: exhaustruct
 				req = &types.QueryTokenPairsRequest{}
+				//nolint: exhaustruct
 				expRes = &types.QueryTokenPairsResponse{Pagination: &query.PageResponse{}}
 			},
 			true,
@@ -32,13 +34,17 @@ func (suite *KeeperTestSuite) TestTokenPairs() {
 		{
 			"1 pair registered w/pagination",
 			func() {
+				//nolint: exhaustruct
 				req = &types.QueryTokenPairsRequest{
+					//nolint: exhaustruct
 					Pagination: &query.PageRequest{Limit: 10, CountTotal: true},
 				}
 				pair := types.NewTokenPair(tests.GenerateAddress(), "coin", true, types.OWNER_MODULE)
 				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, pair)
 
+				//nolint: exhaustruct
 				expRes = &types.QueryTokenPairsResponse{
+					//nolint: exhaustruct
 					Pagination: &query.PageResponse{Total: 1},
 					TokenPairs: []types.TokenPair{pair},
 				}
@@ -48,6 +54,7 @@ func (suite *KeeperTestSuite) TestTokenPairs() {
 		{
 			"2 pairs registered wo/pagination",
 			func() {
+				//nolint: exhaustruct
 				req = &types.QueryTokenPairsRequest{}
 				pair := types.NewTokenPair(tests.GenerateAddress(), "coin", true, types.OWNER_MODULE)
 				pair2 := types.NewTokenPair(tests.GenerateAddress(), "coin2", true, types.OWNER_MODULE)
@@ -55,6 +62,7 @@ func (suite *KeeperTestSuite) TestTokenPairs() {
 				suite.app.Erc20Keeper.SetTokenPair(suite.ctx, pair2)
 
 				expRes = &types.QueryTokenPairsResponse{
+					//nolint: exhaustruct
 					Pagination: &query.PageResponse{Total: 2},
 					TokenPairs: []types.TokenPair{pair, pair2},
 				}
@@ -95,7 +103,9 @@ func (suite *KeeperTestSuite) TestTokenPair() {
 		{
 			"invalid token address",
 			func() {
+				//nolint: exhaustruct
 				req = &types.QueryTokenPairRequest{}
+				//nolint: exhaustruct
 				expRes = &types.QueryTokenPairResponse{}
 			},
 			false,
@@ -103,9 +113,11 @@ func (suite *KeeperTestSuite) TestTokenPair() {
 		{
 			"token pair not found",
 			func() {
+				//nolint: exhaustruct
 				req = &types.QueryTokenPairRequest{
 					Token: tests.GenerateAddress().Hex(),
 				}
+				//nolint: exhaustruct
 				expRes = &types.QueryTokenPairResponse{}
 			},
 			false,
diff --git a/x/erc20/keeper/integration_test.go b/x/erc20/keeper/integration_test.go
index 5b00c064..93368882 100644
--- a/x/erc20/keeper/integration_test.go
+++ b/x/erc20/keeper/integration_test.go
@@ -8,6 +8,8 @@ import (
 
 	abci "github.com/tendermint/tendermint/abci/types"
 
+	sdkmath "cosmossdk.io/math"
+
 	"github.com/cosmos/cosmos-sdk/client/tx"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	"github.com/cosmos/cosmos-sdk/types/tx/signing"
@@ -25,6 +27,8 @@ import (
 	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
 )
 
+const STAKING_TOKEN = "aalthea"
+
 var _ = Describe("Performing EVM transactions", Ordered, func() {
 	BeforeEach(func() {
 		s.SetupTest()
@@ -94,7 +98,9 @@ var _ = Describe("ERC20: Converting", Ordered, func() {
 
 	BeforeEach(func() {
 		s.SetupTest()
-		priv, _ = ethsecp256k1.GenerateKey()
+		var err error
+		priv, err = ethsecp256k1.GenerateKey()
+		s.Require().NoError(err)
 		addrBz := priv.PubKey().Address().Bytes()
 		accAddr = sdk.AccAddress(addrBz)
 		addr = common.BytesToAddress(addrBz)
@@ -108,7 +114,7 @@ var _ = Describe("ERC20: Converting", Ordered, func() {
 			coin = sdk.NewCoin(pair.Denom, amt)
 
 			// denom := s.app.ClaimsKeeper.GetParams(s.ctx).ClaimsDenom
-			denom := "aalthea"
+			denom := STAKING_TOKEN
 
 			err := testutil.FundAccount(s.app.BankKeeper, s.ctx, accAddr, sdk.NewCoins(sdk.NewCoin(denom, sdk.TokensFromConsensusPower(100, ethermint.PowerReduction))))
 			s.Require().NoError(err)
@@ -169,7 +175,7 @@ var _ = Describe("ERC20: Converting", Ordered, func() {
 			coin = sdk.NewCoin(pair.Denom, amt)
 
 			// denom := s.app.ClaimsKeeper.GetParams(s.ctx).ClaimsDenom
-			denom := "aalthea" //use default denom for claimsDenom
+			denom := STAKING_TOKEN
 
 			err := testutil.FundAccount(s.app.BankKeeper, s.ctx, accAddr, sdk.NewCoins(sdk.NewCoin(denom, sdk.NewInt(1000_000_000))))
 			s.Require().NoError(err)
@@ -234,7 +240,7 @@ func convertCoin(priv *ethsecp256k1.PrivKey, coin sdk.Coin) {
 	Expect(res.IsOK()).To(BeTrue(), "failed to convert coin: %s", res.Log)
 }
 
-func convertERC20(priv *ethsecp256k1.PrivKey, amt sdk.Int, contract common.Address) {
+func convertERC20(priv *ethsecp256k1.PrivKey, amt sdkmath.Int, contract common.Address) {
 	addrBz := priv.PubKey().Address().Bytes()
 
 	convertERC20Msg := types.NewMsgConvertERC20(amt, sdk.AccAddress(addrBz), contract, common.BytesToAddress(addrBz))
@@ -246,7 +252,7 @@ func deliverTx(priv *ethsecp256k1.PrivKey, msgs ...sdk.Msg) abci.ResponseDeliver
 	encodingConfig := encoding.MakeConfig(althea.ModuleBasics)
 	accountAddress := sdk.AccAddress(priv.PubKey().Address().Bytes())
 	// denom := s.app.ClaimsKeeper.GetParams(s.ctx).ClaimsDenom
-	denom := "aalthea"
+	denom := STAKING_TOKEN
 
 	txBuilder := encodingConfig.TxConfig.NewTxBuilder()
 
@@ -276,6 +282,7 @@ func deliverTx(priv *ethsecp256k1.PrivKey, msgs ...sdk.Msg) abci.ResponseDeliver
 
 	// Second round: all signer infos are set, so each signer can sign.
 	accNumber := s.app.AccountKeeper.GetAccount(s.ctx, accountAddress).GetAccountNumber()
+	//nolint: exhaustruct
 	signerData := authsigning.SignerData{
 		ChainID:       s.ctx.ChainID(),
 		AccountNumber: accNumber,
diff --git a/x/erc20/keeper/keeper_test.go b/x/erc20/keeper/keeper_test.go
index 61f2ecbc..15bc3297 100644
--- a/x/erc20/keeper/keeper_test.go
+++ b/x/erc20/keeper/keeper_test.go
@@ -117,12 +117,14 @@ func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) {
 		return gs
 	})
 
+	//nolint: exhaustruct
 	suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{
 		Height:          1,
 		ChainID:         "althea_7357-1",
 		Time:            time.Now().UTC(),
 		ProposerAddress: althea.ValidatorPubKey.Address().Bytes(),
 
+		//nolint: exhaustruct
 		Version: tmversion.Consensus{
 			Block: version.BlockProtocol,
 		},
@@ -151,6 +153,7 @@ func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) {
 	suite.queryClient = types.NewQueryClient(queryHelper)
 
 	encodingConfig := encoding.MakeConfig(app.ModuleBasics)
+	//nolint: exhaustruct
 	suite.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig)
 	suite.ethSigner = ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
 }
@@ -171,6 +174,7 @@ func (suite *KeeperTestSuite) MintFeeBurner(coins sdk.Coins) {
 }
 
 // DeployContract deploys the ERC20MinterBurnerDecimalsContract.
+// nolint: dupl
 func (suite *KeeperTestSuite) DeployContract(name, symbol string, decimals uint8) (common.Address, error) {
 	ctx := sdk.WrapSDKContext(suite.ctx)
 	chainID := suite.app.EvmKeeper.ChainID()
@@ -181,6 +185,7 @@ func (suite *KeeperTestSuite) DeployContract(name, symbol string, decimals uint8
 	}
 
 	data := append(contracts.ERC20MinterBurnerDecimalsContract.Bin, ctorArgs...)
+	//nolint: exhaustruct
 	args, err := json.Marshal(&evm.TransactionArgs{
 		From: &suite.address,
 		Data: (*hexutil.Bytes)(&data),
@@ -189,6 +194,7 @@ func (suite *KeeperTestSuite) DeployContract(name, symbol string, decimals uint8
 		return common.Address{}, err
 	}
 
+	//nolint: exhaustruct
 	res, err := suite.queryClientEvm.EstimateGas(ctx, &evm.EthCallRequest{
 		Args:   args,
 		GasCap: uint64(config.DefaultGasCap),
@@ -226,6 +232,7 @@ func (suite *KeeperTestSuite) DeployContract(name, symbol string, decimals uint8
 	return crypto.CreateAddress(suite.address, nonce), nil
 }
 
+// nolint: dupl
 func (suite *KeeperTestSuite) DeployContractMaliciousDelayed(name string, symbol string) common.Address {
 	ctx := sdk.WrapSDKContext(suite.ctx)
 	chainID := suite.app.EvmKeeper.ChainID()
@@ -234,12 +241,14 @@ func (suite *KeeperTestSuite) DeployContractMaliciousDelayed(name string, symbol
 	suite.Require().NoError(err)
 
 	data := append(contracts.ERC20MaliciousDelayedContract.Bin, ctorArgs...)
+	//nolint: exhaustruct
 	args, err := json.Marshal(&evm.TransactionArgs{
 		From: &suite.address,
 		Data: (*hexutil.Bytes)(&data),
 	})
 	suite.Require().NoError(err)
 
+	//nolint: exhaustruct
 	res, err := suite.queryClientEvm.EstimateGas(ctx, &evm.EthCallRequest{
 		Args:   args,
 		GasCap: uint64(config.DefaultGasCap),
@@ -277,12 +286,14 @@ func (suite *KeeperTestSuite) DeployContractDirectBalanceManipulation(name strin
 	suite.Require().NoError(err)
 
 	data := append(contracts.ERC20DirectBalanceManipulationContract.Bin, ctorArgs...)
+	//nolint: exhaustruct
 	args, err := json.Marshal(&evm.TransactionArgs{
 		From: &suite.address,
 		Data: (*hexutil.Bytes)(&data),
 	})
 	suite.Require().NoError(err)
 
+	//nolint: exhaustruct
 	res, err := suite.queryClientEvm.EstimateGas(ctx, &evm.EthCallRequest{
 		Args:   args,
 		GasCap: uint64(config.DefaultGasCap),
@@ -316,6 +327,7 @@ func (suite *KeeperTestSuite) Commit() {
 	_ = suite.app.Commit()
 	header := suite.ctx.BlockHeader()
 	header.Height += 1
+	//nolint: exhaustruct
 	suite.app.BeginBlock(abci.RequestBeginBlock{
 		Header: header,
 	})
@@ -356,8 +368,10 @@ func (suite *KeeperTestSuite) sendTx(contractAddr, from common.Address, transfer
 	ctx := sdk.WrapSDKContext(suite.ctx)
 	chainID := suite.app.EvmKeeper.ChainID()
 
+	//nolint: exhaustruct
 	args, err := json.Marshal(&evm.TransactionArgs{To: &contractAddr, From: &from, Data: (*hexutil.Bytes)(&transferData)})
 	suite.Require().NoError(err)
+	//nolint: exhaustruct
 	res, err := suite.queryClientEvm.EstimateGas(ctx, &evm.EthCallRequest{
 		Args:   args,
 		GasCap: uint64(config.DefaultGasCap),
@@ -400,6 +414,9 @@ func (suite *KeeperTestSuite) BalanceOf(contract, account common.Address) interf
 	}
 
 	unpacked, err := erc20.Unpack("balanceOf", res.Ret)
+	if err != nil {
+		return nil
+	}
 	if len(unpacked) == 0 {
 		return nil
 	}
@@ -427,6 +444,7 @@ func (suite *KeeperTestSuite) TransferERC20Token(contractAddr, from, to common.A
 	return suite.sendTx(contractAddr, from, transferData)
 }
 
+// nolint: exhaustruct
 var _ types.EVMKeeper = &MockEVMKeeper{}
 
 type MockEVMKeeper struct {
@@ -463,6 +481,7 @@ func (m *MockEVMKeeper) ApplyMessage(ctx sdk.Context, msg core.Message, tracer v
 	return args.Get(0).(*evmtypes.MsgEthereumTxResponse), args.Error(1)
 }
 
+// nolint: exhaustruct
 var _ types.BankKeeper = &MockBankKeeper{}
 
 type MockBankKeeper struct {
diff --git a/x/erc20/keeper/migrations.go b/x/erc20/keeper/migrations.go
index a0185291..f877172e 100644
--- a/x/erc20/keeper/migrations.go
+++ b/x/erc20/keeper/migrations.go
@@ -7,6 +7,7 @@ import (
 	v2 "github.com/AltheaFoundation/althea-L1/x/erc20/migrations/v2"
 )
 
+// nolint: exhaustruct
 var _ module.MigrationHandler = Migrator{}.Migrate1to2
 
 // Migrator is a struct for handling in-place store migrations.
diff --git a/x/erc20/keeper/mint.go b/x/erc20/keeper/mint.go
index 72d07f96..1980d90b 100644
--- a/x/erc20/keeper/mint.go
+++ b/x/erc20/keeper/mint.go
@@ -1,6 +1,8 @@
 package keeper
 
 import (
+	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"
@@ -20,44 +22,45 @@ func (k Keeper) MintingEnabled(
 ) (types.TokenPair, error) {
 	params := k.GetParams(ctx)
 	if !params.EnableErc20 {
-		return types.TokenPair{}, sdkerrors.Wrap(
+		return types.TokenPair{}, errorsmod.Wrap(
 			types.ErrERC20Disabled, "module is currently disabled by governance",
 		)
 	}
 
 	id := k.GetTokenPairID(ctx, token)
 	if len(id) == 0 {
-		return types.TokenPair{}, sdkerrors.Wrapf(
+		return types.TokenPair{}, errorsmod.Wrapf(
 			types.ErrTokenPairNotFound, "token '%s' not registered by id", token,
 		)
 	}
 
 	pair, found := k.GetTokenPair(ctx, id)
 	if !found {
-		return types.TokenPair{}, sdkerrors.Wrapf(
+		return types.TokenPair{}, errorsmod.Wrapf(
 			types.ErrTokenPairNotFound, "token '%s' not registered", token,
 		)
 	}
 
 	if !pair.Enabled {
-		return types.TokenPair{}, sdkerrors.Wrapf(
+		return types.TokenPair{}, errorsmod.Wrapf(
 			types.ErrERC20TokenPairDisabled, "minting token '%s' is not enabled by governance", token,
 		)
 	}
 
 	if k.bankKeeper.BlockedAddr(receiver.Bytes()) {
-		return types.TokenPair{}, sdkerrors.Wrapf(
+		return types.TokenPair{}, errorsmod.Wrapf(
 			sdkerrors.ErrUnauthorized, "%s is not allowed to receive transactions", receiver,
 		)
 	}
 
 	// NOTE: ignore amount as only denom is checked on IsSendEnabledCoin
+	//nolint: exhaustruct
 	coin := sdk.Coin{Denom: pair.Denom}
 
 	// check if minting to a recipient address other than the sender is enabled
 	// for for the given coin denom
 	if !sender.Equals(receiver) && !k.bankKeeper.IsSendEnabledCoin(ctx, coin) {
-		return types.TokenPair{}, sdkerrors.Wrapf(
+		return types.TokenPair{}, errorsmod.Wrapf(
 			banktypes.ErrSendDisabled, "minting '%s' coins to an external address is currently disabled", token,
 		)
 	}
diff --git a/x/erc20/keeper/msg_server.go b/x/erc20/keeper/msg_server.go
index 2ad0be99..c0429cda 100644
--- a/x/erc20/keeper/msg_server.go
+++ b/x/erc20/keeper/msg_server.go
@@ -5,6 +5,9 @@ import (
 	"math/big"
 
 	"github.com/armon/go-metrics"
+
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/cosmos/cosmos-sdk/telemetry"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@@ -15,6 +18,7 @@ import (
 	"github.com/AltheaFoundation/althea-L1/x/erc20/types"
 )
 
+// nolint: exhaustruct
 var _ types.MsgServer = &Keeper{}
 
 // ConvertCoin converts native Cosmos coins into ERC20 tokens for both
@@ -119,13 +123,13 @@ func (k Keeper) convertCoinNativeCoin(
 	contract := pair.GetERC20Contract()
 	balanceToken := k.BalanceOf(ctx, erc20, contract, receiver)
 	if balanceToken == nil {
-		return nil, sdkerrors.Wrap(types.ErrEVMCall, "failed to retrieve balance")
+		return nil, errorsmod.Wrap(types.ErrEVMCall, "failed to retrieve balance")
 	}
 
 	// Escrow coins on module account
 	err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, coins)
 	if err != nil {
-		return nil, sdkerrors.Wrap(err, "failed to escrow coins")
+		return nil, errorsmod.Wrap(err, "failed to escrow coins")
 	}
 
 	// Mint tokens and send to receiver
@@ -138,12 +142,12 @@ func (k Keeper) convertCoinNativeCoin(
 	tokens := msg.Coin.Amount.BigInt()
 	balanceTokenAfter := k.BalanceOf(ctx, erc20, contract, receiver)
 	if balanceTokenAfter == nil {
-		return nil, sdkerrors.Wrap(types.ErrEVMCall, "failed to retrieve balance")
+		return nil, errorsmod.Wrap(types.ErrEVMCall, "failed to retrieve balance")
 	}
 	expToken := big.NewInt(0).Add(balanceToken, tokens)
 
 	if r := balanceTokenAfter.Cmp(expToken); r != 0 {
-		return nil, sdkerrors.Wrapf(
+		return nil, errorsmod.Wrapf(
 			types.ErrBalanceInvariance,
 			"invalid token balance - expected: %v, actual: %v", expToken, balanceTokenAfter,
 		)
@@ -207,7 +211,7 @@ func (k Keeper) convertERC20NativeCoin(
 	balanceCoin := k.bankKeeper.GetBalance(ctx, receiver, pair.Denom)
 	balanceToken := k.BalanceOf(ctx, erc20, contract, sender)
 	if balanceToken == nil {
-		return nil, sdkerrors.Wrap(types.ErrEVMCall, "failed to retrieve balance")
+		return nil, errorsmod.Wrap(types.ErrEVMCall, "failed to retrieve balance")
 	}
 
 	// Burn escrowed tokens
@@ -226,7 +230,7 @@ func (k Keeper) convertERC20NativeCoin(
 	balanceCoinAfter := k.bankKeeper.GetBalance(ctx, receiver, pair.Denom)
 	expCoin := balanceCoin.Add(coins[0])
 	if ok := balanceCoinAfter.IsEqual(expCoin); !ok {
-		return nil, sdkerrors.Wrapf(
+		return nil, errorsmod.Wrapf(
 			types.ErrBalanceInvariance,
 			"invalid coin balance - expected: %v, actual: %v",
 			expCoin, balanceCoinAfter,
@@ -237,12 +241,12 @@ func (k Keeper) convertERC20NativeCoin(
 	tokens := coins[0].Amount.BigInt()
 	balanceTokenAfter := k.BalanceOf(ctx, erc20, contract, sender)
 	if balanceTokenAfter == nil {
-		return nil, sdkerrors.Wrap(types.ErrEVMCall, "failed to retrieve balance")
+		return nil, errorsmod.Wrap(types.ErrEVMCall, "failed to retrieve balance")
 	}
 
 	expToken := big.NewInt(0).Sub(balanceToken, tokens)
 	if r := balanceTokenAfter.Cmp(expToken); r != 0 {
-		return nil, sdkerrors.Wrapf(
+		return nil, errorsmod.Wrapf(
 			types.ErrBalanceInvariance,
 			"invalid token balance - expected: %v, actual: %v",
 			expToken, balanceTokenAfter,
@@ -309,7 +313,7 @@ func (k Keeper) convertERC20NativeToken(
 	balanceCoin := k.bankKeeper.GetBalance(ctx, receiver, pair.Denom)
 	balanceToken := k.BalanceOf(ctx, erc20, contract, types.ModuleAddress)
 	if balanceToken == nil {
-		return nil, sdkerrors.Wrap(types.ErrEVMCall, "failed to retrieve balance")
+		return nil, errorsmod.Wrap(types.ErrEVMCall, "failed to retrieve balance")
 	}
 
 	// Escrow tokens on module account
@@ -330,20 +334,20 @@ func (k Keeper) convertERC20NativeToken(
 	}
 
 	if !unpackedRet.Value {
-		return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "failed to execute transfer")
+		return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "failed to execute transfer")
 	}
 
 	// Check expected escrow balance after transfer execution
 	tokens := coins[0].Amount.BigInt()
 	balanceTokenAfter := k.BalanceOf(ctx, erc20, contract, types.ModuleAddress)
 	if balanceTokenAfter == nil {
-		return nil, sdkerrors.Wrap(types.ErrEVMCall, "failed to retrieve balance")
+		return nil, errorsmod.Wrap(types.ErrEVMCall, "failed to retrieve balance")
 	}
 
 	expToken := big.NewInt(0).Add(balanceToken, tokens)
 
 	if r := balanceTokenAfter.Cmp(expToken); r != 0 {
-		return nil, sdkerrors.Wrapf(
+		return nil, errorsmod.Wrapf(
 			types.ErrBalanceInvariance,
 			"invalid token balance - expected: %v, actual: %v",
 			expToken, balanceTokenAfter,
@@ -365,7 +369,7 @@ func (k Keeper) convertERC20NativeToken(
 	expCoin := balanceCoin.Add(coins[0])
 
 	if ok := balanceCoinAfter.IsEqual(expCoin); !ok {
-		return nil, sdkerrors.Wrapf(
+		return nil, errorsmod.Wrapf(
 			types.ErrBalanceInvariance,
 			"invalid coin balance - expected: %v, actual: %v",
 			expCoin, balanceCoinAfter,
@@ -436,12 +440,12 @@ func (k Keeper) convertCoinNativeERC20(
 	contract := pair.GetERC20Contract()
 	balanceToken := k.BalanceOf(ctx, erc20, contract, receiver)
 	if balanceToken == nil {
-		return nil, sdkerrors.Wrap(types.ErrEVMCall, "failed to retrieve balance")
+		return nil, errorsmod.Wrap(types.ErrEVMCall, "failed to retrieve balance")
 	}
 
 	// Escrow Coins on module account
 	if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, coins); err != nil {
-		return nil, sdkerrors.Wrap(err, "failed to escrow coins")
+		return nil, errorsmod.Wrap(err, "failed to escrow coins")
 	}
 
 	// Unescrow Tokens and send to receiver
@@ -457,20 +461,20 @@ func (k Keeper) convertCoinNativeERC20(
 	}
 
 	if !unpackedRet.Value {
-		return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "failed to execute unescrow tokens from user")
+		return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "failed to execute unescrow tokens from user")
 	}
 
 	// Check expected Receiver balance after transfer execution
 	tokens := msg.Coin.Amount.BigInt()
 	balanceTokenAfter := k.BalanceOf(ctx, erc20, contract, receiver)
 	if balanceTokenAfter == nil {
-		return nil, sdkerrors.Wrap(types.ErrEVMCall, "failed to retrieve balance")
+		return nil, errorsmod.Wrap(types.ErrEVMCall, "failed to retrieve balance")
 	}
 
 	exp := big.NewInt(0).Add(balanceToken, tokens)
 
 	if r := balanceTokenAfter.Cmp(exp); r != 0 {
-		return nil, sdkerrors.Wrapf(
+		return nil, errorsmod.Wrapf(
 			types.ErrBalanceInvariance,
 			"invalid token balance - expected: %v, actual: %v", exp, balanceTokenAfter,
 		)
@@ -479,7 +483,7 @@ func (k Keeper) convertCoinNativeERC20(
 	// Burn escrowed Coins
 	err = k.bankKeeper.BurnCoins(ctx, types.ModuleName, coins)
 	if err != nil {
-		return nil, sdkerrors.Wrap(err, "failed to burn coins")
+		return nil, errorsmod.Wrap(err, "failed to burn coins")
 	}
 
 	// Check for unexpected `Approval` event in logs
diff --git a/x/erc20/keeper/msg_server_test.go b/x/erc20/keeper/msg_server_test.go
index 47d1c2c2..7459abb4 100644
--- a/x/erc20/keeper/msg_server_test.go
+++ b/x/erc20/keeper/msg_server_test.go
@@ -15,6 +15,7 @@ import (
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
 )
 
+// nolint: dupl
 func (suite *KeeperTestSuite) TestConvertCoinNativeCoin() {
 	testCases := []struct {
 		name           string
@@ -89,15 +90,18 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeCoin() {
 		{
 			"fail - force evm fail", 100, 10, func(common.Address) {},
 			func() {
+				//nolint: exhaustruct
 				mockEVMKeeper := &MockEVMKeeper{}
 				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
 				suite.Require().True(found)
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				//nolint: exhaustruct
 				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
 				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("forced ApplyMessage error"))
 				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
@@ -106,22 +110,28 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeCoin() {
 		{
 			"fail - force evm balance error", 100, 10, func(common.Address) {},
 			func() {
+				//nolint: exhaustruct
 				mockEVMKeeper := &MockEVMKeeper{}
 				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
 				suite.Require().True(found)
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
 				// first balance of
+				//nolint: exhaustruct
 				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Once()
 				// convert coin
+				//nolint: exhaustruct
 				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, nil).Once()
 				// second balance of
+				//nolint: exhaustruct
 				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, fmt.Errorf("third")).Once()
 				// Extra call on test
+				//nolint: exhaustruct
 				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{}, nil)
 				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
 			}, false, false,
@@ -135,9 +145,11 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeCoin() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
+				//nolint: exhaustruct
 				mockEVMKeeper.On("ApplyMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&evmtypes.MsgEthereumTxResponse{Ret: balance}, nil).Times(4)
 				mockEVMKeeper.On("GetAccountWithoutBalance", mock.Anything, mock.Anything).Return(existingAcc, nil)
 			}, false, false,
@@ -145,6 +157,7 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeCoin() {
 	}
 	for _, tc := range testCases {
 		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			var err error
 			suite.mintFeeCollector = true
 			suite.SetupTest()
 			metadata, pair := suite.setupRegisterCoin()
@@ -162,8 +175,10 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeCoin() {
 				sender,
 			)
 
-			suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
-			suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			err = suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
+			suite.Require().NoError(err)
+			err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			suite.Require().NoError(err)
 
 			tc.extra()
 			res, err := suite.app.Erc20Keeper.ConvertCoin(ctx, msg)
@@ -199,6 +214,7 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeCoin() {
 	suite.mintFeeCollector = false
 }
 
+// nolint: dupl
 func (suite *KeeperTestSuite) TestConvertERC20NativeCoin() {
 	testCases := []struct {
 		name      string
@@ -229,6 +245,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeCoin() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
@@ -247,6 +264,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeCoin() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
@@ -271,6 +289,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeCoin() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
@@ -289,6 +308,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeCoin() {
 		{
 			"fail - force fail unescrow", 100, 10, 5,
 			func() {
+				//nolint: exhaustruct
 				mockBankKeeper := &MockBankKeeper{}
 				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
 				suite.Require().True(found)
@@ -304,6 +324,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeCoin() {
 		{
 			"fail - force fail balance after transfer", 100, 10, 5,
 			func() {
+				//nolint: exhaustruct
 				mockBankKeeper := &MockBankKeeper{}
 				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
 				suite.Require().True(found)
@@ -319,6 +340,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeCoin() {
 	}
 	for _, tc := range testCases {
 		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
+			var err error
 			suite.mintFeeCollector = true
 			suite.SetupTest()
 			metadata, pair := suite.setupRegisterCoin()
@@ -328,8 +350,10 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeCoin() {
 			// Precondition: Convert Coin to ERC20
 			coins := sdk.NewCoins(sdk.NewCoin(cosmosTokenBase, sdk.NewInt(tc.mint)))
 			sender := sdk.AccAddress(suite.address.Bytes())
-			suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
-			suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			err = suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
+			suite.Require().NoError(err)
+			err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			suite.Require().NoError(err)
 			msg := types.NewMsgConvertCoin(
 				sdk.NewCoin(cosmosTokenBase, sdk.NewInt(tc.burn)),
 				suite.address,
@@ -337,7 +361,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeCoin() {
 			)
 
 			ctx := sdk.WrapSDKContext(suite.ctx)
-			_, err := suite.app.Erc20Keeper.ConvertCoin(ctx, msg)
+			_, err = suite.app.Erc20Keeper.ConvertCoin(ctx, msg)
 			suite.Require().NoError(err, tc.name)
 			suite.Commit()
 			balance := suite.BalanceOf(common.HexToAddress(pair.Erc20Address), suite.address)
@@ -374,6 +398,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeCoin() {
 	suite.mintFeeCollector = false
 }
 
+// nolint: dupl
 func (suite *KeeperTestSuite) TestConvertERC20NativeERC20() {
 	var contractAddr common.Address
 	var coinName string
@@ -513,6 +538,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeERC20() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
@@ -536,6 +562,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeERC20() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				balance[31] = uint8(1)
@@ -560,6 +587,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeERC20() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
@@ -584,6 +612,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeERC20() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
@@ -601,6 +630,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeERC20() {
 			10,
 			func(common.Address) {},
 			func() {
+				//nolint: exhaustruct
 				mockBankKeeper := &MockBankKeeper{}
 				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
 				suite.Require().True(found)
@@ -622,6 +652,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeERC20() {
 			10,
 			func(common.Address) {},
 			func() {
+				//nolint: exhaustruct
 				mockBankKeeper := &MockBankKeeper{}
 				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
 				suite.Require().True(found)
@@ -643,6 +674,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeERC20() {
 			10,
 			func(common.Address) {},
 			func() {
+				//nolint: exhaustruct
 				mockBankKeeper := &MockBankKeeper{}
 				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
 				suite.Require().True(found)
@@ -717,6 +749,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeERC20() {
 	suite.mintFeeCollector = false
 }
 
+// nolint: dupl
 func (suite *KeeperTestSuite) TestConvertCoinNativeERC20() {
 	var contractAddr common.Address
 
@@ -798,6 +831,7 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeERC20() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
@@ -820,6 +854,7 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeERC20() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
@@ -842,6 +877,7 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeERC20() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				balance[31] = uint8(1)
@@ -865,6 +901,7 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeERC20() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
@@ -889,8 +926,11 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeERC20() {
 			sender := sdk.AccAddress(suite.address.Bytes())
 
 			// Precondition: Mint Coins to convert on sender account
-			suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
-			suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			err := suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
+			suite.Require().NoError(err)
+			err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			suite.Require().NoError(err)
+
 			cosmosBalance := suite.app.BankKeeper.GetBalance(suite.ctx, sender, coinName)
 			suite.Require().Equal(sdk.NewInt(tc.mint), cosmosBalance.Amount)
 
@@ -932,6 +972,7 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeERC20() {
 	suite.mintFeeCollector = false
 }
 
+// nolint: dupl
 func (suite *KeeperTestSuite) TestWrongPairOwnerERC20NativeCoin() {
 	testCases := []struct {
 		name      string
@@ -953,8 +994,11 @@ func (suite *KeeperTestSuite) TestWrongPairOwnerERC20NativeCoin() {
 			// Precondition: Convert Coin to ERC20
 			coins := sdk.NewCoins(sdk.NewCoin(cosmosTokenBase, sdk.NewInt(tc.mint)))
 			sender := sdk.AccAddress(suite.address.Bytes())
-			suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
-			suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			var err error
+			err = suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
+			suite.Require().NoError(err)
+			err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			suite.Require().NoError(err)
 			msg := types.NewMsgConvertCoin(
 				sdk.NewCoin(cosmosTokenBase, sdk.NewInt(tc.burn)),
 				suite.address,
@@ -965,7 +1009,7 @@ func (suite *KeeperTestSuite) TestWrongPairOwnerERC20NativeCoin() {
 			suite.app.Erc20Keeper.SetTokenPair(suite.ctx, *pair)
 
 			ctx := sdk.WrapSDKContext(suite.ctx)
-			_, err := suite.app.Erc20Keeper.ConvertCoin(ctx, msg)
+			_, err = suite.app.Erc20Keeper.ConvertCoin(ctx, msg)
 			suite.Require().Error(err, tc.name)
 
 			// Convert ERC20s back to Coins
@@ -984,6 +1028,7 @@ func (suite *KeeperTestSuite) TestWrongPairOwnerERC20NativeCoin() {
 	}
 }
 
+// nolint: dupl
 func (suite *KeeperTestSuite) TestConvertCoinNativeIBCVoucher() {
 	testCases := []struct {
 		name           string
@@ -1064,6 +1109,7 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeIBCVoucher() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
@@ -1081,6 +1127,7 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeIBCVoucher() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
@@ -1104,6 +1151,7 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeIBCVoucher() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
@@ -1131,8 +1179,11 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeIBCVoucher() {
 				sender,
 			)
 
-			suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
-			suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			var err error
+			err = suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
+			suite.Require().NoError(err)
+			err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			suite.Require().NoError(err)
 
 			tc.extra()
 			res, err := suite.app.Erc20Keeper.ConvertCoin(ctx, msg)
@@ -1168,6 +1219,7 @@ func (suite *KeeperTestSuite) TestConvertCoinNativeIBCVoucher() {
 	suite.mintFeeCollector = false
 }
 
+// nolint: dupl
 func (suite *KeeperTestSuite) TestConvertERC20NativeIBCVoucher() {
 	testCases := []struct {
 		name      string
@@ -1198,6 +1250,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeIBCVoucher() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
@@ -1216,6 +1269,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeIBCVoucher() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
@@ -1240,6 +1294,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeIBCVoucher() {
 				erc20Keeper := keeper.NewKeeper(suite.app.GetKey("erc20"), suite.app.AppCodec(), sp, suite.app.AccountKeeper, suite.app.BankKeeper, mockEVMKeeper)
 				suite.app.Erc20Keeper = &erc20Keeper
 
+				//nolint: exhaustruct
 				existingAcc := &statedb.Account{Nonce: uint64(1), Balance: common.Big1}
 				balance := make([]uint8, 32)
 				mockEVMKeeper.On("EstimateGas", mock.Anything, mock.Anything).Return(&evmtypes.EstimateGasResponse{Gas: uint64(200)}, nil)
@@ -1258,6 +1313,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeIBCVoucher() {
 		{
 			"fail - force fail unescrow", 100, 10, 5,
 			func() {
+				//nolint: exhaustruct
 				mockBankKeeper := &MockBankKeeper{}
 				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
 				suite.Require().True(found)
@@ -1273,6 +1329,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeIBCVoucher() {
 		{
 			"fail - force fail balance after transfer", 100, 10, 5,
 			func() {
+				//nolint: exhaustruct
 				mockBankKeeper := &MockBankKeeper{}
 				sp, found := suite.app.ParamsKeeper.GetSubspace(types.ModuleName)
 				suite.Require().True(found)
@@ -1297,8 +1354,10 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeIBCVoucher() {
 			// Precondition: Convert Coin to ERC20
 			coins := sdk.NewCoins(sdk.NewCoin(ibcBase, sdk.NewInt(tc.mint)))
 			sender := sdk.AccAddress(suite.address.Bytes())
-			suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
-			suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			var err error
+			err = suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, coins)
+			err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, sender, coins)
+			suite.Require().NoError(err)
 			msg := types.NewMsgConvertCoin(
 				sdk.NewCoin(ibcBase, sdk.NewInt(tc.burn)),
 				suite.address,
@@ -1306,7 +1365,7 @@ func (suite *KeeperTestSuite) TestConvertERC20NativeIBCVoucher() {
 			)
 
 			ctx := sdk.WrapSDKContext(suite.ctx)
-			_, err := suite.app.Erc20Keeper.ConvertCoin(ctx, msg)
+			_, err = suite.app.Erc20Keeper.ConvertCoin(ctx, msg)
 			suite.Require().NoError(err, tc.name)
 			suite.Commit()
 			balance := suite.BalanceOf(common.HexToAddress(pair.Erc20Address), suite.address)
diff --git a/x/erc20/keeper/proposals.go b/x/erc20/keeper/proposals.go
index dbbee327..d5c27234 100644
--- a/x/erc20/keeper/proposals.go
+++ b/x/erc20/keeper/proposals.go
@@ -3,6 +3,8 @@ package keeper
 import (
 	"strings"
 
+	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"
@@ -20,41 +22,41 @@ func (k Keeper) RegisterCoin(
 	// Check if the conversion is globally enabled
 	params := k.GetParams(ctx)
 	if !params.EnableErc20 {
-		return nil, sdkerrors.Wrap(
+		return nil, errorsmod.Wrap(
 			types.ErrERC20Disabled, "registration is currently disabled by governance",
 		)
 	}
 
 	// Prohibit denominations that contain the evm denom
 	if strings.Contains(coinMetadata.Base, "CANTO") {
-		return nil, sdkerrors.Wrapf(
+		return nil, errorsmod.Wrapf(
 			types.ErrEVMDenom, "cannot register the EVM denomination %s", coinMetadata.Base,
 		)
 	}
 
 	// Check if denomination is already registered
 	if k.IsDenomRegistered(ctx, coinMetadata.Name) {
-		return nil, sdkerrors.Wrapf(
+		return nil, errorsmod.Wrapf(
 			types.ErrTokenPairAlreadyExists, "coin denomination already registered: %s", coinMetadata.Name,
 		)
 	}
 
 	// Check if the coin exists by ensuring the supply is set
 	if !k.bankKeeper.HasSupply(ctx, coinMetadata.Base) {
-		return nil, sdkerrors.Wrapf(
+		return nil, errorsmod.Wrapf(
 			sdkerrors.ErrInvalidCoins, "base denomination '%s' cannot have a supply of 0", coinMetadata.Base,
 		)
 	}
 
 	if err := k.verifyMetadata(ctx, coinMetadata); err != nil {
-		return nil, sdkerrors.Wrapf(
+		return nil, errorsmod.Wrapf(
 			types.ErrInternalTokenPair, "coin metadata is invalid %s", coinMetadata.Name,
 		)
 	}
 
 	addr, err := k.DeployERC20Contract(ctx, coinMetadata)
 	if err != nil {
-		return nil, sdkerrors.Wrap(
+		return nil, errorsmod.Wrap(
 			err, "failed to create wrapped coin denom metadata for ERC20",
 		)
 	}
@@ -76,21 +78,21 @@ func (k Keeper) RegisterERC20(
 	// Check if the conversion is globally enabled
 	params := k.GetParams(ctx)
 	if !params.EnableErc20 {
-		return nil, sdkerrors.Wrap(
+		return nil, errorsmod.Wrap(
 			types.ErrERC20Disabled, "registration is currently disabled by governance",
 		)
 	}
 
 	// Check if ERC20 is already registered
 	if k.IsERC20Registered(ctx, contract) {
-		return nil, sdkerrors.Wrapf(
+		return nil, errorsmod.Wrapf(
 			types.ErrTokenPairAlreadyExists, "token ERC20 contract already registered: %s", contract.String(),
 		)
 	}
 
 	metadata, err := k.CreateCoinMetadata(ctx, contract)
 	if err != nil {
-		return nil, sdkerrors.Wrap(
+		return nil, errorsmod.Wrap(
 			err, "failed to create wrapped coin denom metadata for ERC20",
 		)
 	}
@@ -118,13 +120,13 @@ func (k Keeper) CreateCoinMetadata(
 	// Check if metadata already exists
 	_, found := k.bankKeeper.GetDenomMetaData(ctx, types.CreateDenom(strContract))
 	if found {
-		return nil, sdkerrors.Wrap(
+		return nil, errorsmod.Wrap(
 			types.ErrInternalTokenPair, "denom metadata already registered",
 		)
 	}
 
 	if k.IsDenomRegistered(ctx, types.CreateDenom(strContract)) {
-		return nil, sdkerrors.Wrapf(
+		return nil, errorsmod.Wrapf(
 			types.ErrInternalTokenPair, "coin denomination already registered: %s", erc20Data.Name,
 		)
 	}
@@ -135,6 +137,7 @@ func (k Keeper) CreateCoinMetadata(
 	// create a bank denom metadata based on the ERC20 token ABI details
 	// metadata name is should always be the contract since it's the key
 	// to the bank store
+	//nolint: exhaustruct
 	metadata := banktypes.Metadata{
 		Description: types.CreateDenomDescription(strContract),
 		Base:        base,
@@ -155,6 +158,7 @@ func (k Keeper) CreateCoinMetadata(
 		nameSanitized := types.SanitizeERC20Name(erc20Data.Name)
 		metadata.DenomUnits = append(
 			metadata.DenomUnits,
+			//nolint: exhaustruct
 			&banktypes.DenomUnit{
 				Denom:    nameSanitized,
 				Exponent: uint32(erc20Data.Decimals),
@@ -164,7 +168,7 @@ func (k Keeper) CreateCoinMetadata(
 	}
 
 	if err := metadata.Validate(); err != nil {
-		return nil, sdkerrors.Wrapf(
+		return nil, errorsmod.Wrapf(
 			err, "ERC20 token data is invalid for contract %s", strContract,
 		)
 	}
@@ -181,14 +185,14 @@ func (k Keeper) ToggleConversion(
 ) (types.TokenPair, error) {
 	id := k.GetTokenPairID(ctx, token)
 	if len(id) == 0 {
-		return types.TokenPair{}, sdkerrors.Wrapf(
+		return types.TokenPair{}, errorsmod.Wrapf(
 			types.ErrTokenPairNotFound, "token '%s' not registered by id", token,
 		)
 	}
 
 	pair, found := k.GetTokenPair(ctx, id)
 	if !found {
-		return types.TokenPair{}, sdkerrors.Wrapf(
+		return types.TokenPair{}, errorsmod.Wrapf(
 			types.ErrTokenPairNotFound, "token '%s' not registered", token,
 		)
 	}
diff --git a/x/erc20/keeper/proposals_test.go b/x/erc20/keeper/proposals_test.go
index 7910fe8c..81695188 100644
--- a/x/erc20/keeper/proposals_test.go
+++ b/x/erc20/keeper/proposals_test.go
@@ -44,7 +44,9 @@ func (suite *KeeperTestSuite) setupRegisterERC20Pair(contractType int) common.Ad
 	case contractMaliciousDelayed:
 		contract = suite.DeployContractMaliciousDelayed(erc20Name, erc20Symbol)
 	default:
-		contract, _ = suite.DeployContract(erc20Name, erc20Symbol, erc20Decimals)
+		var err error
+		contract, err = suite.DeployContract(erc20Name, erc20Symbol, erc20Decimals)
+		suite.Require().NoError(err)
 	}
 	suite.Commit()
 
@@ -54,6 +56,7 @@ func (suite *KeeperTestSuite) setupRegisterERC20Pair(contractType int) common.Ad
 }
 
 func (suite *KeeperTestSuite) setupRegisterCoin() (banktypes.Metadata, *types.TokenPair) {
+	//nolint: exhaustruct
 	validMetadata := banktypes.Metadata{
 		Description: "description of the token",
 		Base:        cosmosTokenBase,
@@ -86,6 +89,7 @@ func (suite *KeeperTestSuite) setupRegisterCoin() (banktypes.Metadata, *types.To
 func (suite *KeeperTestSuite) setupRegisterIBCVoucher() (banktypes.Metadata, *types.TokenPair) {
 	suite.SetupTest()
 
+	//nolint: exhaustruct
 	validMetadata := banktypes.Metadata{
 		Description: "ATOM IBC voucher (channel 14)",
 		Base:        ibcBase,
@@ -111,7 +115,8 @@ func (suite *KeeperTestSuite) setupRegisterIBCVoucher() (banktypes.Metadata, *ty
 	return validMetadata, pair
 }
 
-func (suite KeeperTestSuite) TestRegisterCoin() {
+func (suite *KeeperTestSuite) TestRegisterCoin() {
+	//nolint: exhaustruct
 	metadata := banktypes.Metadata{
 		Description: "description",
 		Base:        cosmosTokenBase,
@@ -290,7 +295,7 @@ func (suite KeeperTestSuite) TestRegisterCoin() {
 	}
 }
 
-func (suite KeeperTestSuite) TestRegisterERC20() {
+func (suite *KeeperTestSuite) TestRegisterERC20() {
 	var (
 		contractAddr common.Address
 		pair         types.TokenPair
@@ -326,7 +331,8 @@ func (suite KeeperTestSuite) TestRegisterERC20() {
 		{
 			"meta data already stored",
 			func() {
-				suite.app.Erc20Keeper.CreateCoinMetadata(suite.ctx, contractAddr)
+				_, err := suite.app.Erc20Keeper.CreateCoinMetadata(suite.ctx, contractAddr)
+				suite.Require().NoError(err)
 			},
 			false,
 		},
@@ -390,7 +396,7 @@ func (suite *KeeperTestSuite) TestRegisterIBCVoucher() {
 	suite.setupRegisterIBCVoucher()
 }
 
-func (suite KeeperTestSuite) TestToggleConverision() {
+func (suite *KeeperTestSuite) TestToggleConverision() {
 	var (
 		contractAddr common.Address
 		id           []byte
@@ -442,7 +448,9 @@ func (suite KeeperTestSuite) TestToggleConverision() {
 				contractAddr = suite.setupRegisterERC20Pair(contractMinterBurner)
 				id = suite.app.Erc20Keeper.GetTokenPairID(suite.ctx, contractAddr.String())
 				pair, _ = suite.app.Erc20Keeper.GetTokenPair(suite.ctx, id)
-				pair, _ = suite.app.Erc20Keeper.ToggleConversion(suite.ctx, contractAddr.String())
+				var err error
+				pair, err = suite.app.Erc20Keeper.ToggleConversion(suite.ctx, contractAddr.String())
+				suite.Require().NoError(err)
 			},
 			true,
 			true,
diff --git a/x/erc20/keeper/token_pairs.go b/x/erc20/keeper/token_pairs.go
index 702ca05a..7bcda4ba 100644
--- a/x/erc20/keeper/token_pairs.go
+++ b/x/erc20/keeper/token_pairs.go
@@ -10,6 +10,7 @@ import (
 
 // GetTokenPairs - get all registered token tokenPairs
 func (k Keeper) GetTokenPairs(ctx sdk.Context) []types.TokenPair {
+	//nolint: exhaustruct
 	tokenPairs := []types.TokenPair{}
 
 	store := ctx.KVStore(k.storeKey)
@@ -38,6 +39,7 @@ func (k Keeper) GetTokenPairID(ctx sdk.Context, token string) []byte {
 // GetTokenPair - get registered token pair from the identifier
 func (k Keeper) GetTokenPair(ctx sdk.Context, id []byte) (types.TokenPair, bool) {
 	if id == nil {
+		//nolint: exhaustruct
 		return types.TokenPair{}, false
 	}
 
@@ -45,6 +47,7 @@ func (k Keeper) GetTokenPair(ctx sdk.Context, id []byte) (types.TokenPair, bool)
 	var tokenPair types.TokenPair
 	bz := store.Get(id)
 	if len(bz) == 0 {
+		//nolint: exhaustruct
 		return types.TokenPair{}, false
 	}
 
diff --git a/x/erc20/module.go b/x/erc20/module.go
index 948158cb..fd2edfb7 100644
--- a/x/erc20/module.go
+++ b/x/erc20/module.go
@@ -25,8 +25,11 @@ import (
 
 // type check to ensure the interface is properly implemented
 var (
-	_ module.AppModule           = AppModule{}
-	_ module.AppModuleBasic      = AppModuleBasic{}
+	//nolint: exhaustruct
+	_ module.AppModule = AppModule{}
+	//nolint: exhaustruct
+	_ module.AppModuleBasic = AppModuleBasic{}
+	//nolint: exhaustruct
 	_ module.AppModuleSimulation = AppModule{}
 )
 
diff --git a/x/erc20/proposal_handler.go b/x/erc20/proposal_handler.go
index 1e4b15d9..a21c8243 100644
--- a/x/erc20/proposal_handler.go
+++ b/x/erc20/proposal_handler.go
@@ -1,6 +1,8 @@
 package erc20
 
 import (
+	errorsmod "cosmossdk.io/errors"
+
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
@@ -22,7 +24,7 @@ func NewErc20ProposalHandler(k *keeper.Keeper) govv1beta1.Handler {
 			return handleToggleConversionProposal(ctx, k, c)
 
 		default:
-			return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s proposal content type: %T", types.ModuleName, c)
+			return errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s proposal content type: %T", types.ModuleName, c)
 		}
 	}
 }
diff --git a/x/erc20/types/codec.go b/x/erc20/types/codec.go
index 20cec806..fac0bd42 100644
--- a/x/erc20/types/codec.go
+++ b/x/erc20/types/codec.go
@@ -38,13 +38,18 @@ func init() {
 func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
 	registry.RegisterImplementations(
 		(*sdk.Msg)(nil),
+		//nolint: exhaustruct
 		&MsgConvertCoin{},
+		//nolint: exhaustruct
 		&MsgConvertERC20{},
 	)
 	registry.RegisterImplementations(
 		(*govv1beta1.Content)(nil),
+		//nolint: exhaustruct
 		&RegisterCoinProposal{},
+		//nolint: exhaustruct
 		&RegisterERC20Proposal{},
+		//nolint: exhaustruct
 		&ToggleTokenConversionProposal{},
 	)
 
@@ -55,6 +60,8 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
 // concrete types on the provided LegacyAmino codec. These types are used for
 // Amino JSON serialization and EIP-712 compatibility.
 func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
+	//nolint: exhaustruct
 	cdc.RegisterConcrete(&MsgConvertERC20{}, convertERC20Name, nil)
+	//nolint: exhaustruct
 	cdc.RegisterConcrete(&MsgConvertCoin{}, convertCoinName, nil)
 }
diff --git a/x/erc20/types/genesis.go b/x/erc20/types/genesis.go
index dab7227c..43fcf2da 100644
--- a/x/erc20/types/genesis.go
+++ b/x/erc20/types/genesis.go
@@ -4,6 +4,7 @@ import "fmt"
 
 // NewGenesisState creates a new genesis state.
 func NewGenesisState(params Params, pairs []TokenPair) GenesisState {
+	//nolint: exhaustruct
 	return GenesisState{
 		Params:     params,
 		TokenPairs: pairs,
@@ -13,6 +14,7 @@ func NewGenesisState(params Params, pairs []TokenPair) GenesisState {
 // DefaultGenesisState sets default evm genesis state with empty accounts and
 // default params and chain config values.
 func DefaultGenesisState() *GenesisState {
+	//nolint: exhaustruct
 	return &GenesisState{
 		Params: DefaultParams(),
 	}
diff --git a/x/erc20/types/genesis_test.go b/x/erc20/types/genesis_test.go
index a3997232..d6e56136 100644
--- a/x/erc20/types/genesis_test.go
+++ b/x/erc20/types/genesis_test.go
@@ -37,16 +37,20 @@ func (suite *GenesisTestSuite) TestValidateGenesis() {
 		},
 		{
 			name: "valid genesis",
+			//nolint: exhaustruct
 			genState: &GenesisState{
-				Params:     DefaultParams(),
+				Params: DefaultParams(),
+				//nolint: exhaustruct
 				TokenPairs: []TokenPair{},
 			},
 			expPass: true,
 		},
 		{
 			name: "valid genesis - with tokens pairs",
+			//nolint: exhaustruct
 			genState: &GenesisState{
 				Params: DefaultParams(),
+				//nolint: exhaustruct
 				TokenPairs: []TokenPair{
 					{
 						Erc20Address: "0xdac17f958d2ee523a2206206994597c13d831ec7",
@@ -59,8 +63,10 @@ func (suite *GenesisTestSuite) TestValidateGenesis() {
 		},
 		{
 			name: "invalid genesis - duplicated token pair",
+			//nolint: exhaustruct
 			genState: &GenesisState{
 				Params: DefaultParams(),
+				//nolint: exhaustruct
 				TokenPairs: []TokenPair{
 					{
 						Erc20Address: "0xdac17f958d2ee523a2206206994597c13d831ec7",
@@ -78,8 +84,10 @@ func (suite *GenesisTestSuite) TestValidateGenesis() {
 		},
 		{
 			name: "invalid genesis - duplicated token pair",
+			//nolint: exhaustruct
 			genState: &GenesisState{
 				Params: DefaultParams(),
+				//nolint: exhaustruct
 				TokenPairs: []TokenPair{
 					{
 						Erc20Address: "0xdac17f958d2ee523a2206206994597c13d831ec7",
@@ -97,8 +105,10 @@ func (suite *GenesisTestSuite) TestValidateGenesis() {
 		},
 		{
 			name: "invalid genesis - duplicated token pair",
+			//nolint: exhaustruct
 			genState: &GenesisState{
 				Params: DefaultParams(),
+				//nolint: exhaustruct
 				TokenPairs: []TokenPair{
 					{
 						Erc20Address: "0xdac17f958d2ee523a2206206994597c13d831ec7",
@@ -116,8 +126,10 @@ func (suite *GenesisTestSuite) TestValidateGenesis() {
 		},
 		{
 			name: "invalid genesis - invalid token pair",
+			//nolint: exhaustruct
 			genState: &GenesisState{
 				Params: DefaultParams(),
+				//nolint: exhaustruct
 				TokenPairs: []TokenPair{
 					{
 						Erc20Address: "0xinvalidaddress",
@@ -130,7 +142,8 @@ func (suite *GenesisTestSuite) TestValidateGenesis() {
 		},
 		{
 			// Voting period cant be zero
-			name:     "empty genesis",
+			name: "empty genesis",
+			//nolint: exhaustruct
 			genState: &GenesisState{},
 			expPass:  true,
 		},
diff --git a/x/erc20/types/msg.go b/x/erc20/types/msg.go
index 3ad959df..8fe720ed 100644
--- a/x/erc20/types/msg.go
+++ b/x/erc20/types/msg.go
@@ -1,6 +1,10 @@
 package types
 
 import (
+	errorsmod "cosmossdk.io/errors"
+
+	sdkmath "cosmossdk.io/math"
+
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 
@@ -9,7 +13,9 @@ import (
 )
 
 var (
+	//nolint: exhaustruct
 	_ sdk.Msg = &MsgConvertCoin{}
+	//nolint: exhaustruct
 	_ sdk.Msg = &MsgConvertERC20{}
 )
 
@@ -42,14 +48,14 @@ func (msg MsgConvertCoin) ValidateBasic() error {
 	}
 
 	if !msg.Coin.Amount.IsPositive() {
-		return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "cannot mint a non-positive amount")
+		return errorsmod.Wrapf(sdkerrors.ErrInvalidCoins, "cannot mint a non-positive amount")
 	}
 	_, err := sdk.AccAddressFromBech32(msg.Sender)
 	if err != nil {
-		return sdkerrors.Wrap(err, "invalid sender address")
+		return errorsmod.Wrap(err, "invalid sender address")
 	}
 	if !common.IsHexAddress(msg.Receiver) {
-		return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid receiver hex address %s", msg.Receiver)
+		return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid receiver hex address %s", msg.Receiver)
 	}
 	return nil
 }
@@ -66,7 +72,7 @@ func (msg MsgConvertCoin) GetSigners() []sdk.AccAddress {
 }
 
 // NewMsgConvertERC20 creates a new instance of MsgConvertERC20
-func NewMsgConvertERC20(amount sdk.Int, receiver sdk.AccAddress, contract, sender common.Address) *MsgConvertERC20 { // nolint: interfacer
+func NewMsgConvertERC20(amount sdkmath.Int, receiver sdk.AccAddress, contract, sender common.Address) *MsgConvertERC20 { // nolint: interfacer
 	return &MsgConvertERC20{
 		ContractAddress: contract.String(),
 		Amount:          amount,
@@ -84,17 +90,17 @@ func (msg MsgConvertERC20) Type() string { return TypeMsgConvertERC20 }
 // ValidateBasic runs stateless checks on the message
 func (msg MsgConvertERC20) ValidateBasic() error {
 	if !common.IsHexAddress(msg.ContractAddress) {
-		return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid contract hex address '%s'", msg.ContractAddress)
+		return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid contract hex address '%s'", msg.ContractAddress)
 	}
 	if !msg.Amount.IsPositive() {
-		return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "cannot mint a non-positive amount")
+		return errorsmod.Wrapf(sdkerrors.ErrInvalidCoins, "cannot mint a non-positive amount")
 	}
 	_, err := sdk.AccAddressFromBech32(msg.Receiver)
 	if err != nil {
-		return sdkerrors.Wrap(err, "invalid receiver address")
+		return errorsmod.Wrap(err, "invalid receiver address")
 	}
 	if !common.IsHexAddress(msg.Sender) {
-		return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid sender hex address %s", msg.Sender)
+		return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid sender hex address %s", msg.Sender)
 	}
 	return nil
 }
diff --git a/x/erc20/types/msg_test.go b/x/erc20/types/msg_test.go
index 952bad8b..53128f81 100644
--- a/x/erc20/types/msg_test.go
+++ b/x/erc20/types/msg_test.go
@@ -5,6 +5,8 @@ import (
 
 	"github.com/stretchr/testify/suite"
 
+	sdkmath "cosmossdk.io/math"
+
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	"github.com/evmos/ethermint/tests"
 
@@ -20,6 +22,7 @@ func TestMsgsTestSuite(t *testing.T) {
 }
 
 func (suite *MsgsTestSuite) TestMsgConvertCoinGetters() {
+	//nolint: exhaustruct
 	msgInvalid := MsgConvertCoin{}
 	msg := NewMsgConvertCoin(
 		sdk.NewCoin("test", sdk.NewInt(100)),
@@ -139,6 +142,7 @@ func (suite *MsgsTestSuite) TestMsgConvertCoin() {
 }
 
 func (suite *MsgsTestSuite) TestMsgConvertERC20Getters() {
+	//nolint: exhaustruct
 	msgInvalid := MsgConvertERC20{}
 	msg := NewMsgConvertERC20(
 		sdk.NewInt(100),
@@ -155,7 +159,7 @@ func (suite *MsgsTestSuite) TestMsgConvertERC20Getters() {
 func (suite *MsgsTestSuite) TestMsgConvertERC20New() {
 	testCases := []struct {
 		msg        string
-		amount     sdk.Int
+		amount     sdkmath.Int
 		receiver   sdk.AccAddress
 		contract   common.Address
 		sender     common.Address
@@ -186,7 +190,7 @@ func (suite *MsgsTestSuite) TestMsgConvertERC20New() {
 func (suite *MsgsTestSuite) TestMsgConvertERC20() {
 	testCases := []struct {
 		msg        string
-		amount     sdk.Int
+		amount     sdkmath.Int
 		receiver   string
 		contract   string
 		sender     string
diff --git a/x/erc20/types/params.go b/x/erc20/types/params.go
index 8d91a6e2..e59fe184 100644
--- a/x/erc20/types/params.go
+++ b/x/erc20/types/params.go
@@ -12,10 +12,12 @@ var (
 	ParamStoreKeyEnableEVMHook = []byte("EnableEVMHook")
 )
 
+// nolint: exhaustruct
 var _ paramtypes.ParamSet = &Params{}
 
 // ParamKeyTable returns the parameter key table.
 func ParamKeyTable() paramtypes.KeyTable {
+	//nolint: exhaustruct
 	return paramtypes.NewKeyTable().RegisterParamSet(&Params{})
 }
 
diff --git a/x/erc20/types/params_test.go b/x/erc20/types/params_test.go
index 3b6bb56a..b8255c8c 100644
--- a/x/erc20/types/params_test.go
+++ b/x/erc20/types/params_test.go
@@ -33,6 +33,7 @@ func (suite *ParamsTestSuite) TestParamsValidate() {
 		},
 		{
 			"empty",
+			//nolint: exhaustruct
 			Params{},
 			false,
 		},
diff --git a/x/erc20/types/proposal.go b/x/erc20/types/proposal.go
index 741eef99..62b59dab 100644
--- a/x/erc20/types/proposal.go
+++ b/x/erc20/types/proposal.go
@@ -4,8 +4,9 @@ import (
 	"fmt"
 	"strings"
 
+	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"
 	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
 	ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
@@ -21,8 +22,11 @@ const (
 
 // Implements Proposal Interface
 var (
+	//nolint: exhaustruct
 	_ govv1beta1.Content = &RegisterCoinProposal{}
+	//nolint: exhaustruct
 	_ govv1beta1.Content = &RegisterERC20Proposal{}
+	//nolint: exhaustruct
 	_ govv1beta1.Content = &ToggleTokenConversionProposal{}
 )
 
@@ -127,7 +131,7 @@ func (*RegisterERC20Proposal) ProposalType() string {
 // ValidateBasic performs a stateless check of the proposal fields
 func (rtbp *RegisterERC20Proposal) ValidateBasic() error {
 	if err := ethermint.ValidateAddress(rtbp.Erc20Address); err != nil {
-		return sdkerrors.Wrap(err, "ERC20 address")
+		return errorsmod.Wrap(err, "ERC20 address")
 	}
 	return govv1beta1.ValidateAbstract(rtbp)
 }
diff --git a/x/erc20/types/proposal_test.go b/x/erc20/types/proposal_test.go
index ecb58936..8cde9a84 100644
--- a/x/erc20/types/proposal_test.go
+++ b/x/erc20/types/proposal_test.go
@@ -21,11 +21,17 @@ func TestProposalTestSuite(t *testing.T) {
 }
 
 func (suite *ProposalTestSuite) TestKeysTypes() {
+	//nolint: exhaustruct
 	suite.Require().Equal("erc20", (&RegisterCoinProposal{}).ProposalRoute())
+	//nolint: exhaustruct
 	suite.Require().Equal("RegisterCoin", (&RegisterCoinProposal{}).ProposalType())
+	//nolint: exhaustruct
 	suite.Require().Equal("erc20", (&RegisterERC20Proposal{}).ProposalRoute())
+	//nolint: exhaustruct
 	suite.Require().Equal("RegisterERC20", (&RegisterERC20Proposal{}).ProposalType())
+	//nolint: exhaustruct
 	suite.Require().Equal("erc20", (&ToggleTokenConversionProposal{}).ProposalRoute())
+	//nolint: exhaustruct
 	suite.Require().Equal("ToggleTokenConversion", (&ToggleTokenConversionProposal{}).ProposalType())
 }
 
@@ -152,6 +158,7 @@ func (suite *ProposalTestSuite) TestRegisterERC20Proposal() {
 }
 
 func createFullMetadata(denom, symbol, name string) banktypes.Metadata {
+	//nolint: exhaustruct
 	return banktypes.Metadata{
 		Description: "desc",
 		Base:        denom,
@@ -177,6 +184,7 @@ func createMetadata(denom, symbol string) banktypes.Metadata {
 }
 
 func (suite *ProposalTestSuite) TestRegisterCoinProposal() {
+	//nolint: exhaustruct
 	validMetadata := banktypes.Metadata{
 		Description: "desc",
 		Base:        "coin",
diff --git a/x/erc20/types/utils_test.go b/x/erc20/types/utils_test.go
index 4790709e..5c643ae3 100644
--- a/x/erc20/types/utils_test.go
+++ b/x/erc20/types/utils_test.go
@@ -98,9 +98,11 @@ func TestEqualMetadata(t *testing.T) {
 		},
 		{
 			"different base field",
+			//nolint: exhaustruct
 			banktypes.Metadata{
 				Base: "acanto",
 			},
+			//nolint: exhaustruct
 			banktypes.Metadata{
 				Base: "tacanto",
 			},
diff --git a/x/gasfree/ante.go b/x/gasfree/ante.go
index 3a5f3955..62baade9 100644
--- a/x/gasfree/ante.go
+++ b/x/gasfree/ante.go
@@ -1,8 +1,9 @@
 package gasfree
 
 import (
+	errorsmod "cosmossdk.io/errors"
+
 	sdk "github.com/cosmos/cosmos-sdk/types"
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 
 	"github.com/AltheaFoundation/althea-L1/x/gasfree/keeper"
 )
@@ -32,7 +33,7 @@ func (sbd SelectiveBypassDecorator) AnteHandle(
 ) (newCtx sdk.Context, err error) {
 	gasFree, err := sbd.gasfreeKeeper.IsGasFreeTx(ctx, sbd.gasfreeKeeper, tx)
 	if err != nil {
-		return ctx, sdkerrors.Wrap(err, "failed to check AnteDecorator can be bypassed")
+		return ctx, errorsmod.Wrap(err, "failed to check AnteDecorator can be bypassed")
 	}
 
 	if !gasFree {
@@ -48,7 +49,7 @@ var data icatypes.InterchainAccountPacketData
 
 if err := icatypes.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil {
 	// UnmarshalJSON errors are indeterminate and therefore are not wrapped and included in failed acks
-	return nil, sdkerrors.Wrapf(icatypes.ErrUnknownDataType, "cannot unmarshal ICS-27 interchain account packet data")
+	return nil, errorsmod.Wrapf(icatypes.ErrUnknownDataType, "cannot unmarshal ICS-27 interchain account packet data")
 }
 
 switch data.Type {
diff --git a/x/gasfree/keeper/keeper.go b/x/gasfree/keeper/keeper.go
index 7a32bbe7..0eeb602e 100644
--- a/x/gasfree/keeper/keeper.go
+++ b/x/gasfree/keeper/keeper.go
@@ -1,6 +1,8 @@
 package keeper
 
 import (
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/cosmos/cosmos-sdk/codec"
 	storetypes "github.com/cosmos/cosmos-sdk/store/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
@@ -38,7 +40,7 @@ func NewKeeper(cdc codec.Codec, storeKey storetypes.StoreKey, paramSpace paramst
 func (k Keeper) GetParamsIfSet(ctx sdk.Context) (params types.Params, err error) {
 	for _, pair := range params.ParamSetPairs() {
 		if !k.paramSpace.Has(ctx, pair.Key) {
-			return types.Params{}, sdkerrors.Wrapf(sdkerrors.ErrNotFound, "the param key %s has not been set", string(pair.Key))
+			return types.Params{}, errorsmod.Wrapf(sdkerrors.ErrNotFound, "the param key %s has not been set", string(pair.Key))
 		}
 		k.paramSpace.Get(ctx, pair.Key, pair.Value)
 	}
@@ -92,7 +94,7 @@ func (k Keeper) IsGasFreeTx(ctx sdk.Context, keeper Keeper, tx sdk.Tx) (bool, er
 				err := k.Cdc.UnpackAny(m, &inner)
 				if err != nil {
 					return false,
-						sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "unable to unpack authz msgexec message: %v", err)
+						errorsmod.Wrapf(sdkerrors.ErrInvalidType, "unable to unpack authz msgexec message: %v", err)
 				}
 				// Check if the inner Msg is acceptable or not, returning an error kicks this whole Tx out of the mempool
 				if !k.IsGasFreeMsg(gasFreeMessageSet, inner) {
diff --git a/x/gasfree/module_test.go b/x/gasfree/module_test.go
index e72fbf46..3d1eeeb3 100644
--- a/x/gasfree/module_test.go
+++ b/x/gasfree/module_test.go
@@ -85,12 +85,14 @@ func (suite *GasfreeTestSuite) DoSetupTest(t require.TestingT) {
 		return gs
 	})
 
+	//nolint: exhaustruct
 	suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{
 		Height:          1,
 		ChainID:         "althea_7357-1",
 		Time:            time.Now().UTC(),
 		ProposerAddress: althea.ValidatorPubKey.Address().Bytes(),
 
+		//nolint: exhaustruct
 		Version: tmversion.Consensus{
 			Block: version.BlockProtocol,
 		},
diff --git a/x/gasfree/types/errors.go b/x/gasfree/types/errors.go
index 11dcd34b..7252bebb 100644
--- a/x/gasfree/types/errors.go
+++ b/x/gasfree/types/errors.go
@@ -1,7 +1,7 @@
 package types
 
 import (
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	sdkerrors "cosmossdk.io/errors"
 )
 
 const RootCodespace = "gasfree"
diff --git a/x/gasfree/types/genesis.go b/x/gasfree/types/genesis.go
index d9d7af2d..ee9c4438 100644
--- a/x/gasfree/types/genesis.go
+++ b/x/gasfree/types/genesis.go
@@ -3,8 +3,9 @@ package types
 import (
 	"fmt"
 
+	errorsmod "cosmossdk.io/errors"
+
 	sdk "github.com/cosmos/cosmos-sdk/types"
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
 
 	microtxtypes "github.com/AltheaFoundation/althea-L1/x/microtx/types"
@@ -31,7 +32,7 @@ func (s GenesisState) ValidateBasic() error {
 		return ErrInvalidParams
 	}
 	if err := ValidateGasFreeMessageTypes(s.Params.GasFreeMessageTypes); err != nil {
-		return sdkerrors.Wrap(err, "Invalid GasFreeMessageTypes GenesisState")
+		return errorsmod.Wrap(err, "Invalid GasFreeMessageTypes GenesisState")
 	}
 	return nil
 }
diff --git a/x/lockup/ante.go b/x/lockup/ante.go
index 88578708..6640d2b2 100644
--- a/x/lockup/ante.go
+++ b/x/lockup/ante.go
@@ -7,6 +7,8 @@ package lockup
 import (
 	"fmt"
 
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/cosmos/cosmos-sdk/codec"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@@ -95,7 +97,7 @@ func (lad LockAnteDecorator) AnteHandle(
 					err := lad.cdc.UnpackAny(m, &inner)
 					if err != nil {
 						return ctx,
-							sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "unable to unpack authz msgexec message: %v", err)
+							errorsmod.Wrapf(sdkerrors.ErrInvalidType, "unable to unpack authz msgexec message: %v", err)
 					}
 					// Check if the inner Msg is acceptable or not, returning an error kicks this whole Tx out of the mempool
 					if err := lad.isAcceptable(ctx, inner); err != nil {
@@ -127,21 +129,21 @@ func (lad LockAnteDecorator) isAcceptable(ctx sdk.Context, msg sdk.Msg) error {
 	if _, typePresent := lockedMsgTypesSet[msgType]; typePresent {
 		// Check that any locked msg is permissible on a type-case basis
 		if allow, err := allowMessage(msg, exemptSet, lockedTokenDenomsSet); !allow {
-			return sdkerrors.Wrap(err, "Transaction blocked because of a message")
+			return errorsmod.Wrap(err, "Transaction blocked because of a message")
 		} else {
 			// The user is exempt, allow it to pass
 			return nil
 		}
 	}
 	if msgType == "/cosmos.distribution.v1beta1.MsgSetWithdrawAddress" {
-		return sdkerrors.Wrap(types.ErrLocked, "The chain is locked, only exempt addresses may submit this Msg type")
+		return errorsmod.Wrap(types.ErrLocked, "The chain is locked, only exempt addresses may submit this Msg type")
 	}
 	if msgType == "/cosmos.authz.v1beta1.MsgExec" {
-		return sdkerrors.Wrap(types.ErrLocked, "The chain is locked, recursively MsgExec-wrapped Msgs are not allowed")
+		return errorsmod.Wrap(types.ErrLocked, "The chain is locked, recursively MsgExec-wrapped Msgs are not allowed")
 	}
 	if msgType == "/ethermint.evm.v1.MsgEthereumTx" {
 		if allow, err := allowMessage(msg, exemptSet, lockedTokenDenomsSet); !allow {
-			return sdkerrors.Wrap(err, "The chain is locked, only exempt addresses may submit this Msg type")
+			return errorsmod.Wrap(err, "The chain is locked, only exempt addresses may submit this Msg type")
 		} else {
 			return nil
 		}
@@ -166,7 +168,7 @@ func allowMessage(msg sdk.Msg, exemptSet map[string]struct{}, lockedTokenDenomsS
 			// Message sent from a non-exempt address while the chain is locked up, is the transfer a locked coin?
 			for _, coin := range msgSend.Amount {
 				if _, present := lockedTokenDenomsSet[coin.Denom]; present {
-					return false, sdkerrors.Wrap(types.ErrLocked,
+					return false, errorsmod.Wrap(types.ErrLocked,
 						"The chain is locked, only exempt addresses may send locked denoms")
 				}
 			}
@@ -189,7 +191,7 @@ func allowMessage(msg sdk.Msg, exemptSet map[string]struct{}, lockedTokenDenomsS
 			}
 
 			if lockedToken && blockedAddress {
-				return false, sdkerrors.Wrap(types.ErrLocked,
+				return false, errorsmod.Wrap(types.ErrLocked,
 					"The chain is locked, only exempt addresses may be inputs in a MultiSend message containing a locked token denom")
 			}
 		}
@@ -203,7 +205,7 @@ func allowMessage(msg sdk.Msg, exemptSet map[string]struct{}, lockedTokenDenomsS
 			// The sender is not exempt, but are they sending a locked token?
 			if _, present := lockedTokenDenomsSet[msgTransfer.Token.Denom]; present {
 				// The token is locked, return an error
-				return false, sdkerrors.Wrap(types.ErrLocked,
+				return false, errorsmod.Wrap(types.ErrLocked,
 					"The chain is locked, only exempt addresses may Transfer a locked token denom over IBC")
 			}
 		}
@@ -217,7 +219,7 @@ func allowMessage(msg sdk.Msg, exemptSet map[string]struct{}, lockedTokenDenomsS
 			// The sender is not exempt, but are they sending a locked token?
 			if _, present := lockedTokenDenomsSet[msgMicrotx.Amount.Denom]; present {
 				// The token is locked, return an error
-				return false, sdkerrors.Wrap(types.ErrLocked,
+				return false, errorsmod.Wrap(types.ErrLocked,
 					"The chain is locked, only exempt addresses may Microtx a locked token denom")
 			}
 		}
@@ -230,13 +232,13 @@ func allowMessage(msg sdk.Msg, exemptSet map[string]struct{}, lockedTokenDenomsS
 		addressBytes := common.HexToAddress(msgEvmTx.From).Bytes()
 		ethermintAddr := sdk.AccAddress(addressBytes)
 		if _, present := exemptSet[ethermintAddr.String()]; !present {
-			return false, sdkerrors.Wrap(types.ErrLocked,
+			return false, errorsmod.Wrap(types.ErrLocked,
 				"The chain is locked, only exempt addresses may send a MsgEthereumTx")
 		}
 		return true, nil
 
 	default:
-		return false, sdkerrors.Wrap(types.ErrUnhandled,
+		return false, errorsmod.Wrap(types.ErrUnhandled,
 			fmt.Sprintf("Message type %v does not have a case in allowMessage, unable to handle messages like this",
 				sdk.MsgTypeURL(msg),
 			),
diff --git a/x/lockup/keeper/keeper.go b/x/lockup/keeper/keeper.go
index 7d4c5f29..39717388 100644
--- a/x/lockup/keeper/keeper.go
+++ b/x/lockup/keeper/keeper.go
@@ -1,6 +1,8 @@
 package keeper
 
 import (
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/cosmos/cosmos-sdk/codec"
 	storetypes "github.com/cosmos/cosmos-sdk/store/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
@@ -37,7 +39,7 @@ func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, paramSpace p
 func (k Keeper) GetParamsIfSet(ctx sdk.Context) (params types.Params, err error) {
 	for _, pair := range params.ParamSetPairs() {
 		if !k.paramSpace.Has(ctx, pair.Key) {
-			return types.Params{}, sdkerrors.Wrapf(sdkerrors.ErrNotFound, "the param key %s has not been set", string(pair.Key))
+			return types.Params{}, errorsmod.Wrapf(sdkerrors.ErrNotFound, "the param key %s has not been set", string(pair.Key))
 		}
 		k.paramSpace.Get(ctx, pair.Key, pair.Value)
 	}
diff --git a/x/lockup/types/errors.go b/x/lockup/types/errors.go
index 48e31e44..daa322e2 100644
--- a/x/lockup/types/errors.go
+++ b/x/lockup/types/errors.go
@@ -1,7 +1,7 @@
 package types
 
 import (
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	sdkerrors "cosmossdk.io/errors"
 )
 
 const RootCodespace = "lockup"
diff --git a/x/lockup/types/genesis.go b/x/lockup/types/genesis.go
index 61010084..ee137731 100644
--- a/x/lockup/types/genesis.go
+++ b/x/lockup/types/genesis.go
@@ -3,8 +3,9 @@ package types
 import (
 	"fmt"
 
+	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"
 	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
 	ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types"
@@ -51,13 +52,13 @@ func DefaultParams() *Params {
 
 func (s GenesisState) ValidateBasic() error {
 	if err := ValidateLockExempt(s.Params.LockExempt); err != nil {
-		return sdkerrors.Wrap(err, "Invalid LockExempt GenesisState")
+		return errorsmod.Wrap(err, "Invalid LockExempt GenesisState")
 	}
 	if err := ValidateLockedMessageTypes(s.Params.LockedMessageTypes); err != nil {
-		return sdkerrors.Wrap(err, "Invalid LockedMessageTypes GenesisState")
+		return errorsmod.Wrap(err, "Invalid LockedMessageTypes GenesisState")
 	}
 	if err := ValidateLockedTokenDenoms(s.Params.LockedTokenDenoms); err != nil {
-		return sdkerrors.Wrap(err, "Invalid LockedTokenDenoms GenesisState")
+		return errorsmod.Wrap(err, "Invalid LockedTokenDenoms GenesisState")
 	}
 	return nil
 }
diff --git a/x/microtx/client/cli/query.go b/x/microtx/client/cli/query.go
index 61e1038e..d2b71652 100644
--- a/x/microtx/client/cli/query.go
+++ b/x/microtx/client/cli/query.go
@@ -3,9 +3,10 @@ package cli
 import (
 	"strconv"
 
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/cosmos/cosmos-sdk/client"
 	"github.com/cosmos/cosmos-sdk/client/flags"
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	"github.com/spf13/cobra"
 
 	"github.com/AltheaFoundation/althea-L1/x/microtx/types"
@@ -80,7 +81,7 @@ func CmdQueryMicrotxFee() *cobra.Command {
 
 			amount, err := strconv.ParseUint(args[0], 10, 64)
 			if err != nil {
-				return sdkerrors.Wrap(err, "invalid amount, expecting a nonnegative integer")
+				return errorsmod.Wrap(err, "invalid amount, expecting a nonnegative integer")
 			}
 
 			req := types.QueryMicrotxFeeRequest{
diff --git a/x/microtx/client/cli/tx.go b/x/microtx/client/cli/tx.go
index f2fad6a3..afeaf922 100644
--- a/x/microtx/client/cli/tx.go
+++ b/x/microtx/client/cli/tx.go
@@ -3,11 +3,12 @@ package cli
 import (
 	"github.com/spf13/cobra"
 
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/cosmos/cosmos-sdk/client"
 	"github.com/cosmos/cosmos-sdk/client/flags"
 	"github.com/cosmos/cosmos-sdk/client/tx"
 	sdk "github.com/cosmos/cosmos-sdk/types"
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 
 	"github.com/AltheaFoundation/althea-L1/x/microtx/types"
 )
@@ -47,24 +48,24 @@ func CmdMicrotx() *cobra.Command {
 
 			sender, err := sdk.AccAddressFromBech32(args[0])
 			if err != nil {
-				return sdkerrors.Wrapf(err, "provided sender address is invalid: %v", args[0])
+				return errorsmod.Wrapf(err, "provided sender address is invalid: %v", args[0])
 			}
 
 			receiver, err := sdk.AccAddressFromBech32(args[1])
 			if err != nil {
-				return sdkerrors.Wrapf(err, "provided receiver address is invalid: %v", args[1])
+				return errorsmod.Wrapf(err, "provided receiver address is invalid: %v", args[1])
 			}
 
 			amount := args[2]
 			coin, err := sdk.ParseCoinNormalized(amount)
 			if err != nil {
-				return sdkerrors.Wrapf(err, "invalid amount provided: %v", amount)
+				return errorsmod.Wrapf(err, "invalid amount provided: %v", amount)
 			}
 
 			// Make the message
 			msg := types.NewMsgMicrotx(sender.String(), receiver.String(), coin)
 			if err := msg.ValidateBasic(); err != nil {
-				return sdkerrors.Wrap(err, "invalid argument provided")
+				return errorsmod.Wrap(err, "invalid argument provided")
 			}
 
 			// Send it
@@ -89,14 +90,14 @@ func CmdLiquify() *cobra.Command {
 			}
 
 			if _, err := cmd.Flags().GetString(flags.FlagFrom); err != nil {
-				return sdkerrors.Wrap(err, "--from value missing or incorrect")
+				return errorsmod.Wrap(err, "--from value missing or incorrect")
 			}
 			from := cliCtx.GetFromAddress().String()
 
 			// Make the message
 			msg := types.NewMsgLiquify(from)
 			if err := msg.ValidateBasic(); err != nil {
-				return sdkerrors.Wrap(err, "invalid --from value provided")
+				return errorsmod.Wrap(err, "invalid --from value provided")
 			}
 
 			// Send it
diff --git a/x/microtx/handler.go b/x/microtx/handler.go
index 703d3652..d229bd44 100644
--- a/x/microtx/handler.go
+++ b/x/microtx/handler.go
@@ -3,6 +3,8 @@ package microtx
 import (
 	"fmt"
 
+	errorsmod "cosmossdk.io/errors"
+
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 
@@ -21,7 +23,7 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
 			res, err := msgServer.Microtx(sdk.WrapSDKContext(ctx), msg)
 			return sdk.WrapServiceResult(ctx, res, err)
 		default:
-			return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, fmt.Sprintf("Unrecognized microtx Msg type: %v", sdk.MsgTypeURL(msg)))
+			return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, fmt.Sprintf("Unrecognized microtx Msg type: %v", sdk.MsgTypeURL(msg)))
 		}
 	}
 }
diff --git a/x/microtx/keeper/grpc_query.go b/x/microtx/keeper/grpc_query.go
index 6cc6759f..4f78e1c5 100644
--- a/x/microtx/keeper/grpc_query.go
+++ b/x/microtx/keeper/grpc_query.go
@@ -5,6 +5,8 @@ import (
 	"fmt"
 	"strings"
 
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/AltheaFoundation/althea-L1/x/microtx/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerror "github.com/cosmos/cosmos-sdk/types/errors"
@@ -72,7 +74,7 @@ func (k Keeper) LiquidAccount(c context.Context, req *types.QueryLiquidAccountRe
 
 			return &types.QueryLiquidAccountResponse{Accounts: accs}, nil
 		} else {
-			return nil, sdkerror.Wrapf(sdkerror.ErrInvalidAddress, "owner must start with 0x (eip-55) or %v (bech32)", cosmosPrefix)
+			return nil, errorsmod.Wrapf(sdkerror.ErrInvalidAddress, "owner must start with 0x (eip-55) or %v (bech32)", cosmosPrefix)
 		}
 	}
 
diff --git a/x/microtx/keeper/keeper.go b/x/microtx/keeper/keeper.go
index 67e5fa00..43f6bcac 100644
--- a/x/microtx/keeper/keeper.go
+++ b/x/microtx/keeper/keeper.go
@@ -5,6 +5,8 @@ import (
 
 	"github.com/tendermint/tendermint/libs/log"
 
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/cosmos/cosmos-sdk/codec"
 	storetypes "github.com/cosmos/cosmos-sdk/store/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
@@ -102,7 +104,7 @@ func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
 func (k Keeper) GetParamsIfSet(ctx sdk.Context) (params types.Params, err error) {
 	for _, pair := range params.ParamSetPairs() {
 		if !k.paramSpace.Has(ctx, pair.Key) {
-			return types.Params{}, sdkerrors.Wrapf(sdkerrors.ErrNotFound, "the param key %s has not been set", string(pair.Key))
+			return types.Params{}, errorsmod.Wrapf(sdkerrors.ErrNotFound, "the param key %s has not been set", string(pair.Key))
 		}
 		k.paramSpace.Get(ctx, pair.Key, pair.Value)
 	}
@@ -113,7 +115,7 @@ func (k Keeper) GetParamsIfSet(ctx sdk.Context) (params types.Params, err error)
 // SetParams will store the given params after validating them
 func (k Keeper) SetParams(ctx sdk.Context, params types.Params) error {
 	if err := params.ValidateBasic(); err != nil {
-		return sdkerrors.Wrap(err, "unable to store params with failing ValidateBasic()")
+		return errorsmod.Wrap(err, "unable to store params with failing ValidateBasic()")
 	}
 	k.paramSpace.SetParamSet(ctx, &params)
 	return nil
diff --git a/x/microtx/keeper/liquid_account.go b/x/microtx/keeper/liquid_account.go
index 834ce907..1f99a293 100644
--- a/x/microtx/keeper/liquid_account.go
+++ b/x/microtx/keeper/liquid_account.go
@@ -11,6 +11,8 @@ import (
 
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
 
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/cosmos/cosmos-sdk/store/prefix"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@@ -39,11 +41,11 @@ func (k Keeper) DoLiquify(
 ) (common.Address, error) {
 	nftAddr, err := k.deployLiquidInfrastructureNFTContract(ctx, account)
 	if err != nil {
-		return common.Address{}, sdkerrors.Wrapf(types.ErrContractDeployment,
+		return common.Address{}, errorsmod.Wrapf(types.ErrContractDeployment,
 			"EVM::Liquify error deploying LiquidInfrastructureNFT: %s", err.Error())
 	}
 	if err := k.addLiquidInfrastructureEntry(ctx, account, nftAddr); err != nil {
-		return common.Address{}, sdkerrors.Wrapf(err, "unable to map bech32 -> NFT address")
+		return common.Address{}, errorsmod.Wrapf(err, "unable to map bech32 -> NFT address")
 	}
 
 	ctx.EventManager().EmitEvent(types.NewEventLiquify(account.String(), nftAddr))
@@ -57,15 +59,15 @@ func (k Keeper) DoLiquify(
 func (k Keeper) deployLiquidInfrastructureNFTContract(ctx sdk.Context, account sdk.AccAddress) (common.Address, error) {
 	contract, err := k.DeployContract(ctx, account, types.LiquidInfrastructureNFT, account.String())
 	if err != nil {
-		return common.Address{}, sdkerrors.Wrap(err, "liquid infrastructure account contract deployment failed")
+		return common.Address{}, errorsmod.Wrap(err, "liquid infrastructure account contract deployment failed")
 	}
 
 	version, err := k.queryLiquidInfrastructureContractVersion(ctx, contract)
 	if err != nil {
-		return common.Address{}, sdkerrors.Wrap(err, "could not query NFT version")
+		return common.Address{}, errorsmod.Wrap(err, "could not query NFT version")
 	}
 	if version.Cmp(CurrentNFTVersion) != 0 {
-		return common.Address{}, sdkerrors.Wrapf(err, "expected contract with version %v, got %v", CurrentNFTVersion, version)
+		return common.Address{}, errorsmod.Wrapf(err, "expected contract with version %v, got %v", CurrentNFTVersion, version)
 	}
 
 	return contract, nil
@@ -79,7 +81,7 @@ func (k Keeper) addLiquidInfrastructureEntry(ctx sdk.Context, accAddress sdk.Acc
 	key := types.GetLiquidAccountKey(accAddress)
 
 	if store.Has(key) {
-		return sdkerrors.Wrapf(types.ErrContractDeployment, "account %v already liquified", accAddress.String())
+		return errorsmod.Wrapf(types.ErrContractDeployment, "account %v already liquified", accAddress.String())
 	}
 
 	store.Set(key, nftAddress.Bytes())
@@ -93,7 +95,7 @@ func (k Keeper) queryLiquidInfrastructureOwner(ctx sdk.Context, nftAddress commo
 	var args = ToMethodArgs(&AccountId)
 	res, err := k.QueryEVM(ctx, "ownerOf", types.LiquidInfrastructureNFT, types.ModuleEVMAddress, &nftAddress, args...)
 	if err != nil {
-		return nil, sdkerrors.Wrap(err, "unable to call ownerOf with arg0=1")
+		return nil, errorsmod.Wrap(err, "unable to call ownerOf with arg0=1")
 	}
 	owner := common.BytesToAddress(res.Ret)
 	return &owner, nil
@@ -104,7 +106,7 @@ func (k Keeper) queryLiquidInfrastructureContractVersion(ctx sdk.Context, nftAdd
 	// ABI: uint256 public constant Version
 	res, err := k.QueryEVM(ctx, "Version", types.LiquidInfrastructureNFT, types.ModuleEVMAddress, &nftAddress)
 	if err != nil {
-		return nil, sdkerrors.Wrap(err, "unable to call Versionw with no args")
+		return nil, errorsmod.Wrap(err, "unable to call Versionw with no args")
 	}
 	version := big.NewInt(0).SetBytes(res.Ret)
 	return version, nil
@@ -116,13 +118,13 @@ func (k Keeper) queryLiquidInfrastructureThresholds(ctx sdk.Context, nftAddress
 	// ABI: getThresholds() public virtual view returns (address[] memory, uint256[] memory)
 	res, err := k.QueryEVM(ctx, "getThresholds", types.LiquidInfrastructureNFT, types.ModuleEVMAddress, &nftAddress)
 	if err != nil {
-		return nil, sdkerrors.Wrap(err, "unable to call getThresholds with no arguments")
+		return nil, errorsmod.Wrap(err, "unable to call getThresholds with no arguments")
 	}
 
 	// Use the ABI to unpack values. Expecting ([addr, ...], [uint256, ...])
 	values, err := types.LiquidInfrastructureNFT.ABI.Unpack("getThresholds", res.Ret)
 	if err != nil {
-		return nil, sdkerrors.Wrap(err, "Unable to unpack the getThresholds() response")
+		return nil, errorsmod.Wrap(err, "Unable to unpack the getThresholds() response")
 	}
 	if len(values) != 2 {
 		return nil, fmt.Errorf("expected to get a 2 tuple response, instead got %v values", len(values))
@@ -364,19 +366,19 @@ func (k Keeper) RedirectBalanceToToken(
 	logger.Debug("Enter RedirectBalanceToToken", "account", account.String(), "nft", nft.Hex(), "currBalance", currBalance.String(), "thresholdAmount", thresholdAmount.String())
 	excessBalance := currBalance.Amount.Sub(sdk.NewIntFromBigInt(&thresholdAmount))
 	if excessBalance.IsNegative() {
-		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "attempted to funnel insufficient balance")
+		return nil, errorsmod.Wrap(sdkerrors.ErrInvalidCoins, "attempted to funnel insufficient balance")
 	}
 	funnelAmount := sdk.NewCoin(currBalance.Denom, excessBalance)
 	context := sdk.WrapSDKContext(ctx)
 	msgConvertCoin := erc20types.NewMsgConvertCoin(funnelAmount, nft, account)
 	logger.Debug("About to redirect balance via convert coin", "msg", msgConvertCoin.String())
 	if err := msgConvertCoin.ValidateBasic(); err != nil {
-		return nil, sdkerrors.Wrap(err, "generated invalid convert coin msg")
+		return nil, errorsmod.Wrap(err, "generated invalid convert coin msg")
 	}
 	logger.Debug("Calling convert coin")
 	_, err := k.erc20Keeper.ConvertCoin(context, msgConvertCoin)
 	if err != nil {
-		return nil, sdkerrors.Wrap(err, "unable to funnel excess balance via x/erc20 convert coin")
+		return nil, errorsmod.Wrap(err, "unable to funnel excess balance via x/erc20 convert coin")
 	}
 	logger.Debug("Redirected balanace via convert coin")
 	return &funnelAmount, nil
@@ -419,7 +421,7 @@ func (k Keeper) DeployContract(
 	// method name is nil in this case, we are calling the constructor
 	ctorArgs, err := contract.ABI.Pack("", args...)
 	if err != nil {
-		return common.Address{}, sdkerrors.Wrapf(types.ErrContractDeployment,
+		return common.Address{}, errorsmod.Wrapf(types.ErrContractDeployment,
 			"EVM::DeployContract error packing data: %s", err.Error())
 	}
 
@@ -436,7 +438,7 @@ func (k Keeper) DeployContract(
 	nonce, err := k.accountKeeper.GetSequence(ctx, deployer)
 	if err != nil {
 		return common.Address{},
-			sdkerrors.Wrapf(types.ErrContractDeployment,
+			errorsmod.Wrapf(types.ErrContractDeployment,
 				"EVM::DeployContract error retrieving nonce: %s", err.Error())
 	}
 
@@ -444,7 +446,7 @@ func (k Keeper) DeployContract(
 	_, err = k.CallEVM(ctx, deployerEVM, nil, amount, data, true)
 	if err != nil {
 		return common.Address{},
-			sdkerrors.Wrapf(types.ErrContractDeployment,
+			errorsmod.Wrapf(types.ErrContractDeployment,
 				"EVM::DeployContract error deploying contract: %s", err.Error())
 	}
 
@@ -468,13 +470,13 @@ func (k Keeper) CallMethod(
 	// pack method args
 	data, err := contract.ABI.Pack(method, args...)
 	if err != nil {
-		return nil, sdkerrors.Wrapf(types.ErrContractCall, "EVM::CallMethod there was an issue packing the arguments into the method signature: %s", err.Error())
+		return nil, errorsmod.Wrapf(types.ErrContractCall, "EVM::CallMethod there was an issue packing the arguments into the method signature: %s", err.Error())
 	}
 
 	// call method
 	resp, err := k.CallEVM(ctx, from, contractAddr, amount, data, true)
 	if err != nil {
-		return nil, sdkerrors.Wrapf(types.ErrContractCall, "EVM::CallMethod error applying message: %s", err.Error())
+		return nil, errorsmod.Wrapf(types.ErrContractCall, "EVM::CallMethod error applying message: %s", err.Error())
 	}
 
 	return resp, nil
@@ -498,7 +500,7 @@ func (k Keeper) CallEVM(
 	cosmosLimit := ctx.GasMeter().Limit()
 	gasUsed := ctx.GasMeter().GasConsumed()
 	if cosmosLimit <= gasUsed {
-		return nil, sdkerrors.Wrap(sdkerrors.ErrOutOfGas, "insufficient gas")
+		return nil, errorsmod.Wrap(sdkerrors.ErrOutOfGas, "insufficient gas")
 	}
 	gasLimit := cosmosLimit - gasUsed
 
@@ -529,7 +531,7 @@ func (k Keeper) CallEVM(
 	ctx.GasMeter().ConsumeGas(res.GasUsed, "EVM call")
 
 	if res.Failed() {
-		return nil, sdkerrors.Wrap(evmtypes.ErrVMExecution, res.VmError)
+		return nil, errorsmod.Wrap(evmtypes.ErrVMExecution, res.VmError)
 	}
 	return res, nil
 }
@@ -550,7 +552,7 @@ func (k Keeper) QueryEVM(
 	// pack method args
 	data, err := contract.ABI.Pack(method, args...)
 	if err != nil {
-		return nil, sdkerrors.Wrapf(types.ErrContractCall, "EVM::CallMethod there was an issue packing the arguments into the method signature: %s", err.Error())
+		return nil, errorsmod.Wrapf(types.ErrContractCall, "EVM::CallMethod there was an issue packing the arguments into the method signature: %s", err.Error())
 	}
 
 	commit := false // Do not modify state, just simulate and return results
@@ -576,7 +578,7 @@ func (k Keeper) QueryEVM(
 	}
 
 	if res.Failed() {
-		return nil, sdkerrors.Wrap(evmtypes.ErrVMExecution, res.VmError)
+		return nil, errorsmod.Wrap(evmtypes.ErrVMExecution, res.VmError)
 	}
 	return res, nil
 }
diff --git a/x/microtx/keeper/msg_server.go b/x/microtx/keeper/msg_server.go
index aa29efd2..180dc308 100644
--- a/x/microtx/keeper/msg_server.go
+++ b/x/microtx/keeper/msg_server.go
@@ -3,6 +3,9 @@ package keeper
 import (
 	"context"
 
+	errorsmod "cosmossdk.io/errors"
+	sdkmath "cosmossdk.io/math"
+
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	sdkante "github.com/cosmos/cosmos-sdk/x/auth/ante"
@@ -48,12 +51,12 @@ func (m msgServer) Microtx(c context.Context, msg *types.MsgMicrotx) (*types.Msg
 	}
 
 	if m.bankKeeper.BlockedAddr(receiver) {
-		return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", msg.Receiver)
+		return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", msg.Receiver)
 	}
 
 	// Call the actual transfer implementation
 	if err := m.Keeper.Microtx(ctx, sender, receiver, msg.Amount); err != nil {
-		return nil, sdkerrors.Wrap(err, "unable to complete the transfer")
+		return nil, errorsmod.Wrap(err, "unable to complete the transfer")
 	}
 
 	return &types.MsgMicrotxResponse{}, err
@@ -72,7 +75,7 @@ func (k Keeper) Microtx(ctx sdk.Context, sender sdk.AccAddress, receiver sdk.Acc
 	if !k.gasfreeKeeper.IsGasFreeMsgType(ctx, sdk.MsgTypeURL(&types.MsgMicrotx{})) {
 		collected, err := k.DeductMicrotxFee(ctx, sender, amount)
 		if err != nil {
-			return sdkerrors.Wrap(err, "unable to collect MsgMicrotx fees")
+			return errorsmod.Wrap(err, "unable to collect MsgMicrotx fees")
 		}
 		ctx.EventManager().EmitEvent(types.NewEventMicrotxFeeCollected(sender.String(), *collected))
 	}
@@ -80,7 +83,7 @@ func (k Keeper) Microtx(ctx sdk.Context, sender sdk.AccAddress, receiver sdk.Acc
 	// Perform the transfer now that fees have been collected
 	err = k.bankKeeper.SendCoins(ctx, sender, receiver, sdk.NewCoins(amount))
 	if err != nil {
-		return sdkerrors.Wrap(err, "unable to send tokens via the bank module")
+		return errorsmod.Wrap(err, "unable to send tokens via the bank module")
 	}
 
 	// Emit an event for the block's event log
@@ -92,7 +95,7 @@ func (k Keeper) Microtx(ctx sdk.Context, sender sdk.AccAddress, receiver sdk.Acc
 	// migrate balances to the NFT if the amounts are in excess of any configured threshold
 	k.Logger(ctx).Debug("Detecting and funneling excess balances for liquid infrastructure accounts")
 	if err := k.RedirectLiquidAccountExcessBalance(ctx, receiver, erc20Address); err != nil {
-		return sdkerrors.Wrapf(err, "failed to redirect excess balance")
+		return errorsmod.Wrapf(err, "failed to redirect excess balance")
 	}
 
 	return nil
@@ -105,10 +108,10 @@ func (k Keeper) ValidateAndGetERC20Address(ctx sdk.Context, amount sdk.Coin) (co
 		// Ensure the input tokens are actively registered as an ERC20-convertible token
 		pair, found := k.erc20Keeper.GetTokenPair(ctx, k.erc20Keeper.GetTokenPairID(ctx, amount.Denom))
 		if !found {
-			return common.Address{}, sdkerrors.Wrapf(types.ErrInvalidMicrotx, "token %v is not registered as an erc20, only evm-compatible tokens may be used", amount.Denom)
+			return common.Address{}, errorsmod.Wrapf(types.ErrInvalidMicrotx, "token %v is not registered as an erc20, only evm-compatible tokens may be used", amount.Denom)
 		}
 		if !pair.Enabled {
-			return common.Address{}, sdkerrors.Wrapf(types.ErrInvalidMicrotx, "token %v is registered as an erc20 (%v), but the pair is not enabled", amount.Denom, pair.Erc20Address)
+			return common.Address{}, errorsmod.Wrapf(types.ErrInvalidMicrotx, "token %v is registered as an erc20 (%v), but the pair is not enabled", amount.Denom, pair.Erc20Address)
 		}
 		// Collect the ERC20 address for later use in funneling
 		erc20Address = common.HexToAddress(pair.Erc20Address)
@@ -125,7 +128,7 @@ func (k Keeper) ValidateAndGetERC20Address(ctx sdk.Context, amount sdk.Coin) (co
 func (k Keeper) DeductMsgMicrotxFee(ctx sdk.Context, msg *types.MsgMicrotx) (feeCollected *sdk.Coin, err error) {
 	_, err = k.ValidateAndGetERC20Address(ctx, msg.Amount)
 	if err != nil {
-		return nil, sdkerrors.Wrap(err, "unable to deduct Microtx fees")
+		return nil, errorsmod.Wrap(err, "unable to deduct Microtx fees")
 	}
 
 	sender, err := sdk.AccAddressFromBech32(msg.Sender)
@@ -136,7 +139,7 @@ func (k Keeper) DeductMsgMicrotxFee(ctx sdk.Context, msg *types.MsgMicrotx) (fee
 	feeCollected, err = k.DeductMicrotxFee(ctx, sender, msg.Amount)
 
 	if err != nil {
-		return nil, sdkerrors.Wrap(err, "unable to collect fees")
+		return nil, errorsmod.Wrap(err, "unable to collect fees")
 	}
 
 	return
@@ -156,7 +159,7 @@ func (k Keeper) DeductMicrotxFee(ctx sdk.Context, sender sdk.AccAddress, sendAmo
 	if !microtxFee.IsZero() { // Ignore fees too low to collect
 		balance := k.bankKeeper.GetBalance(ctx, sender, microtxFeeCoin.Denom)
 		if balance.IsLT(microtxFeeCoin) {
-			err := sdkerrors.Wrapf(
+			err := errorsmod.Wrapf(
 				sdkerrors.ErrInsufficientFee,
 				"balance is insufficient to pay the fee (%v < %v)",
 				balance.Amount,
@@ -179,7 +182,7 @@ func (k Keeper) DeductMicrotxFee(ctx sdk.Context, sender sdk.AccAddress, sendAmo
 }
 
 // getMicrotxFeeForAmount Computes the fee a user must pay for any input `amount`, given the current `basisPoints`
-func (k Keeper) getMicrotxFeeForAmount(amount sdk.Int, basisPoints uint64) sdk.Int {
+func (k Keeper) getMicrotxFeeForAmount(amount sdkmath.Int, basisPoints uint64) sdkmath.Int {
 	return sdk.NewDecFromInt(amount).
 		MulInt64(int64(basisPoints)).
 		QuoInt64(int64(BasisPointDivisor)).
@@ -201,7 +204,7 @@ func (m *msgServer) Liquify(c context.Context, msg *types.MsgLiquify) (*types.Ms
 	}
 	senderAcc := m.accountKeeper.GetAccount(ctx, sender)
 	if !IsEthermintAccount(senderAcc) {
-		return nil, sdkerrors.Wrap(sdkerrors.ErrorInvalidSigner, "liquid infrastructure accounts must use ethermint keys, perhaps this is the first message the sender has sent?")
+		return nil, errorsmod.Wrap(sdkerrors.ErrorInvalidSigner, "liquid infrastructure accounts must use ethermint keys, perhaps this is the first message the sender has sent?")
 	}
 
 	if m.Keeper.IsLiquidAccount(ctx, sender) {
@@ -211,7 +214,7 @@ func (m *msgServer) Liquify(c context.Context, msg *types.MsgLiquify) (*types.Ms
 	// Call the actual liquify implementation
 	nft, err := m.Keeper.DoLiquify(ctx, sender)
 	if err != nil {
-		return nil, sdkerrors.Wrap(err, "failed to liquify account")
+		return nil, errorsmod.Wrap(err, "failed to liquify account")
 	}
 
 	return &types.MsgLiquifyResponse{
diff --git a/x/microtx/module.go b/x/microtx/module.go
index 86c26990..a771f872 100644
--- a/x/microtx/module.go
+++ b/x/microtx/module.go
@@ -10,6 +10,8 @@ import (
 	"github.com/grpc-ecosystem/grpc-gateway/runtime"
 	"github.com/spf13/cobra"
 
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/cosmos/cosmos-sdk/client"
 	"github.com/cosmos/cosmos-sdk/codec"
 	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
@@ -145,7 +147,7 @@ func (am AppModule) QuerierRoute() string {
 // LegacyQuerierHandler returns the legacy sdk.Querier (NOT IMPLEMENTED)
 func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier {
 	return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) {
-		return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "Legacy querier is not implemented!")
+		return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "Legacy querier is not implemented!")
 	}
 }
 
diff --git a/x/microtx/types/errors.go b/x/microtx/types/errors.go
index ea5f69c3..6206b47f 100644
--- a/x/microtx/types/errors.go
+++ b/x/microtx/types/errors.go
@@ -1,15 +1,15 @@
 package types
 
 import (
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	errorsmod "cosmossdk.io/errors"
 )
 
 var (
-	ErrContractDeployment   = sdkerrors.Register(ModuleName, 1, "contract deploy failed")
-	ErrContractCall         = sdkerrors.Register(ModuleName, 2, "contract call failed")
-	ErrNoLiquidAccount      = sdkerrors.Register(ModuleName, 3, "account is not a liquid infrastructure account")
-	ErrInvalidThresholds    = sdkerrors.Register(ModuleName, 4, "invalid liquid infrastructure account thresholds")
-	ErrInvalidMicrotx       = sdkerrors.Register(ModuleName, 5, "invalid microtx")
-	ErrInvalidContract      = sdkerrors.Register(ModuleName, 6, "invalid contract")
-	ErrAccountAlreadyLiquid = sdkerrors.Register(ModuleName, 7, "account is already a liquid infrastructure account")
+	ErrContractDeployment   = errorsmod.Register(ModuleName, 1, "contract deploy failed")
+	ErrContractCall         = errorsmod.Register(ModuleName, 2, "contract call failed")
+	ErrNoLiquidAccount      = errorsmod.Register(ModuleName, 3, "account is not a liquid infrastructure account")
+	ErrInvalidThresholds    = errorsmod.Register(ModuleName, 4, "invalid liquid infrastructure account thresholds")
+	ErrInvalidMicrotx       = errorsmod.Register(ModuleName, 5, "invalid microtx")
+	ErrInvalidContract      = errorsmod.Register(ModuleName, 6, "invalid contract")
+	ErrAccountAlreadyLiquid = errorsmod.Register(ModuleName, 7, "account is already a liquid infrastructure account")
 )
diff --git a/x/microtx/types/genesis.go b/x/microtx/types/genesis.go
index e46184f2..3b211b93 100644
--- a/x/microtx/types/genesis.go
+++ b/x/microtx/types/genesis.go
@@ -4,7 +4,8 @@ import (
 	"bytes"
 	"fmt"
 
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	errorsmod "cosmossdk.io/errors"
+
 	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
 )
 
@@ -26,7 +27,7 @@ var (
 // calling their validation functions
 func (s GenesisState) ValidateBasic() error {
 	if err := s.Params.ValidateBasic(); err != nil {
-		return sdkerrors.Wrap(err, "params")
+		return errorsmod.Wrap(err, "params")
 	}
 	return nil
 }
@@ -48,7 +49,7 @@ func DefaultParams() *Params {
 // ValidateBasic checks that the parameters have valid values.
 func (p Params) ValidateBasic() error {
 	if err := validateMicrotxFeeBasisPoints(p.MicrotxFeeBasisPoints); err != nil {
-		return sdkerrors.Wrap(err, "MicrotxFeeBasisPoints")
+		return errorsmod.Wrap(err, "MicrotxFeeBasisPoints")
 	}
 	return nil
 }
diff --git a/x/microtx/types/liquid_account.go b/x/microtx/types/liquid_account.go
index c88b237e..3ed4c593 100644
--- a/x/microtx/types/liquid_account.go
+++ b/x/microtx/types/liquid_account.go
@@ -3,7 +3,8 @@ package types
 import (
 	"math/big"
 
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/ethereum/go-ethereum/common"
 )
 
@@ -19,7 +20,7 @@ func NewLiquidAccountThreshold(token common.Address, amount big.Int) LiquidAccou
 
 func NewLiquidAccountThresholds(tokens []common.Address, amounts []big.Int) ([]LiquidAccountThreshold, error) {
 	if len(tokens) != len(amounts) {
-		return nil, sdkerrors.Wrap(ErrInvalidThresholds, "token addresses must match limiting amounts")
+		return nil, errorsmod.Wrap(ErrInvalidThresholds, "token addresses must match limiting amounts")
 	}
 	out := []LiquidAccountThreshold{}
 	for i := 0; i < len(tokens); i++ {
diff --git a/x/microtx/types/msgs.go b/x/microtx/types/msgs.go
index 172c7dc3..09e450cc 100644
--- a/x/microtx/types/msgs.go
+++ b/x/microtx/types/msgs.go
@@ -1,8 +1,9 @@
 package types
 
 import (
+	errorsmod "cosmossdk.io/errors"
+
 	sdk "github.com/cosmos/cosmos-sdk/types"
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	authlegacy "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
 )
 
@@ -37,18 +38,18 @@ func (msg MsgMicrotx) Type() string { return TypeMsgMicrotx }
 func (msg *MsgMicrotx) ValidateBasic() error {
 	_, err := sdk.AccAddressFromBech32(msg.Sender)
 	if err != nil {
-		return sdkerrors.Wrap(err, "invalid sender in microtx msg microtx")
+		return errorsmod.Wrap(err, "invalid sender in microtx msg microtx")
 	}
 	_, err = sdk.AccAddressFromBech32(msg.Receiver)
 	if err != nil {
-		return sdkerrors.Wrap(err, "invalid receiver in microtx msg microtx")
+		return errorsmod.Wrap(err, "invalid receiver in microtx msg microtx")
 	}
 	if err := msg.Amount.Validate(); err != nil {
-		return sdkerrors.Wrap(err, "invalid coin in microtx msg microtx")
+		return errorsmod.Wrap(err, "invalid coin in microtx msg microtx")
 	}
 
 	if msg.Amount.Amount.Equal(sdk.ZeroInt()) {
-		return sdkerrors.Wrap(ErrInvalidMicrotx, "zero amount in microtx msg microtx")
+		return errorsmod.Wrap(ErrInvalidMicrotx, "zero amount in microtx msg microtx")
 	}
 	return nil
 }
@@ -83,7 +84,7 @@ func (msg MsgLiquify) Type() string { return TypeMsgLiquify }
 func (msg *MsgLiquify) ValidateBasic() error {
 	_, err := sdk.AccAddressFromBech32(msg.Sender)
 	if err != nil {
-		return sdkerrors.Wrap(err, "invalid sender in microtx msg liquify")
+		return errorsmod.Wrap(err, "invalid sender in microtx msg liquify")
 	}
 
 	return nil
diff --git a/x/nativedex/client/cli/tx.go b/x/nativedex/client/cli/tx.go
index 07fc0bd1..c17825f2 100644
--- a/x/nativedex/client/cli/tx.go
+++ b/x/nativedex/client/cli/tx.go
@@ -6,11 +6,11 @@ import (
 
 	"github.com/spf13/cobra"
 
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/cosmos/cosmos-sdk/client"
 	"github.com/cosmos/cosmos-sdk/version"
 
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
-
 	"github.com/AltheaFoundation/althea-L1/x/nativedex/types"
 )
 
@@ -63,13 +63,13 @@ Where metadata.json contains (example):
 
 			setup, err := GenericProposalCmdSetup(cmd)
 			if err != nil {
-				return sdkerrors.Wrap(err, "invalid arguments to command")
+				return errorsmod.Wrap(err, "invalid arguments to command")
 			}
 			var clientCtx, title, description, deposit, from = setup.ClientCtx, setup.Title, setup.Description, setup.Deposit, setup.From
 
 			propMetaData, err := ParseUpgradeProxyMetadata(clientCtx.Codec, args[0])
 			if err != nil {
-				return sdkerrors.Wrap(err, "Failure to parse JSON object")
+				return errorsmod.Wrap(err, "Failure to parse JSON object")
 			}
 
 			content := types.NewUpgradeProxyProposal(title, description, propMetaData)
@@ -104,18 +104,18 @@ Where metadata.json contains (example):
 
 			setup, err := GenericProposalCmdSetup(cmd)
 			if err != nil {
-				return sdkerrors.Wrap(err, "invalid arguments to command")
+				return errorsmod.Wrap(err, "invalid arguments to command")
 			}
 			var clientCtx, title, description, deposit, from = setup.ClientCtx, setup.Title, setup.Description, setup.Deposit, setup.From
 
 			propMetaData, err := ParseCollectTreasuryMetadata(clientCtx.Codec, args[0])
 			if err != nil {
-				return sdkerrors.Wrap(err, "Failure to parse JSON object")
+				return errorsmod.Wrap(err, "Failure to parse JSON object")
 			}
 
 			inSafeMode, err := strconv.ParseBool(args[1])
 			if err != nil {
-				return sdkerrors.Wrap(err, "invalid in-safe-mode")
+				return errorsmod.Wrap(err, "invalid in-safe-mode")
 			}
 
 			content := types.NewCollectTreasuryProposal(title, description, propMetaData, inSafeMode)
@@ -150,18 +150,18 @@ Where metadata.json contains (example):
 
 			setup, err := GenericProposalCmdSetup(cmd)
 			if err != nil {
-				return sdkerrors.Wrap(err, "invalid arguments to command")
+				return errorsmod.Wrap(err, "invalid arguments to command")
 			}
 			var clientCtx, title, description, deposit, from = setup.ClientCtx, setup.Title, setup.Description, setup.Deposit, setup.From
 
 			propMetaData, err := ParseSetTreasuryMetadata(clientCtx.Codec, args[0])
 			if err != nil {
-				return sdkerrors.Wrap(err, "Failure to parse JSON object")
+				return errorsmod.Wrap(err, "Failure to parse JSON object")
 			}
 
 			inSafeMode, err := strconv.ParseBool(args[1])
 			if err != nil {
-				return sdkerrors.Wrap(err, "invalid in-safe-mode")
+				return errorsmod.Wrap(err, "invalid in-safe-mode")
 			}
 
 			content := types.NewSetTreasuryProposal(title, description, propMetaData, inSafeMode)
@@ -197,18 +197,18 @@ Where metadata.json contains (example):
 
 			setup, err := GenericProposalCmdSetup(cmd)
 			if err != nil {
-				return sdkerrors.Wrap(err, "invalid arguments to command")
+				return errorsmod.Wrap(err, "invalid arguments to command")
 			}
 			var clientCtx, title, description, deposit, from = setup.ClientCtx, setup.Title, setup.Description, setup.Deposit, setup.From
 
 			propMetaData, err := ParseAuthorityTransferMetadata(clientCtx.Codec, args[0])
 			if err != nil {
-				return sdkerrors.Wrap(err, "Failure to parse JSON object")
+				return errorsmod.Wrap(err, "Failure to parse JSON object")
 			}
 
 			inSafeMode, err := strconv.ParseBool(args[1])
 			if err != nil {
-				return sdkerrors.Wrap(err, "invalid in-safe-mode")
+				return errorsmod.Wrap(err, "invalid in-safe-mode")
 			}
 
 			content := types.NewAuthorityTransferProposal(title, description, propMetaData, inSafeMode)
@@ -243,18 +243,18 @@ Where metadata.json contains (example):
 
 			setup, err := GenericProposalCmdSetup(cmd)
 			if err != nil {
-				return sdkerrors.Wrap(err, "invalid arguments to command")
+				return errorsmod.Wrap(err, "invalid arguments to command")
 			}
 			var clientCtx, title, description, deposit, from = setup.ClientCtx, setup.Title, setup.Description, setup.Deposit, setup.From
 
 			propMetaData, err := ParseHotPathOpenMetadata(clientCtx.Codec, args[0])
 			if err != nil {
-				return sdkerrors.Wrap(err, "Failure to parse JSON object")
+				return errorsmod.Wrap(err, "Failure to parse JSON object")
 			}
 
 			inSafeMode, err := strconv.ParseBool(args[1])
 			if err != nil {
-				return sdkerrors.Wrap(err, "invalid in-safe-mode")
+				return errorsmod.Wrap(err, "invalid in-safe-mode")
 			}
 
 			content := types.NewHotPathOpenProposal(title, description, propMetaData, inSafeMode)
@@ -289,18 +289,18 @@ Where metadata.json contains (example):
 
 			setup, err := GenericProposalCmdSetup(cmd)
 			if err != nil {
-				return sdkerrors.Wrap(err, "invalid arguments to command")
+				return errorsmod.Wrap(err, "invalid arguments to command")
 			}
 			var clientCtx, title, description, deposit, from = setup.ClientCtx, setup.Title, setup.Description, setup.Deposit, setup.From
 
 			propMetaData, err := ParseSetSafeModeMetadata(clientCtx.Codec, args[0])
 			if err != nil {
-				return sdkerrors.Wrap(err, "Failure to parse JSON object")
+				return errorsmod.Wrap(err, "Failure to parse JSON object")
 			}
 
 			inSafeMode, err := strconv.ParseBool(args[1])
 			if err != nil {
-				return sdkerrors.Wrap(err, "invalid in-safe-mode")
+				return errorsmod.Wrap(err, "invalid in-safe-mode")
 			}
 
 			content := types.NewSetSafeModeProposal(title, description, propMetaData, inSafeMode)
@@ -335,13 +335,13 @@ Where metadata.json contains (example):
 
 			setup, err := GenericProposalCmdSetup(cmd)
 			if err != nil {
-				return sdkerrors.Wrap(err, "invalid arguments to command")
+				return errorsmod.Wrap(err, "invalid arguments to command")
 			}
 			var clientCtx, title, description, deposit, from = setup.ClientCtx, setup.Title, setup.Description, setup.Deposit, setup.From
 
 			propMetaData, err := ParseTransferGovernanceMetadata(clientCtx.Codec, args[0])
 			if err != nil {
-				return sdkerrors.Wrap(err, "Failure to parse JSON object")
+				return errorsmod.Wrap(err, "Failure to parse JSON object")
 			}
 
 			content := types.NewTransferGovernanceProposal(title, description, propMetaData)
@@ -376,13 +376,13 @@ Where metadata.json contains (example):
 
 			setup, err := GenericProposalCmdSetup(cmd)
 			if err != nil {
-				return sdkerrors.Wrap(err, "invalid arguments to command")
+				return errorsmod.Wrap(err, "invalid arguments to command")
 			}
 			var clientCtx, title, description, deposit, from = setup.ClientCtx, setup.Title, setup.Description, setup.Deposit, setup.From
 
 			propMetaData, err := ParseOpsMetadata(clientCtx.Codec, args[0])
 			if err != nil {
-				return sdkerrors.Wrap(err, "Failure to parse JSON object")
+				return errorsmod.Wrap(err, "Failure to parse JSON object")
 			}
 
 			content := types.NewOpsProposal(title, description, propMetaData)
diff --git a/x/nativedex/client/cli/utils.go b/x/nativedex/client/cli/utils.go
index 0b83c4d4..831af04b 100644
--- a/x/nativedex/client/cli/utils.go
+++ b/x/nativedex/client/cli/utils.go
@@ -7,11 +7,12 @@ import (
 
 	"github.com/spf13/cobra"
 
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/cosmos/cosmos-sdk/client"
 	"github.com/cosmos/cosmos-sdk/client/tx"
 	"github.com/cosmos/cosmos-sdk/codec"
 	sdk "github.com/cosmos/cosmos-sdk/types"
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	"github.com/cosmos/cosmos-sdk/x/gov/client/cli"
 	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
 
@@ -83,13 +84,13 @@ func AddGenericProposalCommandFlags(cmd *cobra.Command) {
 	cmd.Flags().String(cli.FlagDescription, "", "description of proposal")
 	cmd.Flags().String(cli.FlagDeposit, "1aalthea", "deposit of proposal")
 	if err := cmd.MarkFlagRequired(cli.FlagTitle); err != nil {
-		panic(sdkerrors.Wrap(err, "No title provided"))
+		panic(errorsmod.Wrap(err, "No title provided"))
 	}
 	if err := cmd.MarkFlagRequired(cli.FlagDescription); err != nil {
-		panic(sdkerrors.Wrap(err, "No description provided"))
+		panic(errorsmod.Wrap(err, "No description provided"))
 	}
 	if err := cmd.MarkFlagRequired(cli.FlagDeposit); err != nil {
-		panic(sdkerrors.Wrap(err, "No deposit provided"))
+		panic(errorsmod.Wrap(err, "No deposit provided"))
 	}
 }
 
diff --git a/x/nativedex/proposal_handler.go b/x/nativedex/proposal_handler.go
index 7ca4dba9..dca23c6b 100644
--- a/x/nativedex/proposal_handler.go
+++ b/x/nativedex/proposal_handler.go
@@ -1,6 +1,8 @@
 package nativedex
 
 import (
+	errorsmod "cosmossdk.io/errors"
+
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
@@ -47,7 +49,7 @@ func NewNativeDexProposalHandler(k *keeper.Keeper) govv1beta1.Handler {
 			return handleOpsProposal(ctx, k, c)
 
 		default:
-			return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s proposal content type: %T", types.ModuleName, c)
+			return errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s proposal content type: %T", types.ModuleName, c)
 		}
 	}
 }
diff --git a/x/nativedex/types/errors.go b/x/nativedex/types/errors.go
index 670984c8..f57cca56 100644
--- a/x/nativedex/types/errors.go
+++ b/x/nativedex/types/errors.go
@@ -1,7 +1,7 @@
 package types
 
 import (
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	sdkerrors "cosmossdk.io/errors"
 )
 
 // x/nativedex module errors
diff --git a/x/nativedex/types/genesis.go b/x/nativedex/types/genesis.go
index 0092ca1f..38d39317 100644
--- a/x/nativedex/types/genesis.go
+++ b/x/nativedex/types/genesis.go
@@ -4,7 +4,8 @@ import (
 	"bytes"
 	"fmt"
 
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	errorsmod "cosmossdk.io/errors"
+
 	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
 	"github.com/ethereum/go-ethereum/common"
 )
@@ -28,7 +29,7 @@ var (
 // calling their validation functions
 func (s GenesisState) ValidateBasic() error {
 	if err := s.Params.ValidateBasic(); err != nil {
-		return sdkerrors.Wrap(err, "params")
+		return errorsmod.Wrap(err, "params")
 	}
 	return nil
 }
@@ -51,10 +52,10 @@ func DefaultParams() *Params {
 // ValidateBasic checks that the parameters have valid values.
 func (p Params) ValidateBasic() error {
 	if err := validateVerifiedNativeDexAddress(p.VerifiedNativeDexAddress); err != nil {
-		return sdkerrors.Wrap(err, "VerifiedNativeDexAddress")
+		return errorsmod.Wrap(err, "VerifiedNativeDexAddress")
 	}
 	if err := validateVerifiedCrocPolicyAddress(p.VerifiedCrocPolicyAddress); err != nil {
-		return sdkerrors.Wrap(err, "VerifiedCrocPolicyAddress")
+		return errorsmod.Wrap(err, "VerifiedCrocPolicyAddress")
 	}
 	return nil
 }
@@ -87,7 +88,7 @@ func validateVerifiedNativeDexAddress(i interface{}) error {
 	}
 
 	if !common.IsHexAddress(v) {
-		return sdkerrors.Wrap(ErrInvalidEvmAddress, "invalid address")
+		return errorsmod.Wrap(ErrInvalidEvmAddress, "invalid address")
 	}
 
 	return nil
@@ -100,7 +101,7 @@ func validateVerifiedCrocPolicyAddress(i interface{}) error {
 	}
 
 	if !common.IsHexAddress(v) {
-		return sdkerrors.Wrap(ErrInvalidEvmAddress, "invalid address")
+		return errorsmod.Wrap(ErrInvalidEvmAddress, "invalid address")
 	}
 
 	return nil
diff --git a/x/nativedex/types/proposal.go b/x/nativedex/types/proposal.go
index 45d699c9..e04cf154 100644
--- a/x/nativedex/types/proposal.go
+++ b/x/nativedex/types/proposal.go
@@ -3,7 +3,7 @@ package types
 import (
 	"github.com/ethereum/go-ethereum/common"
 
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	errorsmod "cosmossdk.io/errors"
 
 	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
 	govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
@@ -69,7 +69,7 @@ func (p *UpgradeProxyProposal) ValidateBasic() error {
 
 	md := p.GetMetadata()
 	if !common.IsHexAddress(md.CallpathAddress) {
-		return sdkerrors.Wrap(ErrInvalidEvmAddress, "invalid callpath address")
+		return errorsmod.Wrap(ErrInvalidEvmAddress, "invalid callpath address")
 	}
 
 	if md.CallpathIndex == 0 {
@@ -101,7 +101,7 @@ func (p *CollectTreasuryProposal) ValidateBasic() error {
 
 	md := p.GetMetadata()
 	if !common.IsHexAddress(md.TokenAddress) {
-		return sdkerrors.Wrap(ErrInvalidEvmAddress, "invalid token address")
+		return errorsmod.Wrap(ErrInvalidEvmAddress, "invalid token address")
 	}
 
 	return nil
@@ -129,7 +129,7 @@ func (p *SetTreasuryProposal) ValidateBasic() error {
 
 	md := p.GetMetadata()
 	if !common.IsHexAddress(md.TreasuryAddress) {
-		return sdkerrors.Wrap(ErrInvalidEvmAddress, "invalid treasury address")
+		return errorsmod.Wrap(ErrInvalidEvmAddress, "invalid treasury address")
 	}
 
 	return nil
@@ -157,7 +157,7 @@ func (p *AuthorityTransferProposal) ValidateBasic() error {
 
 	md := p.GetMetadata()
 	if !common.IsHexAddress(md.AuthAddress) {
-		return sdkerrors.Wrap(ErrInvalidEvmAddress, "invalid auth address")
+		return errorsmod.Wrap(ErrInvalidEvmAddress, "invalid auth address")
 	}
 
 	return nil
@@ -237,11 +237,11 @@ func (p *TransferGovernanceProposal) ValidateBasic() error {
 	md := p.GetMetadata()
 
 	if !common.IsHexAddress(md.Ops) {
-		return sdkerrors.Wrap(ErrInvalidEvmAddress, "invalid ops address")
+		return errorsmod.Wrap(ErrInvalidEvmAddress, "invalid ops address")
 	}
 
 	if !common.IsHexAddress(md.Emergency) {
-		return sdkerrors.Wrap(ErrInvalidEvmAddress, "invalid emergency address")
+		return errorsmod.Wrap(ErrInvalidEvmAddress, "invalid emergency address")
 	}
 
 	return nil
@@ -273,7 +273,7 @@ func (p *OpsProposal) ValidateBasic() error {
 	}
 
 	if len(md.CmdArgs) == 0 {
-		return sdkerrors.Wrap(govtypes.ErrInvalidProposalContent, "cmd args has zero length")
+		return errorsmod.Wrap(govtypes.ErrInvalidProposalContent, "cmd args has zero length")
 	}
 
 	return nil
diff --git a/x/onboarding/keeper/ibc_callbacks_test.go b/x/onboarding/keeper/ibc_callbacks_test.go
index 2935f6cf..27de4298 100644
--- a/x/onboarding/keeper/ibc_callbacks_test.go
+++ b/x/onboarding/keeper/ibc_callbacks_test.go
@@ -9,7 +9,7 @@ import (
 	evmtypes "github.com/evmos/ethermint/x/evm/types"
 	"github.com/stretchr/testify/mock"
 
-	math "cosmossdk.io/math"
+	sdkmath "cosmossdk.io/math"
 
 	"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
 	sdk "github.com/cosmos/cosmos-sdk/types"
@@ -100,7 +100,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
 	// Setup Cosmos <=> althea IBC relayer
 	denom := "uUSDC"
 	ibcDenom := uusdcIbcdenom
-	transferAmount := math.NewIntWithDecimal(25, 6)
+	transferAmount := sdkmath.NewIntWithDecimal(25, 6)
 	sourceChannel := "channel-0"
 	altheaChannel := sourceChannel
 	path := fmt.Sprintf("%s/%s", transfertypes.PortID, altheaChannel)
@@ -116,7 +116,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
 		malleate          func()
 		ackSuccess        bool
 		expVoucherBalance sdk.Coin
-		expErc20Balance   sdk.Int
+		expErc20Balance   sdkmath.Int
 	}{
 		{
 			"fail - invalid sender - missing '1' ",
@@ -186,7 +186,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
 				denom = uusdcCh100DenomTrace.BaseDenom
 				ibcDenom = uusdcCh100IbcDenom
 				altheaChannel = "channel-100"
-				transferAmount = math.NewIntWithDecimal(25, 6)
+				transferAmount = sdkmath.NewIntWithDecimal(25, 6)
 				transfer := transfertypes.NewFungibleTokenPacketData(denom, transferAmount.String(), secpAddrCosmos, ethsecpAddrAlthea, "")
 				bz := transfertypes.ModuleCdc.MustMarshalJSON(&transfer)
 				packet = channeltypes.NewPacket(bz, 100, transfertypes.PortID, sourceChannel, transfertypes.PortID, altheaChannel, timeoutHeight, 0)
@@ -202,7 +202,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
 				ibcDenom = uusdcIbcdenom
 
 				altheaChannel = sourceChannel
-				transferAmount = math.NewIntWithDecimal(25, 6)
+				transferAmount = sdkmath.NewIntWithDecimal(25, 6)
 				transfer := transfertypes.NewFungibleTokenPacketData(denom, transferAmount.String(), secpAddrCosmos, ethsecpAddrAlthea, "")
 				bz := transfertypes.ModuleCdc.MustMarshalJSON(&transfer)
 				packet = channeltypes.NewPacket(bz, 100, transfertypes.PortID, sourceChannel, transfertypes.PortID, altheaChannel, timeoutHeight, 0)
@@ -214,7 +214,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
 
 			},
 			true,
-			sdk.NewCoin(uusdcIbcdenom, math.NewIntWithDecimal(25, 6)),
+			sdk.NewCoin(uusdcIbcdenom, sdkmath.NewIntWithDecimal(25, 6)),
 			sdk.NewInt(0),
 		},
 	}
@@ -222,7 +222,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() {
 		suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
 			suite.SetupTest() // reset
 
-			coins := sdk.NewCoins(sdk.NewCoin("aalthea", math.NewIntWithDecimal(10000, 18)), sdk.NewCoin(uusdcIbcdenom, math.NewIntWithDecimal(10000, 6)))
+			coins := sdk.NewCoins(sdk.NewCoin("aalthea", sdkmath.NewIntWithDecimal(10000, 18)), sdk.NewCoin(uusdcIbcdenom, sdkmath.NewIntWithDecimal(10000, 6)))
 			suite.Require().NoError(suite.app.BankKeeper.MintCoins(suite.ctx, evmtypes.ModuleName, coins))
 			err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, evmtypes.ModuleName, secpAddr, coins)
 			suite.Require().NoError(err)
diff --git a/x/onboarding/keeper/keeper_test.go b/x/onboarding/keeper/keeper_test.go
index d0ad7b8e..94094beb 100644
--- a/x/onboarding/keeper/keeper_test.go
+++ b/x/onboarding/keeper/keeper_test.go
@@ -89,10 +89,10 @@ func (suite *KeeperTestSuite) SetupTest() {
 	})
 	cAddr, err := val.GetConsAddr()
 	require.NoError(suite.T(), err)
-	cfg, err := suite.app.EvmKeeper.EVMConfig(suite.ctx, cAddr, suite.app.EvmKeeper.ChainID())
+	_, err = suite.app.EvmKeeper.EVMConfig(suite.ctx, cAddr, suite.app.EvmKeeper.ChainID())
 	require.NoError(suite.T(), err)
-	cfg = cfg
 }
+
 func TestKeeperTestSuite(t *testing.T) {
 	suite.Run(t, new(KeeperTestSuite))
 }
diff --git a/x/onboarding/module.go b/x/onboarding/module.go
index 3a1aed84..b7a6de80 100644
--- a/x/onboarding/module.go
+++ b/x/onboarding/module.go
@@ -12,6 +12,8 @@ import (
 
 	abci "github.com/tendermint/tendermint/abci/types"
 
+	errorsmod "cosmossdk.io/errors"
+
 	"github.com/cosmos/cosmos-sdk/client"
 	"github.com/cosmos/cosmos-sdk/codec"
 	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
@@ -129,7 +131,7 @@ func (AppModule) QuerierRoute() string {
 // LegacyQuerierHandler returns the legacy sdk.Querier (NOT IMPLEMENTED)
 func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier {
 	return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) {
-		return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "Legacy querier is not implemented!")
+		return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "Legacy querier is not implemented!")
 	}
 }
 
diff --git a/x/onboarding/types/errors.go b/x/onboarding/types/errors.go
index c9fe73b1..b5ddd8be 100644
--- a/x/onboarding/types/errors.go
+++ b/x/onboarding/types/errors.go
@@ -1,7 +1,7 @@
 package types
 
 import (
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	sdkerrors "cosmossdk.io/errors"
 )
 
 // errors

From 06be91042e17c0233b0ca50bbdfaf74bd742590a Mon Sep 17 00:00:00 2001
From: Justin Kilpatrick <justin@althea.net>
Date: Sun, 12 Jan 2025 18:45:50 -0500
Subject: [PATCH 61/63] Fix: unknown method test

This test required no error for calling an endpoint that didn't exist.
Behavior wise this wasn't ever correct but previously the failure
occured at a different location in the call. Now it correctly occurs
when we try to call a method that obviously doesn't exist.

Contriburing to this is that calling "" is implicitly the constructor.
So the error was different than it normally would be for a unknown
method.
---
 x/erc20/keeper/evm_test.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/x/erc20/keeper/evm_test.go b/x/erc20/keeper/evm_test.go
index 7665032a..d849e9a1 100644
--- a/x/erc20/keeper/evm_test.go
+++ b/x/erc20/keeper/evm_test.go
@@ -163,8 +163,8 @@ func (suite *KeeperTestSuite) TestCallEVMWithData() {
 				contract, err := suite.DeployContract("coin", "token", erc20Decimals)
 				suite.Require().NoError(err)
 				account := tests.GenerateAddress()
-				data, err := erc20.Pack("", account)
-				suite.Require().NoError(err)
+				data, err := erc20.Pack("notACall", account)
+				suite.Require().Error(err)
 				return data, &contract
 			},
 			false,

From 6f7b85a7fe47a82df1a87ccbfd8664e871298460 Mon Sep 17 00:00:00 2001
From: Justin Kilpatrick <justin@althea.net>
Date: Sun, 12 Jan 2025 18:48:53 -0500
Subject: [PATCH 62/63] Compile required contracts in CI

Since the docker test and the ci test run on different flows now we need
to call this specifically in the ci flow.
---
 tests/all-up-test-ci.sh | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/tests/all-up-test-ci.sh b/tests/all-up-test-ci.sh
index ede1bf3e..8ce37be3 100755
--- a/tests/all-up-test-ci.sh
+++ b/tests/all-up-test-ci.sh
@@ -32,6 +32,9 @@ ls -lah artifacts/contracts/
 pwd
 popd
 
+# Copy the now complied contracts into the correct directory
+bash scripts/compile-contracts-for-go.sh
+
 # Compile the DEX contracts
 git clone https://github.com/AltheaFoundation/althea-dex.git solidity-dex/
 pushd solidity-dex/

From d368cb159946e58f2d9c7f4eebaf82f1a1991636 Mon Sep 17 00:00:00 2001
From: Justin Kilpatrick <justin@althea.net>
Date: Sun, 12 Jan 2025 18:58:06 -0500
Subject: [PATCH 63/63] Fix: contracts prep in test container

---
 tests/dockerfile/Dockerfile | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/tests/dockerfile/Dockerfile b/tests/dockerfile/Dockerfile
index 44ae817b..0b9bb6c3 100755
--- a/tests/dockerfile/Dockerfile
+++ b/tests/dockerfile/Dockerfile
@@ -19,10 +19,12 @@ RUN git clone https://github.com/AltheaFoundation/althea-dex.git /althea/solidit
 RUN pushd /althea/integration_tests && PATH=$PATH:$HOME/.cargo/bin cargo build --bin test-runner --release
 # generate artifacts for the ethereum contracts
 RUN pushd /althea/solidity/ && HUSKY_SKIP_INSTALL=1 npm install && npm run typechain
+# copy the contracts over so that the althea bin can use them
+RUN pushd /althea/ && bash scripts/compile-contracts-for-go.sh
 # generate artifacts for the dex contracts
 RUN pushd /althea/solidity-dex/ && HUSKY_SKIP_INSTALL=1 npm install && npx hardhat compile
 # The althea bin now depends on the output of the contracts
-RUN pushd /althea && make contracts
+RUN pushd /althea && make
 # build the althea chain binary
 RUN pushd /althea/ && PATH=$PATH:/usr/local/go/bin GOPROXY=https://proxy.golang.org make && PATH=$PATH:/usr/local/go/bin make install
 RUN git config --global --add safe.directory /althea