Skip to content

feat(upgrade): add v10 upgrade#105

Merged
AdriaCarrera merged 35 commits intomainfrom
feat/upgrade/evm-v0.5
Feb 19, 2026
Merged

feat(upgrade): add v10 upgrade#105
AdriaCarrera merged 35 commits intomainfrom
feat/upgrade/evm-v0.5

Conversation

@AdriaCarrera
Copy link
Contributor

@AdriaCarrera AdriaCarrera commented Feb 17, 2026

feat(upgrade): add v10 upgrade

Summary 💡

This PR includes the changes needed for the upgrade to cosmos/evm v0.5.1

References used:

Also changed:

  • Cmd also initializes evm-chain-id from the cosmos chain id
  • Cmd properly reads home directory
  • Reduced cmd redundancy with cosmos/evm
  • Improved DefaultGenesisState to include basic XRP and native coin representation
  • Refactored v9.CreateUpgradeHandler in order to be used in v10 for mainnet

To Do 🛠

  • Resolve code FIXEMEs
  • Update cosmos/cosmos-sdk dependency to our own fork that includes slashing invariants among others
  • Update cosmos/evm dependency to our own fork that includes erc20 precompile changes
  • Upgrade whole package version to v10
  • Pass firewatch tests in local environment
  • Test upgrade with devnet, testnet and mainnet states

NOTES 🤔

  • Simulation tests are broken due to the need denom metadata registrations inside x/bank

Summary by CodeRabbit

  • New Features

    • v10 upgrade handler added; EVM mempool exposed; dynamic-fee transactions enabled; updated EVM/precompile and address-encoding wiring.
  • Bug Fixes

    • Improved context handling and fee-market parameter initialization for more reliable fee logic.
  • Chores

    • Switched native denomination to XRP (axrp) and simplified EVM configuration; reworked genesis builders; removed legacy CLI/keyring commands; registered upgrades and updated dependencies.

@AdriaCarrera
Copy link
Contributor Author

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Feb 17, 2026

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link

coderabbitai bot commented Feb 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Ante handlers now accept sdk.Context and baseevmante options; fee-market params and dynamic-fee wiring were centralized. The app switches to axrp denomination constants, restructures genesis into module builders, removes the precompiles factory, adds v9/v10 upgrade flows and keeper interfaces, adjusts CLI/tests, and updates dependencies.

Changes

Cohort / File(s) Summary
Ante pipeline
app/ante/ante.go, app/ante/cosmos_handler.go, app/ante/evm_handler.go
Changed Ante handler constructors to accept ctx sdk.Context and baseevmante.HandlerOptions; switched imports to baseevmante; obtain feemarket/evm params from ctx; add DynamicFeeChecker boolean wiring; updated extension option type URL and decorator parameter wiring.
App core & mempool / codecs
app/app.go
Removed explicit homePath/evmChainID params from New; added EVMMempool field and GetMempool(); switched address codecs to evmaddress.NewEvmCodec; derived chain ID from app options; DefaultGenesis() now returns map[string]json.RawMessage.
Config / denomination
app/config.go
Replaced dynamic EVM config with static denom constants (Denom, DenomName, DenomSymbol, DenomDescription, NativeErc20ContractAddress); changed BaseDenom to axrp; removed chain-ID mappings and EVM options helpers.
Genesis builders
app/genesis.go
Refactored genesis into modular builders: NewEVMGenesisState, NewErc20GenesisState, NewFeeMarketGenesisState, NewBankGenesisState; compose module genesis into global genesis map.
Precompiles removed & upgrades added
app/precompiles.go (deleted), app/upgrades/v9/..., app/upgrades/v10/..., app/upgrades.go
Removed the precompiles factory; added v10 upgrade package, UpgradeName, and keeper interfaces (ERC20Keeper, EvmKeeper); centralized v9 upgrade steps into UpgradeHandler; registered v10 upgrade handler.
CLI: chain ID, keyring, utils, removed cmds
cmd/exrpd/cmd/root.go, cmd/exrpd/cmd/utils.go, cmd/exrpd/main.go, cmd/exrpd/cmd/chain.go (updated/removed), cmd/exrpd/cmd/keyring.go (deleted), cmd/exrpd/cmd/genaccounts.go (deleted)
Removed hard-coded chain-ID constants and custom keyring command/file; added CosmosChainIDToEvmChainID() and MustGetDefaultNodeHome(); root resolves chain ID from flags/home and uses EthSecp256k1 keyring option; removed AddGenesisAccountCmd.
Local node script
local-node.sh
Updated genesis script and genesis values to use axrp denom and adjusted ERC20/native precompile addresses, token pairs, and genesis accounts.
Integration & tests
testutil/integration/..., tests/integration/network.go, app/simulation_test.go
Switched integration genesis types to testutil.GenesisState; removed explicit evmChainID from tests; updated ante test wiring to use antetypes and dynamic fee flag; added GetPreciseBankClient() stubs; adjusted decimals handling and gas-meter/test harness.
Unit/integration harness
testutil/integration/exrp/...
Adjusted block gas-meter and StateDB init (removed header-hash usage); propagated genesis-type changes through setup helpers.
CLI internals / commands removed
cmd/exrpd/cmd/genaccounts.go, cmd/exrpd/cmd/keyring.go, cmd/exrpd/cmd/chain.go
Removed legacy CLI helpers and commands for adding genesis accounts and custom keyring options; removed predefined chain-ID constants and mappings.
Dependencies
go.mod
Bumped core dependencies (cosmos-sdk, cosmos/evm, gogoproto, ibc-go, grpc-gateway, testify, etc.) and various indirect modules; adjusted or removed some replace directives.
Upgrade metadata
app/upgrades/v10/constants.go
Added UpgradeName = "v10.0.0".

Sequence Diagram(s)

sequenceDiagram
    participant Client as TxSender
    participant Ante as NewAnteHandler
    participant FeeMarket as FeeMarketKeeper
    participant EVMK as EvmKeeper
    participant Next as NextHandler

    Client->>Ante: Deliver tx
    Ante->>Ante: Check extension options (dynamic fee)
    Ante->>FeeMarket: GetParams(ctx)
    FeeMarket-->>Ante: feemarketParams
    Ante->>EVMK: GetParams(ctx)
    EVMK-->>Ante: evmParams
    Ante->>Ante: Build decorator chain (gas fee, deduct, evm mono)
    Ante->>Next: Pass to next handler/decorators
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐇 I hop where handlers now take Context at play,

Fees gathered neatly, routed on their way.
Genesis parts assembled, axrp seeds sown,
Upgrades march onward — new keepers grown. 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The pull request description partially follows the required template with 'Summary' and 'To Do' sections, but lacks a structured 'Motivation', 'Changes', 'Considerations', and 'Dependencies' sections as specified. Reorganize the description to match the template structure: add explicit 'Motivation' section explaining the problem, restructure 'Changes' as a bulleted list with clear descriptions, add 'Considerations' for potential impacts, and include 'Dependencies' section for related branches or PRs.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(upgrade): add v10 upgrade' directly and clearly summarizes the main change - adding v10 upgrade support for cosmos/evm v0.5.1.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/upgrade/evm-v0.5

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
app/app.go (2)

195-213: ⚠️ Potential issue | 🔴 Critical

Initialize EVMMempool or add nil handling in GetMempool().

EVMMempool is declared but never initialized in the New() constructor. The method returns a nil pointer, which violates the sdkmempool.ExtMempool interface contract and will panic any caller. Either instantiate EVMMempool during app setup or add a safe default fallback in GetMempool(). The FIXME comment on line 196 confirms this is a known gap.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/app.go` around lines 195 - 213, GetMempool currently returns the
possibly-nil field EVMMempool which causes panics; either initialize EVMMempool
in the app constructor (New()) using the appropriate constructor (e.g.,
evmmempool.New... or the project's EVMMempool initializer) so the field is
non-nil, or make GetMempool defensive by checking if app.EVMMempool == nil and
returning a safe fallback/no-op implementation that satisfies
sdkmempool.ExtMempool (or lazily creating and assigning app.EVMMempool before
returning); update references to GetMempool and the New() constructor
accordingly.

471-494: ⚠️ Potential issue | 🟠 Major

GovKeeper is uninitialized when captured by DefaultStaticPrecompiles.

DefaultStaticPrecompiles(...) is called at line 472 before app.GovKeeper is initialized at line 574. Since GovKeeper is passed by value to the precompiles, they receive and permanently capture a zero-value keeper. This is acknowledged as a temporary workaround in the existing FIX comment. Either initialize GovKeeper earlier or defer precompile registration until after GovKeeper is ready.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/app.go` around lines 471 - 494, The EVM precompiles are being created
with a zero-value app.GovKeeper because DefaultStaticPrecompiles(...) is invoked
before app.GovKeeper is initialized; fix this by deferring the precompile
registration: call evmkeeper.NewKeeper(...) to create app.EvmKeeper first
(without chaining WithStaticPrecompiles), then after app.GovKeeper is assigned
initialize the static precompiles by calling app.EvmKeeper =
app.EvmKeeper.WithStaticPrecompiles(DefaultStaticPrecompiles(*app.StakingKeeper,
app.DistrKeeper, app.BankKeeper, &app.Erc20Keeper, &app.TransferKeeper,
app.IBCKeeper.ChannelKeeper, app.GovKeeper, app.SlashingKeeper, appCodec)).
Ensure you remove the inline WithStaticPrecompiles chain at creation so
DefaultStaticPrecompiles receives the fully-initialized app.GovKeeper.
🧹 Nitpick comments (9)
app/upgrades/v10/upgrades.go (1)

15-15: Prefer const for MainnetChainID.
This is a fixed identifier and doesn’t need to be mutable.

♻️ Proposed tweak
-var MainnetChainID = "xrplevm_1440000-1"
+const MainnetChainID = "xrplevm_1440000-1"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/upgrades/v10/upgrades.go` at line 15, MainnetChainID is declared with var
but is a fixed, immutable identifier; change its declaration from var
MainnetChainID = "xrplevm_1440000-1" to a constant (const MainnetChainID =
"xrplevm_1440000-1") so the symbol MainnetChainID is immutable and the intent is
clear (adjust any code that tries to reassign it if necessary).
testutil/integration/exrp/integration/clients.go (1)

20-20: Consider adding a fail-fast mechanism for defensive coding.
While GetPreciseBankClient() is currently never called and the precisebank keeper does not exist in the application, the pattern of returning nil could mask issues if the method is accidentally called. The // NOTE: Not needed comment makes the intent clear, but replacing the nil return with a panic provides better fail-fast behavior and prevents silent nil-dereference errors. This aligns with similar defensive practices already adopted elsewhere in the codebase.

Optional: Fail fast if called
func (n *IntegrationNetwork) GetPreciseBankClient() precisebanktypes.QueryClient {
-	return nil
+	panic("GetPreciseBankClient is not wired in IntegrationNetwork")
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@testutil/integration/exrp/integration/clients.go` at line 20,
GetPreciseBankClient currently returns nil which can silently hide misuse;
update the GetPreciseBankClient function to fail fast by panicking with a clear
message (e.g., "GetPreciseBankClient called but precisebank keeper not
implemented") instead of returning nil, while preserving the existing NOTE
comment so intent remains explicit and callers immediately surface incorrect
usage.
tests/integration/network.go (1)

82-85: Avoid returning nil from a client accessor.

Returning nil can lead to nil‑pointer panics if any test calls this. Consider failing fast or wiring a real client.

🧯 Fail fast suggestion
 func (n *Network) GetPreciseBankClient() precisebanktypes.QueryClient {
-	return nil
+	panic("GetPreciseBankClient not implemented")
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/network.go` around lines 82 - 85, The GetPreciseBankClient
method on type Network currently returns nil which can cause nil-pointer panics
when tests call it; update Network.GetPreciseBankClient to fail fast or provide
a real client: either return a properly initialized precisebanktypes.QueryClient
instance or make the accessor panic/raise a test-failing error with a clear
message (e.g., "precise bank client not initialized") so callers don't receive
nil; locate the Network.GetPreciseBankClient definition and replace the nil
return with an initialized client or an explicit fail path that surfaces the
misconfiguration immediately.
app/app.go (2)

262-274: Guard against a missing evm-chain-id value.

cast.ToUint64 yields 0 on an unset/invalid option. Consider validating and failing fast (or deriving from chain-id) to avoid an unintended 0 chain ID.

🛡️ Suggested guard
- evmChainID := cast.ToUint64(appOpts.Get(srvflags.EVMChainID))
+ evmChainID := cast.ToUint64(appOpts.Get(srvflags.EVMChainID))
+ if evmChainID == 0 {
+ 	panic("evm-chain-id must be set")
+ }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/app.go` around lines 262 - 274, The code in New uses
cast.ToUint64(appOpts.Get(srvflags.EVMChainID)) which returns 0 when the option
is missing, so add a guard after computing evmChainID to detect an unset/invalid
value and fail fast (or derive a value from the chain-id) instead of proceeding
with a 0; specifically, after evmChainID is computed in New, check if evmChainID
== 0 and then either return an error/panic with a clear message or compute
evmChainID from the existing chain-id, ensuring the validation references
evmChainID, appOpts.Get(srvflags.EVMChainID) and MakeEncodingConfig so the fix
is localized to the New constructor.

954-965: Keep DefaultGenesis in sync with the new genesis builders.

DefaultGenesis still uses direct EVM/ERC20 defaults, while the new helpers set base denom, preinstalls, and token pairs. Using the helpers here avoids drift in tests/simulations.

♻️ Suggested alignment
- evmGenState := evmtypes.DefaultGenesisState()
- evmGenState.Params.ActiveStaticPrecompiles = evmtypes.AvailableStaticPrecompiles
+ evmGenState := NewEVMGenesisState()
  genesis[evmtypes.ModuleName] = app.AppCodec().MustMarshalJSON(evmGenState)

- erc20GenState := erc20types.DefaultGenesisState()
+ erc20GenState := NewErc20GenesisState()
  genesis[erc20types.ModuleName] = app.AppCodec().MustMarshalJSON(erc20GenState)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/app.go` around lines 954 - 965, DefaultGenesis currently constructs EVM
and ERC20 genesis by calling evmtypes.DefaultGenesisState() and
erc20types.DefaultGenesisState(), which diverges from the new genesis builders
that set base denom, preinstalls and token pairs; update DefaultGenesis to use
the new genesis builder helpers instead of the direct DefaultGenesisState calls
(replace the evmGenState and erc20GenState construction with the corresponding
new builder functions), ensure the returned genesis JSON uses
app.AppCodec().MustMarshalJSON(...) as before, and make sure the builder results
include Params.ActiveStaticPrecompiles, base denom, preinstalled contracts, and
ERC20 token pair config so tests/simulations remain in sync.
app/simulation_test.go (1)

34-37: SimAppEVMChainID constant appears unused.

The constant SimAppEVMChainID = 777 is defined but doesn't appear to be used after the refactoring removed EVM chain ID parameters from the app constructor. Consider removing it if it's no longer needed.

#!/bin/bash
# Check if SimAppEVMChainID is used anywhere
rg -n "SimAppEVMChainID" --type=go
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/simulation_test.go` around lines 34 - 37, The SimAppEVMChainID constant
is defined but appears unused after refactoring; remove the unused constant
SimAppEVMChainID (or if it was intended to be kept, reintroduce its use where
EVM chain ID is required) — locate the constant declaration of SimAppEVMChainID
in simulation_test.go and delete the line `SimAppEVMChainID = 777` (or
reintegrate it into the app constructor/test setup if you actually need an EVM
chain id).
cmd/exrpd/cmd/root.go (2)

386-404: Consolidate app instantiation in appExport.

The newApp instantiation is duplicated across the if/else branches with only minor differences (false vs true for the trace parameter and the presence of LoadHeight). Consider refactoring to reduce duplication.

♻️ Suggested refactor
 func (a appCreator) appExport(
 	logger log.Logger,
 	db dbm.DB,
 	traceStore io.Writer,
 	height int64,
 	forZeroHeight bool,
 	jailAllowedAddrs []string,
 	appOpts servertypes.AppOptions,
 	modulesToExport []string,
 ) (servertypes.ExportedApp, error) {
 	chainID, err := getChainIDFromOpts(appOpts)
 	if err != nil {
 		return servertypes.ExportedApp{}, err
 	}

+	loadLatest := height == -1
+	newApp := app.New(logger, db, traceStore, loadLatest, map[int64]bool{}, uint(1), appOpts, baseapp.SetChainID(chainID))
 
-	if height != -1 {
-		newApp = app.New(logger, db, traceStore, false, map[int64]bool{}, uint(1), appOpts, baseapp.SetChainID(chainID))
-
-		if err := newApp.LoadHeight(height); err != nil {
+	if !loadLatest {
+		if err := newApp.LoadHeight(height); err != nil {
 			return servertypes.ExportedApp{}, err
 		}
-	} else {
-		newApp = app.New(logger, db, traceStore, true, map[int64]bool{}, uint(1), appOpts, baseapp.SetChainID(chainID))
 	}

 	return newApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/exrpd/cmd/root.go` around lines 386 - 404, The app instantiation is
duplicated; set a trace boolean based on height (e.g., trace := height == -1),
call newApp = app.New(logger, db, trace, map[int64]bool{}, uint(1), appOpts,
baseapp.SetChainID(chainID)) (keeping the same args and using the existing
app.New signature), then if height != -1 call newApp.LoadHeight(height) and
handle its error, and finally call
newApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs,
modulesToExport); update references to newApp, app.New, LoadHeight and
ExportAppStateAndValidators accordingly to remove the duplicated branches.

128-132: Document the fallback chain ID magic number.

The fallback value 9999 is used when CosmosChainIDToEvmChainID fails, but this magic number lacks documentation. Consider extracting it to a named constant with a comment explaining when this fallback is expected to be used and its implications.

📝 Suggested improvement
+// DefaultFallbackEVMChainID is used when the chain ID cannot be parsed from
+// the Cosmos chain ID format. This typically occurs during initial setup
+// before the chain ID is configured.
+const DefaultFallbackEVMChainID = 9999
+
 chainID, err := CosmosChainIDToEvmChainID(initClientCtx.ChainID)
 if err != nil {
-    chainID = 9999
+    chainID = DefaultFallbackEVMChainID
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/exrpd/cmd/root.go` around lines 128 - 132, Extract the magic number 9999
into a named constant (e.g. FALLBACK_EVM_CHAIN_ID = 9999) and add a comment
explaining this is a fallback EVM chain ID used when
CosmosChainIDToEvmChainID(initClientCtx.ChainID) fails (include when this can
happen and any implications for InitAppConfig). Replace the inline 9999
assignment to chainID with the constant and ensure references around
CosmosChainIDToEvmChainID and the subsequent InitAppConfig(app.BaseDenom,
chainID) call use the new constant.
cmd/exrpd/cmd/utils.go (1)

14-31: Edge case: chain IDs with multiple underscores or dashes are not handled gracefully.

The parsing logic will reject valid-looking chain IDs like xrp_evm_12345-1 (multiple underscores) or xrplevm_12345-1-hotfix (multiple dashes after the number). While this may be intentional, consider whether such formats could appear in practice.

Additionally, consider adding a brief unit test for this function to verify expected behavior with various inputs (valid formats, edge cases, and invalid formats).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/exrpd/cmd/utils.go` around lines 14 - 31, The function
CosmosChainIDToEvmChainID fails on IDs with extra underscores or multiple
dashes; change the parsing to split on the last underscore (use
strings.LastIndex to separate name and numeric segment) and then split the
numeric segment on the first dash (use strings.Index or strings.SplitN(segment,
"-", 2)) to extract the EVM numeric id, parse that with strconv.ParseUint, and
return clearer error messages on invalid format or parse failure; also add unit
tests for CosmosChainIDToEvmChainID covering valid cases like "xrp_evm_12345-1",
"xrplevm_12345-1-hotfix", names with underscores, and invalid inputs to assert
expected errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/ante/ante.go`:
- Around line 28-33: The switch case uses the wrong protobuf type URL: replace
the string literal "/cosmos.evm.ante.v1.ExtensionOptionDynamicFeeTx" with the
actual registered type URL "/ethermint.types.v1.ExtensionOptionDynamicFeeTx" so
the extension option is recognized; update the case in the same switch where
"/cosmos.evm.vm.v1.ExtensionOptionsEthereumTx" is handled (the block that
assigns anteHandler via newMonoEVMAnteHandler and newCosmosAnteHandler) to use
the corrected type URL.

In `@app/upgrades/v9/upgrades.go`:
- Around line 45-55: The migration calls MigrateEthAccountsToBaseAccounts and
MigrateErc20Module lack error handling; change those helpers to return an error
(e.g., MigrateEthAccountsToBaseAccounts(ctx, accountKeeper, evmKeeper) (error)
and MigrateErc20Module(ctx, storeKeys, erc20Keeper) (error)), update this
upgrade path to capture their returned errors, and handle failures by logging
the error with ctx.Logger().Error and returning or panicking appropriately (or
wrap and return the error from the upgrade function); alternatively, if you
choose not to change signatures, at minimum check for internal failure
indicators from those functions and replace the FIXME with a clear logged error
and deterministic failure behavior.
- Around line 106-110: The FIXME comment above the construction of
evmtypes.Params should be replaced with an explanatory note that discarding
legacyEvmParams.AllowUnprotectedTxs is intentional due to the Cosmos EVM v0.5.0
breaking change; update the comment near params := evmtypes.Params (and next to
the commented AllowUnprotectedTxs line) to say something like
"AllowUnprotectedTxs was removed in v0.5.0; unprotected tx acceptance is now
controlled per-node config" so future readers know this removal is deliberate.

In `@cmd/exrpd/cmd/root.go`:
- Around line 180-183: The code inconsistently calls two different
MustGetDefaultNodeHome implementations; replace the call in rootCmd (currently
using config.MustGetDefaultNodeHome()) with the same local utility function used
elsewhere (MustGetDefaultNodeHome()) or vice versa—pick one canonical
implementation and update references so all callers (e.g., in
cmd/exrpd/cmd/root.go and cmd/exrpd/cmd/utils.go) use that single symbol
(MustGetDefaultNodeHome) to ensure a consistent default node home path across
the codebase.

In `@go.mod`:
- Around line 21-23: The go.mod currently references upstream modules
(github.com/cosmos/cosmos-sdk and github.com/cosmos/evm) while the fork replace
directives are commented out; confirm which source should be authoritative for
this PR and then either uncomment and point the replace directives to the
xrplevm forks (to enable forked fixes like slashing invariants and ERC20
precompile) or remove/commented fork references if upstream already contains the
changes. Specifically, check the replace entries for
github.com/cosmos/cosmos-sdk and add/verify a replace for github.com/cosmos/evm
if needed, then update go.mod so the active replace directives reflect the
chosen fork or upstream versions before merging.

In `@local-node.sh`:
- Around line 61-69: Replace the hardcoded address literal
"ethm1zrxl239wa6ad5xge3gs68rt98227xgnjq0xyw2" in local-node.sh (used in the
bin/exrpd genesis add-genesis-account command) with a value derived from
configuration or a shell variable; e.g., read the eth-m prefix/address from
app/config.go or export it as an environment variable (e.g., ETHM_ADDR) and
reference that variable in the script so the address automatically matches the
configured ethm prefix and can be changed without editing the script.

---

Outside diff comments:
In `@app/app.go`:
- Around line 195-213: GetMempool currently returns the possibly-nil field
EVMMempool which causes panics; either initialize EVMMempool in the app
constructor (New()) using the appropriate constructor (e.g., evmmempool.New...
or the project's EVMMempool initializer) so the field is non-nil, or make
GetMempool defensive by checking if app.EVMMempool == nil and returning a safe
fallback/no-op implementation that satisfies sdkmempool.ExtMempool (or lazily
creating and assigning app.EVMMempool before returning); update references to
GetMempool and the New() constructor accordingly.
- Around line 471-494: The EVM precompiles are being created with a zero-value
app.GovKeeper because DefaultStaticPrecompiles(...) is invoked before
app.GovKeeper is initialized; fix this by deferring the precompile registration:
call evmkeeper.NewKeeper(...) to create app.EvmKeeper first (without chaining
WithStaticPrecompiles), then after app.GovKeeper is assigned initialize the
static precompiles by calling app.EvmKeeper =
app.EvmKeeper.WithStaticPrecompiles(DefaultStaticPrecompiles(*app.StakingKeeper,
app.DistrKeeper, app.BankKeeper, &app.Erc20Keeper, &app.TransferKeeper,
app.IBCKeeper.ChannelKeeper, app.GovKeeper, app.SlashingKeeper, appCodec)).
Ensure you remove the inline WithStaticPrecompiles chain at creation so
DefaultStaticPrecompiles receives the fully-initialized app.GovKeeper.

---

Nitpick comments:
In `@app/app.go`:
- Around line 262-274: The code in New uses
cast.ToUint64(appOpts.Get(srvflags.EVMChainID)) which returns 0 when the option
is missing, so add a guard after computing evmChainID to detect an unset/invalid
value and fail fast (or derive a value from the chain-id) instead of proceeding
with a 0; specifically, after evmChainID is computed in New, check if evmChainID
== 0 and then either return an error/panic with a clear message or compute
evmChainID from the existing chain-id, ensuring the validation references
evmChainID, appOpts.Get(srvflags.EVMChainID) and MakeEncodingConfig so the fix
is localized to the New constructor.
- Around line 954-965: DefaultGenesis currently constructs EVM and ERC20 genesis
by calling evmtypes.DefaultGenesisState() and erc20types.DefaultGenesisState(),
which diverges from the new genesis builders that set base denom, preinstalls
and token pairs; update DefaultGenesis to use the new genesis builder helpers
instead of the direct DefaultGenesisState calls (replace the evmGenState and
erc20GenState construction with the corresponding new builder functions), ensure
the returned genesis JSON uses app.AppCodec().MustMarshalJSON(...) as before,
and make sure the builder results include Params.ActiveStaticPrecompiles, base
denom, preinstalled contracts, and ERC20 token pair config so tests/simulations
remain in sync.

In `@app/simulation_test.go`:
- Around line 34-37: The SimAppEVMChainID constant is defined but appears unused
after refactoring; remove the unused constant SimAppEVMChainID (or if it was
intended to be kept, reintroduce its use where EVM chain ID is required) —
locate the constant declaration of SimAppEVMChainID in simulation_test.go and
delete the line `SimAppEVMChainID = 777` (or reintegrate it into the app
constructor/test setup if you actually need an EVM chain id).

In `@app/upgrades/v10/upgrades.go`:
- Line 15: MainnetChainID is declared with var but is a fixed, immutable
identifier; change its declaration from var MainnetChainID = "xrplevm_1440000-1"
to a constant (const MainnetChainID = "xrplevm_1440000-1") so the symbol
MainnetChainID is immutable and the intent is clear (adjust any code that tries
to reassign it if necessary).

In `@cmd/exrpd/cmd/root.go`:
- Around line 386-404: The app instantiation is duplicated; set a trace boolean
based on height (e.g., trace := height == -1), call newApp = app.New(logger, db,
trace, map[int64]bool{}, uint(1), appOpts, baseapp.SetChainID(chainID)) (keeping
the same args and using the existing app.New signature), then if height != -1
call newApp.LoadHeight(height) and handle its error, and finally call
newApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs,
modulesToExport); update references to newApp, app.New, LoadHeight and
ExportAppStateAndValidators accordingly to remove the duplicated branches.
- Around line 128-132: Extract the magic number 9999 into a named constant (e.g.
FALLBACK_EVM_CHAIN_ID = 9999) and add a comment explaining this is a fallback
EVM chain ID used when CosmosChainIDToEvmChainID(initClientCtx.ChainID) fails
(include when this can happen and any implications for InitAppConfig). Replace
the inline 9999 assignment to chainID with the constant and ensure references
around CosmosChainIDToEvmChainID and the subsequent InitAppConfig(app.BaseDenom,
chainID) call use the new constant.

In `@cmd/exrpd/cmd/utils.go`:
- Around line 14-31: The function CosmosChainIDToEvmChainID fails on IDs with
extra underscores or multiple dashes; change the parsing to split on the last
underscore (use strings.LastIndex to separate name and numeric segment) and then
split the numeric segment on the first dash (use strings.Index or
strings.SplitN(segment, "-", 2)) to extract the EVM numeric id, parse that with
strconv.ParseUint, and return clearer error messages on invalid format or parse
failure; also add unit tests for CosmosChainIDToEvmChainID covering valid cases
like "xrp_evm_12345-1", "xrplevm_12345-1-hotfix", names with underscores, and
invalid inputs to assert expected errors.

In `@tests/integration/network.go`:
- Around line 82-85: The GetPreciseBankClient method on type Network currently
returns nil which can cause nil-pointer panics when tests call it; update
Network.GetPreciseBankClient to fail fast or provide a real client: either
return a properly initialized precisebanktypes.QueryClient instance or make the
accessor panic/raise a test-failing error with a clear message (e.g., "precise
bank client not initialized") so callers don't receive nil; locate the
Network.GetPreciseBankClient definition and replace the nil return with an
initialized client or an explicit fail path that surfaces the misconfiguration
immediately.

In `@testutil/integration/exrp/integration/clients.go`:
- Line 20: GetPreciseBankClient currently returns nil which can silently hide
misuse; update the GetPreciseBankClient function to fail fast by panicking with
a clear message (e.g., "GetPreciseBankClient called but precisebank keeper not
implemented") instead of returning nil, while preserving the existing NOTE
comment so intent remains explicit and callers immediately surface incorrect
usage.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@go.mod`:
- Line 22: go.mod currently references upstream module github.com/cosmos/evm
v0.5.1 but the PR requires using the xrplevm fork; add a replace directive in
go.mod such as replacing github.com/cosmos/evm with the xrplevm fork (e.g.,
replace github.com/cosmos/evm => github.com/xrplevm/evm <fork-version>) so the
build uses the forked code, then run go mod tidy to update the dependency graph
and verify imports referencing github.com/cosmos/evm now resolve to the fork.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
app/app.go (1)

438-445: Address the TODO comment about IBC subspace removal.

The TODO on line 442 questions why the subspace parameter is removed in the upstream evmd reference. This should be investigated and documented before merging to avoid maintenance confusion.

Do you want me to search the upstream cosmos/evm repository for context on this change?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/app.go` around lines 438 - 445, Investigate and resolve the TODO about
the removed subspace parameter: check the upstream cosmos/evm (evmd) change that
removed passing app.GetSubspace(ibcexported.ModuleName) to ibckeeper.NewKeeper,
confirm whether ibckeeper.NewKeeper signature or module parameter handling
changed (search for ibckeeper.NewKeeper usages and its constructor definition),
then either restore passing app.GetSubspace(ibcexported.ModuleName) to
app.IBCKeeper = ibckeeper.NewKeeper(...) if required, or remove the TODO and add
a concise comment documenting the upstream rationale (referencing IBCKeeper,
ibckeeper.NewKeeper, GetSubspace, and evmd) so future maintainers understand why
the subspace is omitted.
app/upgrades/v10/upgrades.go (2)

28-43: Inconsistent logger usage.

The function initializes a structured logger with upgrade context on line 28, but then switches to ctx.Logger() directly for subsequent log statements (lines 38-43). Use the structured logger consistently for better log correlation.

♻️ Proposed fix for consistent logger usage
 		}
-		ctx.Logger().Info("Running v10 upgrade handler...")
-		ctx.Logger().Info("Init evm coin info...")
+		logger.Info("Running v10 upgrade handler...")
+		logger.Info("Init evm coin info...")
 		if err := evmKeeper.InitEvmCoinInfo(ctx); err != nil {
 			return nil, err
 		}
-		ctx.Logger().Info("Finished v10 upgrade handler")
+		logger.Info("Finished v10 upgrade handler")
 		return mm.RunMigrations(ctx, configurator, vm)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/upgrades/v10/upgrades.go` around lines 28 - 43, The code creates a
structured logger named logger := ctx.Logger().With("upgrade", UpgradeName) but
then uses ctx.Logger() for later messages; replace the direct ctx.Logger().Info
calls with logger.Info to keep the structured context for all log messages
(specifically the "Running v10 upgrade handler...", "Init evm coin info...", and
"Finished v10 upgrade handler" messages) and ensure any error logging around
evmKeeper.InitEvmCoinInfo and v9.UpgradeHandler also uses logger so the
"upgrade" field is consistently attached to all logs.

15-15: Potential duplicate constant declaration.

The MainnetChainID constant is defined here, but based on the AI summary, app/upgrades/v10/constants.go also exports constants for this package. Consider consolidating this constant in constants.go alongside UpgradeName for consistency and maintainability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/upgrades/v10/upgrades.go` at line 15, The MainnetChainID constant
declared in upgrades.go duplicates constants stored in constants.go; remove the
duplicate from app/upgrades/v10/upgrades.go and consolidate it into
app/upgrades/v10/constants.go alongside UpgradeName (or, if it already exists
there, delete the one in upgrades.go and import/reference the existing
constant), then update any local references to use the single MainnetChainID
definition to avoid duplicate declarations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/app.go`:
- Around line 196-199: GetMempool currently returns app.EVMMempool which is
never initialized and will be nil; update the New() constructor (where App is
created) to initialize the EVMMempool field with a proper sdkmempool.ExtMempool
instance (or a safe no-op/mock implementation) so GetMempool always returns a
non-nil mempool, or alternatively modify GetMempool to lazily create and assign
a default implementation to app.EVMMempool on first call; reference symbols:
New(), App struct, EVMMempool, and GetMempool().

---

Nitpick comments:
In `@app/app.go`:
- Around line 438-445: Investigate and resolve the TODO about the removed
subspace parameter: check the upstream cosmos/evm (evmd) change that removed
passing app.GetSubspace(ibcexported.ModuleName) to ibckeeper.NewKeeper, confirm
whether ibckeeper.NewKeeper signature or module parameter handling changed
(search for ibckeeper.NewKeeper usages and its constructor definition), then
either restore passing app.GetSubspace(ibcexported.ModuleName) to app.IBCKeeper
= ibckeeper.NewKeeper(...) if required, or remove the TODO and add a concise
comment documenting the upstream rationale (referencing IBCKeeper,
ibckeeper.NewKeeper, GetSubspace, and evmd) so future maintainers understand why
the subspace is omitted.

In `@app/upgrades/v10/upgrades.go`:
- Around line 28-43: The code creates a structured logger named logger :=
ctx.Logger().With("upgrade", UpgradeName) but then uses ctx.Logger() for later
messages; replace the direct ctx.Logger().Info calls with logger.Info to keep
the structured context for all log messages (specifically the "Running v10
upgrade handler...", "Init evm coin info...", and "Finished v10 upgrade handler"
messages) and ensure any error logging around evmKeeper.InitEvmCoinInfo and
v9.UpgradeHandler also uses logger so the "upgrade" field is consistently
attached to all logs.
- Line 15: The MainnetChainID constant declared in upgrades.go duplicates
constants stored in constants.go; remove the duplicate from
app/upgrades/v10/upgrades.go and consolidate it into
app/upgrades/v10/constants.go alongside UpgradeName (or, if it already exists
there, delete the one in upgrades.go and import/reference the existing
constant), then update any local references to use the single MainnetChainID
definition to avoid duplicate declarations.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/upgrades/v9/upgrades.go (1)

42-69: Inconsistent logger usage.

A logger with "upgrade" context is created at line 43 but only used at lines 44 and 67. The intermediate log statements (lines 46, 49, 55, 56, 65) use ctx.Logger() directly, losing the upgrade context.

♻️ Proposed fix for consistent logger usage
 func UpgradeHandler(ctx sdk.Context, storeKeys map[string]*storetypes.KVStoreKey, appCodec codec.Codec, accountKeeper authkeeper.AccountKeeper, evmKeeper EvmKeeper, erc20Keeper ERC20Keeper) error {
 	logger := ctx.Logger().With("upgrade", UpgradeName)
 	logger.Info("Running v9 upgrade handler...")
 
-	ctx.Logger().Info("migration EthAccounts to BaseAccounts...")
+	logger.Info("migrating EthAccounts to BaseAccounts...")
 	MigrateEthAccountsToBaseAccounts(ctx, accountKeeper, evmKeeper)
 
-	ctx.Logger().Info("migrating erc20 module...")
+	logger.Info("migrating erc20 module...")
 	MigrateErc20Module(
 		ctx,
 		storeKeys,
 		erc20Keeper,
 	)
-	ctx.Logger().Info("erc20 module migrated successfully")
-	ctx.Logger().Info("migrating evm module...")
+	logger.Info("erc20 module migrated successfully")
+	logger.Info("migrating evm module...")
 	if err := MigrateEvmModule(
 		ctx,
 		storeKeys,
 		appCodec,
 		evmKeeper,
 	); err != nil {
 		return err
 	}
-	ctx.Logger().Info("evm module migrated successfully")
+	logger.Info("evm module migrated successfully")
 
 	logger.Info("Finished v9 upgrade handler")
 	return nil
 }

Also note there's a typo at line 46: "migration" should be "migrating".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/upgrades/v9/upgrades.go` around lines 42 - 69, The upgrade handler
creates a contextual logger named logger in UpgradeHandler but several
intermediate logs call ctx.Logger().Info which loses the "upgrade" context;
replace all ctx.Logger().Info(...) calls in UpgradeHandler with logger.Info(...)
so every log (e.g., the messages around MigrateEthAccountsToBaseAccounts,
MigrateErc20Module, and MigrateEvmModule) uses the same contextual logger, and
fix the typo by changing the message "migration EthAccounts to BaseAccounts..."
to "migrating EthAccounts to BaseAccounts...".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/upgrades/v9/upgrades.go`:
- Around line 147-149: The per-account warning using
ctx.Logger().Warn(fmt.Sprintf("skipping non EthAccount %s",
account.GetAddress().String())) inside ak.IterateAccounts should be removed to
avoid millions of logs; instead, declare a counter before calling
ak.IterateAccounts, increment that counter when the account is not an EthAccount
in the IterateAccounts callback (do not log there), and after iteration emit a
single summary log (e.g., ctx.Logger().Info or Debug) reporting how many
non-EthAccount entries were skipped; update references to EthAccount,
ak.IterateAccounts, account.GetAddress().String(), and ctx.Logger().Warn
accordingly.

---

Duplicate comments:
In `@app/upgrades/v9/upgrades.go`:
- Around line 46-55: Both migration helpers currently swallow failures; update
MigrateEthAccountsToBaseAccounts and MigrateErc20Module to return error (e.g.,
func MigrateEthAccountsToBaseAccounts(ctx ..., ...) error and similar for
MigrateErc20Module), propagate errors up to the caller in upgrades.go, and
handle them where called (check returned error after each call, log a
descriptive error via ctx.Logger().Error and return the error from the outer
upgrade function or abort migration) so failures no longer silently proceed;
also update any callers/tests to handle the new error-returning signatures.

---

Nitpick comments:
In `@app/upgrades/v9/upgrades.go`:
- Around line 42-69: The upgrade handler creates a contextual logger named
logger in UpgradeHandler but several intermediate logs call ctx.Logger().Info
which loses the "upgrade" context; replace all ctx.Logger().Info(...) calls in
UpgradeHandler with logger.Info(...) so every log (e.g., the messages around
MigrateEthAccountsToBaseAccounts, MigrateErc20Module, and MigrateEvmModule) uses
the same contextual logger, and fix the typo by changing the message "migration
EthAccounts to BaseAccounts..." to "migrating EthAccounts to BaseAccounts...".

Comment on lines 147 to 149
ctx.Logger().Warn(fmt.Sprintf("skipping non EthAccount %s", account.GetAddress().String()))
return false
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Warning log will fire for every non-EthAccount, which is expected behavior.

ak.IterateAccounts iterates over all accounts in the store, including module accounts and standard Cosmos accounts. On a production chain, the vast majority of accounts are NOT EthAccounts—this would produce potentially millions of warning logs during the upgrade, obscuring actual issues.

The message "skipping non EthAccount" implies something unexpected, but non-EthAccounts are normal and expected. Consider removing this log entirely or changing it to Debug level with a counter that logs a summary at the end.

♻️ Proposed fix: aggregate count instead of per-account warning
 func MigrateEthAccountsToBaseAccounts(ctx sdk.Context, ak authkeeper.AccountKeeper, ek EvmKeeper) {
+	var migratedCount, skippedCount int
 	ak.IterateAccounts(ctx, func(account sdk.AccountI) (stop bool) {
 		ethAcc, ok := account.(*legacytypes.EthAccount)
 		if !ok {
-			ctx.Logger().Warn(fmt.Sprintf("skipping non EthAccount %s", account.GetAddress().String()))
+			skippedCount++
 			return false
 		}
 
 		// NOTE: we only need to add store entries for smart contracts
 		codeHashBytes := common.HexToHash(ethAcc.CodeHash).Bytes()
 		if !evmtypes.IsEmptyCodeHash(codeHashBytes) {
 			ek.SetCodeHash(ctx, ethAcc.EthAddress().Bytes(), codeHashBytes)
 		}
 
 		// Set the base account in the account keeper instead of the EthAccount
 		ak.SetAccount(ctx, ethAcc.BaseAccount)
+		migratedCount++
 
 		return false
 	})
+	ctx.Logger().Info(fmt.Sprintf("EthAccount migration complete: migrated=%d, skipped=%d", migratedCount, skippedCount))
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/upgrades/v9/upgrades.go` around lines 147 - 149, The per-account warning
using ctx.Logger().Warn(fmt.Sprintf("skipping non EthAccount %s",
account.GetAddress().String())) inside ak.IterateAccounts should be removed to
avoid millions of logs; instead, declare a counter before calling
ak.IterateAccounts, increment that counter when the account is not an EthAccount
in the IterateAccounts callback (do not log there), and after iteration emit a
single summary log (e.g., ctx.Logger().Info or Debug) reporting how many
non-EthAccount entries were skipped; update references to EthAccount,
ak.IterateAccounts, account.GetAddress().String(), and ctx.Logger().Warn
accordingly.

@AdriaCarrera AdriaCarrera merged commit 84457b5 into main Feb 19, 2026
5 checks passed
@AdriaCarrera AdriaCarrera deleted the feat/upgrade/evm-v0.5 branch February 19, 2026 18:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants