Skip to content

Conversation

@matthiasmatt
Copy link
Contributor

@matthiasmatt matthiasmatt commented Jul 24, 2025

feat(evm): add "eth.evm.v1.MsgConvertEvmToCoin" tx for ERC20 to bank coin conversions with a non-Ethereum transaction

This pull request extends the EVM module with full support for converting between ERC20 tokens and Cosmos bank coins. It introduces new message types, CLI commands, and keeper logic to cover both directions of conversion, including special-case handling for NIBI via WNIBI.

  • New Transaction Message: "eth.evm.v1.MsgConvertEvmToCoin"
  • Includes edits to behvaior of "eth.evm.v1.MsgConvertCoinToEvm" to account for
    WNIBI

1. ERC20 → Bank Coin Conversion

  • New Messages and API

    • Added MsgConvertEvmToCoin and MsgConvertEvmToCoinResponse in proto/eth/evm/v1/tx.proto.
    • Introduced gRPC and protobuf service definitions for ConvertEvmToCoin.
    • Added EventConvertEvmToCoin in proto/eth/evm/v1/events.proto to record conversions.
  • CLI

    • New convert-evm-to-coin command in x/evm/cli/tx.go with full test coverage in cli_test.go.
    • Validates ERC20 address, amount, and recipient, and supports both Bech32 and hex recipients.
  • Keeper Logic

    • Implemented ConvertEvmToCoin in msg_server.go.

    • Two execution paths:

      • Coin-originated FunTokens: burn ERC20 supply and release bank coins from module.
      • ERC20-originated FunTokens: transfer ERC20 to module and mint new bank coins.
    • Emits typed events and transaction logs.

    • Added validation to prevent conversion without a registered FunToken mapping.

2. Bank Coin → ERC20 Conversion with WNIBI Support

  • WNIBI Special Case (NIBI Wrapping)

    • Introduced convertCoinToEvmForWNIBI to handle wrapping NIBI into its ERC20 equivalent, WNIBI.
    • Validates that the canonical WNIBI contract is deployed and well-defined.
    • Converts bank-side NIBI into WNIBI tokens, then transfers them to the recipient.
  • General FunToken Flow

    • Existing ConvertCoinToEvm logic extended to distinguish between:

      • Coin-originated FunTokens → mint ERC20.
      • ERC20-originated FunTokens → escrow/move ERC20 from the module.
  • Upgrade Handling

    • Added v2_7_0 upgrade to set canonical WNIBI in the EVM params for mainnet.
    • Mainnet constants (MAINNET_WNIBI_ADDR, MAINNET_STNIBI_ADDR) defined in appconst.

3. Testing and Validation

  • Unit Tests

    • Added msg_convert_evm_to_coin_test.go with full scenarios: happy paths, insufficient funds, invalid addresses, and event checks.
    • Extended msg_convert_coin_to_evm_test.go with WNIBI coverage and race condition scenarios.
    • New helper assertions for balances across both bank coins and ERC20 (BalanceAssertNIBI, FunTokenBalanceAssert).
  • Genesis and Params Validation

    • Enforced that Params.CanonicalWnibi cannot be the zero address.
    • Extended genesis tests to validate WNIBI settings.

Minor Fixes and Updates

  • Improved error messages for MsgConvertCoinToEvm validation.
  • Added EIP55Addr.Equal helper method.
  • Updated CallContractWithInput to accept an optional weiValue.
  • Default tracer configs updated to reduce debug verbosity.

Summary

With these changes, Nibiru supports:

  1. Converting ERC20s to Bank Coins with MsgConvertEvmToCoin.
  2. Converting Bank Coins to ERC20s with MsgConvertCoinToEvm.
  3. Wrapping/unwrapping NIBI to WNIBI using canonical contracts, aligned with mainnet behavior.

@matthiasmatt matthiasmatt requested a review from a team as a code owner July 24, 2025 10:35
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 24, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds ERC20→bank-coin conversion: new protobuf messages/events and RPC; keeper handlers for coin/ERC20/WNIBI origins; CLI and tests; params extended with CanonicalWnibi and upgrade wiring; CallContractWithInput gains optional value param and many call sites/tests updated; embeds WNIBI contract and sudo-based params auth.

Changes

Cohort / File(s) Summary
Protobuf API (generated)
api/eth/evm/v1/events.pulsar.go, api/eth/evm/v1/tx.pulsar.go, api/eth/evm/v1/evm.pulsar.go
Generated Go additions: EventConvertEvmToCoin; MsgConvertEvmToCoin (+Response) and RPC; Params.CanonicalWnibi added with reflection/descriptor plumbing and getters.
Protobuf schema
proto/eth/evm/v1/events.proto, proto/eth/evm/v1/tx.proto, proto/eth/evm/v1/evm.proto
New EventConvertEvmToCoin message; MsgConvertEvmToCoin & response and ConvertEvmToCoin RPC; Params.canonical_wnibi added; option/format cleanup.
Keeper core / Call API
x/evm/keeper/call_contract.go, x/evm/keeper/erc20.go, x/evm/keeper/msg_server.go, x/evm/keeper/msg_convert_evm_to_coin.go, x/evm/keeper/msg_convert_coin_to_evm.go, x/evm/keeper/keeper.go, x/evm/keeper/msg_update_params.go
CallContractWithInput gains optional wei/value param; new Keeper.ConvertEvmToCoin and origin-specific helpers (coin/erc20/wnibi); WNIBI wrap/unwrap flows; Keeper stores sudoKeeper; UpdateParams now authorizes via sudo or authority.
Messages / codec / params / deps
x/evm/msg.go, x/evm/codec.go, x/evm/params.go, x/evm/errors.go, x/evm/deps.go
Adds MsgConvertEvmToCoin (Validate/GetSigners/GetSignBytes/ValidateBasic); registers new msgs in codec; adds CanonicalWnibi param with validation/default; adds ErrCanonicalWnibi and SudoKeeper interface; EIP55/gethcommon adjustments.
CLI
x/evm/cli/tx.go, x/evm/cli/cli_test.go
Adds convert-evm-to-coin tx command and TestCmdConvertEvmToCoin covering success/failure cases; registers command in TxCmd.
Embeds & EIP55
x/evm/embeds/embeds.go, eth/eip55.go
Embeds WNIBI contract JSON and loader; adds EIP55Addr.Equal helper.
Tests — keeper, flows, helpers
x/evm/keeper/*_test.go, x/evm/keeper/msg_convert_coin_to_evm.go
Adds comprehensive integration tests for convert flows (coin/erc20/WNIBI); reorganizes test suites (SuiteFunToken); updates denom usage and many CallContractWithInput call sites to include new param.
Tests — precompile/wasm/oracle/helpers
x/evm/precompile/*_test.go, x/evm/precompile/test/export.go, x/evm/evmtest/erc20.go, x/evm/evmtest/test_deps.go, x/evm/statedb/journal_test.go, x/evm/vmtracer.go, x/evm/evmmodule/genesis_test.go, x/evm/evmtest/genesis_inject.go
Adds test helpers: BalanceAssertNIBI, DeployWNIBI, WNIBI genesis injection; quiets tracer logs (Debug false); updates many tests to pass extra nil/value param to CallContractWithInput.
App constants & upgrades
app/appconst/appconst.go, app/upgrades.go, app/upgrades/v2_7_0/v2_7_0.go, app/upgrades/v2_5_0/*
Exposes MAINNET_WNIBI_ADDR and MAINNET_STNIBI_ADDR; registers v2.7.0 upgrade that sets CanonicalWnibi; v2.5.0 updated to use appconst and new CallContract signature.
Tooling / misc
justfile, CHANGELOG.md, .gitignore
justfile lint uses Dockerized golangci-lint; changelog formatting and Unreleased entry added; minor .gitignore whitespace trim.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User/CLI
  participant CLI as x/evm CLI
  participant MsgSrv as Msg gRPC
  participant Keeper as EVM Keeper
  participant EVM as EVM / StateDB
  participant ERC20 as ERC20 Contract
  participant Bank as Cosmos Bank

  U->>CLI: convert-evm-to-coin <erc20> <amount> <to>
  CLI->>MsgSrv: Broadcast MsgConvertEvmToCoin
  MsgSrv->>Keeper: ConvertEvmToCoin(msg)
  Keeper->>Keeper: Validate msg (sender, erc20, amount, to)
  alt erc20 == Params.CanonicalWnibi
    Keeper->>EVM: Call withdraw on WNIBI (CallContractWithInput(value=nil))
    EVM-->>Keeper: result & logs
    Keeper->>Bank: send bank coins to recipient
  else coin-originated mapping
    Keeper->>ERC20: burnFromAuthority(amount)
    ERC20-->>Keeper: logs/result
    Keeper->>Bank: transfer bank coins to recipient
  else erc20-originated mapping
    Keeper->>ERC20: transfer from sender to module
    ERC20-->>Keeper: logs/result
    Keeper->>Bank: mint+send bank coins to recipient
  end
  Keeper-->>MsgSrv: response + events (EventConvertEvmToCoin, tx logs)
  MsgSrv-->>CLI: tx result
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels

x: evm

Poem

I hopped through bytes where tokens meet,
From ERC20 burrows to bank-street sweet.
WNIBI unwrapped and found its way,
Events sang out the conversion play.
Upgrades set — I leap, delighted, hooray! 🐇

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch mat/evm-to-coin

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@matthiasmatt matthiasmatt requested a review from Copilot July 24, 2025 10:38
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This pull request introduces a new feature that enables converting ERC20 tokens back to their corresponding bank coin representation. The implementation includes comprehensive updates across protobuf definitions, gRPC interfaces, CLI commands, message validation, and keeper logic to support bidirectional token conversion.

  • Added ConvertEvmToCoin functionality to convert ERC20 tokens to bank coins
  • Implemented proper validation, event emission, and test coverage for the new conversion feature
  • Fixed a spelling error in oracle parameter documentation and updated cosmos-sdk dependency

Reviewed Changes

Copilot reviewed 18 out of 19 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
proto/eth/evm/v1/tx.proto Added MsgConvertEvmToCoin message and service definition
proto/eth/evm/v1/events.proto Added EventConvertEvmToCoin event for conversion tracking
x/evm/keeper/msg_server.go Implemented conversion logic for both coin-originated and ERC20-originated tokens
x/evm/msg.go Added message validation and signing methods for MsgConvertEvmToCoin
x/evm/cli/tx.go Added CLI command for ERC20 to bank coin conversion
x/oracle/types/oracle.pb.go Fixed spelling error: "maxium" to "maximum"
go.mod Updated cosmos-sdk dependency to v0.47.11-nibiru.3

Comment on lines 823 to 824
From: evm.EVM_MODULE_ADDRESS,
Nonce: k.GetAccNonce(ctx, evm.EVM_MODULE_ADDRESS),
Copy link

Copilot AI Jul 24, 2025

Choose a reason for hiding this comment

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

The EVM module address should not be the sender for burnFromAuthority call on coin-originated tokens. The actual sender should be used as the From address to properly represent who is initiating the burn operation.

Suggested change
From: evm.EVM_MODULE_ADDRESS,
Nonce: k.GetAccNonce(ctx, evm.EVM_MODULE_ADDRESS),
From: senderEthAddr,
Nonce: k.GetAccNonce(ctx, senderEthAddr),

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@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 (2)
.gitignore (1)

362-362: Preserve trailing newline to avoid unnecessary future diffs

Removing the final newline violates POSIX text-file conventions and can cause “No newline at end of file” diff noise going forward. Consider keeping the terminating line break.

 debug_container.dot
+
api/nibiru/oracle/v1/oracle.pulsar.go (1)

3794-3803: Residual typo in comment (“deviation.a”)

The words “deviation” and “a” are concatenated, which harms readability and documentation searchability.

-// deviation, the band is taken to be 1 standard deviation.a price
+// deviation, the band is taken to be 1 standard deviation.

No functional impact, just polish.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7f2ee2 and ae94ac9.

⛔ Files ignored due to path filters (4)
  • api/eth/evm/v1/tx_grpc.pb.go is excluded by !**/*.pb.go
  • x/evm/events.pb.go is excluded by !**/*.pb.go
  • x/evm/tx.pb.go is excluded by !**/*.pb.go
  • x/oracle/types/oracle.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (15)
  • .gitignore (1 hunks)
  • CHANGELOG.md (1 hunks)
  • api/eth/evm/v1/events.pulsar.go (6 hunks)
  • api/eth/evm/v1/tx.pulsar.go (6 hunks)
  • api/nibiru/oracle/v1/oracle.pulsar.go (1 hunks)
  • go.mod (1 hunks)
  • proto/eth/evm/v1/events.proto (1 hunks)
  • proto/eth/evm/v1/tx.proto (2 hunks)
  • x/evm/cli/cli_test.go (1 hunks)
  • x/evm/cli/tx.go (2 hunks)
  • x/evm/codec.go (1 hunks)
  • x/evm/keeper/funtoken_evm_to_coin_test.go (1 hunks)
  • x/evm/keeper/msg_server.go (1 hunks)
  • x/evm/msg.go (2 hunks)
  • x/evm/msg_test.go (1 hunks)
🧰 Additional context used
🧠 Learnings (13)
📓 Common learnings
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2315
File: app/upgrades/v2_5_0/v2_5_0.go:0-0
Timestamp: 2025-05-21T21:47:04.451Z
Learning: In the v2.5.0 upgrade handler for Nibiru Chain, excess stNIBI ERC20 token balances (those not belonging to explicitly tracked holders) are sent to a Nibiru team-controlled Gnosis Safe for redistribution to ensure no tokens are lost during contract upgrades.
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2095
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-10-31T18:28:26.466Z
Learning: When implementing the `bankkeeper.Keeper` interface in `x/evm/keeper/bank_extension.go`, methods must use non-pointer receivers to satisfy the interface requirements.
go.mod (2)

Learnt from: Unique-Divine
PR: #2315
File: app/upgrades/v2_5_0/v2_5_0.go:0-0
Timestamp: 2025-05-21T21:47:04.451Z
Learning: In the v2.5.0 upgrade handler for Nibiru Chain, excess stNIBI ERC20 token balances (those not belonging to explicitly tracked holders) are sent to a Nibiru team-controlled Gnosis Safe for redistribution to ensure no tokens are lost during contract upgrades.

Learnt from: CalicoNino
PR: #2317
File: token-registry/official_erc20s.json:31-31
Timestamp: 2025-05-30T17:11:36.101Z
Learning: In the Nibiru token registry, the USDC (Stargate) token intentionally uses different symbols in different files: "USDC.e" in token-registry/official_erc20s.json and "USDC" in token-registry/cosmos_assetlist.go. This difference is deliberate and should not be flagged as an inconsistency.

CHANGELOG.md (1)

Learnt from: Unique-Divine
PR: #2315
File: app/upgrades/v2_5_0/v2_5_0.go:0-0
Timestamp: 2025-05-21T21:47:04.451Z
Learning: In the v2.5.0 upgrade handler for Nibiru Chain, excess stNIBI ERC20 token balances (those not belonging to explicitly tracked holders) are sent to a Nibiru team-controlled Gnosis Safe for redistribution to ensure no tokens are lost during contract upgrades.

proto/eth/evm/v1/events.proto (1)

Learnt from: Unique-Divine
PR: #2095
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-10-31T18:28:26.466Z
Learning: When implementing the bankkeeper.Keeper interface in x/evm/keeper/bank_extension.go, methods must use non-pointer receivers to satisfy the interface requirements.

x/evm/codec.go (1)

Learnt from: onikonychev
PR: #2101
File: eth/rpc/backend/tx_info.go:144-170
Timestamp: 2024-10-31T16:39:44.773Z
Learning: In the file eth/rpc/backend/tx_info.go, when implementing the MarshalJSON method for TransactionReceipt, using a custom struct for marshaling instead of an intermediate map[string]interface{} does not work.

x/evm/cli/tx.go (2)

Learnt from: Unique-Divine
PR: #1869
File: x/evm/cli/cli.go:44-47
Timestamp: 2024-10-08T15:45:59.488Z
Learning: The loop in GetQueryCmd function of x/evm/cli/cli.go is a placeholder for future subcommands.

Learnt from: Unique-Divine
PR: #1869
File: x/evm/cli/cli.go:44-47
Timestamp: 2024-07-27T01:11:54.422Z
Learning: The loop in GetQueryCmd function of x/evm/cli/cli.go is a placeholder for future subcommands.

x/evm/cli/cli_test.go (5)

Learnt from: Unique-Divine
PR: #2119
File: x/evm/keeper/bank_extension_test.go:67-71
Timestamp: 2024-12-06T11:54:38.275Z
Learning: When writing tests in x/evm/keeper/bank_extension_test.go, prefer comparing gas consumption values as formatted strings using fmt.Sprintf("%d", value) instead of numeric values directly in s.Equalf. This avoids hexadecimal representations that are hard to read in shell outputs.

Learnt from: Unique-Divine
PR: #2119
File: x/evm/keeper/bank_extension_test.go:161-165
Timestamp: 2024-12-06T16:09:01.767Z
Learning: In Go test assertions within x/evm/keeper/bank_extension_test.go, when comparing uint64 values using s.Equalf, prefer formatting the values as decimal strings with fmt.Sprintf("%d", value) to avoid them being printed in hexadecimal.

Learnt from: k-yang
PR: #2165
File: x/evm/evmmodule/genesis_test.go:51-51
Timestamp: 2025-01-17T17:32:27.341Z
Learning: The NewEVM() method in TestDeps returns only *vm.EVM without an error value.

Learnt from: Unique-Divine
PR: #1869
File: x/evm/cli/cli.go:44-47
Timestamp: 2024-10-08T15:45:59.488Z
Learning: The loop in GetQueryCmd function of x/evm/cli/cli.go is a placeholder for future subcommands.

Learnt from: Unique-Divine
PR: #1869
File: x/evm/cli/cli.go:44-47
Timestamp: 2024-07-27T01:11:54.422Z
Learning: The loop in GetQueryCmd function of x/evm/cli/cli.go is a placeholder for future subcommands.

x/evm/msg_test.go (5)

Learnt from: Unique-Divine
PR: #2119
File: x/evm/keeper/bank_extension_test.go:161-165
Timestamp: 2024-12-06T16:09:01.767Z
Learning: In Go test assertions within x/evm/keeper/bank_extension_test.go, when comparing uint64 values using s.Equalf, prefer formatting the values as decimal strings with fmt.Sprintf("%d", value) to avoid them being printed in hexadecimal.

Learnt from: Unique-Divine
PR: #2119
File: x/evm/keeper/bank_extension_test.go:67-71
Timestamp: 2024-12-06T11:54:38.275Z
Learning: When writing tests in x/evm/keeper/bank_extension_test.go, prefer comparing gas consumption values as formatted strings using fmt.Sprintf("%d", value) instead of numeric values directly in s.Equalf. This avoids hexadecimal representations that are hard to read in shell outputs.

Learnt from: Unique-Divine
PR: #1680
File: x/perp/v2/keeper/msg_server.go:210-210
Timestamp: 2024-10-08T15:45:59.488Z
Learning: The user indicated that error handling for sdk.AccAddressFromBech32(msg.Sender) is covered by prior ValidateBasic checks in the code flow.

Learnt from: Unique-Divine
PR: #1680
File: x/perp/v2/keeper/msg_server.go:210-210
Timestamp: 2024-07-27T01:11:54.422Z
Learning: The user indicated that error handling for sdk.AccAddressFromBech32(msg.Sender) is covered by prior ValidateBasic checks in the code flow.

Learnt from: Unique-Divine
PR: #1680
File: x/perp/v2/keeper/msg_server.go:198-198
Timestamp: 2024-10-08T15:45:59.488Z
Learning: The user Unique-Divine has indicated that the error check for sdk.AccAddressFromBech32(msg.Sender) is not necessary in the context of their code because it is covered by the ValidateBasic method.

x/evm/msg.go (3)

Learnt from: Unique-Divine
PR: #1680
File: x/perp/v2/keeper/msg_server.go:210-210
Timestamp: 2024-10-08T15:45:59.488Z
Learning: The user indicated that error handling for sdk.AccAddressFromBech32(msg.Sender) is covered by prior ValidateBasic checks in the code flow.

Learnt from: Unique-Divine
PR: #1680
File: x/perp/v2/keeper/msg_server.go:210-210
Timestamp: 2024-07-27T01:11:54.422Z
Learning: The user indicated that error handling for sdk.AccAddressFromBech32(msg.Sender) is covered by prior ValidateBasic checks in the code flow.

Learnt from: Unique-Divine
PR: #1680
File: x/perp/v2/keeper/msg_server.go:198-198
Timestamp: 2024-10-08T15:45:59.488Z
Learning: The user Unique-Divine has indicated that the error check for sdk.AccAddressFromBech32(msg.Sender) is not necessary in the context of their code because it is covered by the ValidateBasic method.

x/evm/keeper/funtoken_evm_to_coin_test.go (5)

Learnt from: Unique-Divine
PR: #2119
File: x/evm/keeper/bank_extension_test.go:161-165
Timestamp: 2024-12-06T16:09:01.767Z
Learning: In Go test assertions within x/evm/keeper/bank_extension_test.go, when comparing uint64 values using s.Equalf, prefer formatting the values as decimal strings with fmt.Sprintf("%d", value) to avoid them being printed in hexadecimal.

Learnt from: Unique-Divine
PR: #2119
File: x/evm/keeper/bank_extension_test.go:67-71
Timestamp: 2024-12-06T11:54:38.275Z
Learning: When writing tests in x/evm/keeper/bank_extension_test.go, prefer comparing gas consumption values as formatted strings using fmt.Sprintf("%d", value) instead of numeric values directly in s.Equalf. This avoids hexadecimal representations that are hard to read in shell outputs.

Learnt from: k-yang
PR: #2129
File: x/evm/embeds/contracts/TestInfiniteRecursionERC20.sol:24-32
Timestamp: 2024-12-26T04:43:43.966Z
Learning: The TestInfiniteRecursionERC20 contract is intentionally designed to test infinite recursion scenarios in ERC20-based contracts for the FunToken architecture.

Learnt from: Unique-Divine
PR: #2095
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-10-31T18:28:26.466Z
Learning: When implementing the bankkeeper.Keeper interface in x/evm/keeper/bank_extension.go, methods must use non-pointer receivers to satisfy the interface requirements.

Learnt from: Unique-Divine
PR: #2119
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-12-06T16:10:34.174Z
Learning: In x/evm/keeper/bank_extension.go, assigning a new GasMeter to the context within the NibiruBankKeeper.ForceGasInvariant function is intentional and acceptable. This ensures that subsequent operations do not affect the parent ctx's gas consumption.

x/evm/keeper/msg_server.go (6)

Learnt from: Unique-Divine
PR: #2095
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-10-31T18:28:26.466Z
Learning: When implementing the bankkeeper.Keeper interface in x/evm/keeper/bank_extension.go, methods must use non-pointer receivers to satisfy the interface requirements.

Learnt from: Unique-Divine
PR: #2119
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-12-06T16:10:34.174Z
Learning: In x/evm/keeper/bank_extension.go, assigning a new GasMeter to the context within the NibiruBankKeeper.ForceGasInvariant function is intentional and acceptable. This ensures that subsequent operations do not affect the parent ctx's gas consumption.

Learnt from: Unique-Divine
PR: #2076
File: x/evm/keeper/msg_server.go:115-116
Timestamp: 2024-10-13T05:48:51.771Z
Learning: In the ApplyEvmTx function within x/evm/keeper/msg_server.go, a zero value for weiPerGas is acceptable as it signifies that gas is free. There is no need to check for zero before performing the gas refund in this context.

Learnt from: Unique-Divine
PR: #2119
File: x/evm/keeper/bank_extension_test.go:161-165
Timestamp: 2024-12-06T16:09:01.767Z
Learning: In Go test assertions within x/evm/keeper/bank_extension_test.go, when comparing uint64 values using s.Equalf, prefer formatting the values as decimal strings with fmt.Sprintf("%d", value) to avoid them being printed in hexadecimal.

Learnt from: Unique-Divine
PR: #2097
File: x/oracle/keeper/keeper.go:195-195
Timestamp: 2024-10-31T09:55:25.630Z
Learning: In x/oracle/keeper/keeper.go, within the GetDatedExchangeRate method, it's not possible for exchangeRate.CreatedBlock to be in the future relative to ctx.BlockHeight(), so additional validation before time calculation is unnecessary.

Learnt from: Unique-Divine
PR: #1687
File: x/perp/v2/keeper/amm.go:0-0
Timestamp: 2024-10-08T15:45:59.488Z
Learning: The user has addressed the issue of explicitly handling the case where costAmt is zero in the function handleMarketUpdateCost in the file x/perp/v2/keeper/amm.go.

api/eth/evm/v1/events.pulsar.go (2)

Learnt from: onikonychev
PR: #2101
File: eth/rpc/backend/tx_info.go:144-170
Timestamp: 2024-10-31T16:39:44.773Z
Learning: In the file eth/rpc/backend/tx_info.go, when implementing the MarshalJSON method for TransactionReceipt, using a custom struct for marshaling instead of an intermediate map[string]interface{} does not work.

Learnt from: Unique-Divine
PR: #2095
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-10-31T18:28:26.466Z
Learning: When implementing the bankkeeper.Keeper interface in x/evm/keeper/bank_extension.go, methods must use non-pointer receivers to satisfy the interface requirements.

api/eth/evm/v1/tx.pulsar.go (1)

Learnt from: onikonychev
PR: #2101
File: eth/rpc/backend/tx_info.go:144-170
Timestamp: 2024-10-31T16:39:44.773Z
Learning: In the file eth/rpc/backend/tx_info.go, when implementing the MarshalJSON method for TransactionReceipt, using a custom struct for marshaling instead of an intermediate map[string]interface{} does not work.

🧬 Code Graph Analysis (3)
x/evm/codec.go (2)
api/eth/evm/v1/tx.pulsar.go (9)
  • MsgCreateFunToken (9305-9316)
  • MsgCreateFunToken (9331-9331)
  • MsgCreateFunToken (9334-9336)
  • MsgConvertCoinToEvm (9396-9407)
  • MsgConvertCoinToEvm (9422-9422)
  • MsgConvertCoinToEvm (9425-9427)
  • MsgConvertEvmToCoin (9477-9491)
  • MsgConvertEvmToCoin (9506-9506)
  • MsgConvertEvmToCoin (9509-9511)
x/evm/tx.pb.go (9)
  • MsgCreateFunToken (480-487)
  • MsgCreateFunToken (491-491)
  • MsgCreateFunToken (492-494)
  • MsgConvertCoinToEvm (582-589)
  • MsgConvertCoinToEvm (593-593)
  • MsgConvertCoinToEvm (594-596)
  • MsgConvertEvmToCoin (675-685)
  • MsgConvertEvmToCoin (689-689)
  • MsgConvertEvmToCoin (690-692)
x/evm/cli/cli_test.go (3)
x/evm/cli/cli_setup_test.go (2)
  • Suite (23-32)
  • TestCase (66-71)
x/evm/keeper/grpc_query_test.go (1)
  • TestCase (23-33)
app/server/flags.go (1)
  • Address (13-13)
x/evm/msg_test.go (2)
x/evm/tx.pb.go (3)
  • MsgConvertEvmToCoin (675-685)
  • MsgConvertEvmToCoin (689-689)
  • MsgConvertEvmToCoin (690-692)
eth/eip55.go (1)
  • EIP55Addr (16-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: unit-tests
  • GitHub Check: build
  • GitHub Check: e2e-evm
  • GitHub Check: integration-tests
  • GitHub Check: lint
🔇 Additional comments (26)
go.mod (1)

16-16: LGTM: Dependency update aligns with new EVM features.

The update to the custom Nibiru fork of cosmos-sdk (v0.47.11-nibiru.3) is appropriate for supporting the new convertEvmToCoin functionality introduced in this PR.

x/evm/codec.go (1)

37-39: LGTM: Proper codec registration for new message types.

The registration of MsgCreateFunToken, MsgConvertCoinToEvm, and MsgConvertEvmToCoin as sdk.Msg implementations follows the established pattern and is necessary for protobuf Any packing/unpacking functionality.

proto/eth/evm/v1/events.proto (1)

79-88: LGTM: Well-structured event message for EVM to coin conversion.

The EventConvertEvmToCoin message follows the established pattern of existing event messages and includes appropriate field types and gogoproto annotations. The nullable = false annotation on the bank_coin field is correct since it should always contain a value.

x/evm/cli/tx.go (2)

31-31: LGTM: Proper registration of the new CLI command.

The CmdConvertEvmToCoin() command is correctly added to the commands list alongside existing transaction commands.


128-172: LGTM: Well-implemented CLI command with proper validation.

The CmdConvertEvmToCoin command is well-structured with:

  • Proper argument validation for all three parameters
  • Descriptive error messages for better user experience
  • Consistent pattern with existing CLI commands
  • Appropriate use of SDK validation functions

The validation chain ensures:

  1. ERC20 address format is valid (EIP-55)
  2. Amount is parseable as a valid integer
  3. Recipient address is a valid bech32 account address
x/evm/cli/cli_test.go (1)

52-124: Excellent comprehensive test coverage for the new CLI command.

The test function TestCmdConvertEvmToCoin provides thorough coverage of the new convert-evm-to-coin command with appropriate test cases for both success and failure scenarios. The implementation follows the established patterns in the codebase and uses proper dummy data.

x/evm/msg_test.go (2)

999-1123: Comprehensive validation testing for MsgConvertEvmToCoin.

The TestMsgConvertEvmToCoin_ValidateBasic test provides excellent coverage of all validation scenarios including invalid addresses, empty fields, and invalid amounts. The test structure is consistent with existing patterns in the codebase.


1125-1137: Clean and correct GetSigners test implementation.

The test properly verifies that the GetSigners method returns exactly one signer corresponding to the sender address, following the established pattern for message signer testing.

x/evm/msg.go (2)

37-38: Correct interface assertions for both conversion message types.

The sdk.Msg interface assertions are properly added for both MsgConvertCoinToEvm and MsgConvertEvmToCoin, ensuring compile-time interface compliance.


526-556: Well-implemented message interface methods.

The implementation of GetSigners, ValidateBasic, and GetSignBytes methods for MsgConvertEvmToCoin follows established patterns in the codebase:

  • GetSigners correctly extracts the sender address (error handling is omitted as per established pattern where ValidateBasic handles validation)
  • ValidateBasic provides comprehensive validation of all fields with appropriate error messages
  • GetSignBytes uses the standard JSON marshaling approach
proto/eth/evm/v1/tx.proto (2)

37-40: Clean RPC method definition following established patterns.

The ConvertEvmToCoin RPC method is properly defined with clear documentation, mirroring the structure of the existing ConvertCoinToEvm method for consistency.


294-315: Well-structured message definitions with appropriate field types.

The MsgConvertEvmToCoin message is properly defined with:

  • Clear field documentation
  • Appropriate custom types for EIP55 addresses and math integers
  • Correct field numbering and protobuf annotations
  • Empty response message following established patterns
api/eth/evm/v1/tx.pulsar.go (6)

7668-7943: Auto-generated protobuf code looks correct

The FastReflection implementation for MsgConvertEvmToCoin follows the expected protobuf code generation patterns with proper handling of all four fields.


9476-9565: Message type definitions are well-structured

The MsgConvertEvmToCoin message properly defines all required fields for ERC20 to bank coin conversion, and the empty response type follows standard patterns where results are communicated via events.


9725-9781: Auto-generated protobuf descriptor data


9796-9796: Message type array correctly sized

The array size update to 14 properly accounts for the two new message types.


9810-9839: Type registrations and RPC mappings are correct

The new message types are properly registered and the ConvertEvmToCoin RPC method is correctly mapped with appropriate input/output types.


9995-10029: Reflection exporters properly implemented

The exporters for the new message types follow standard protobuf patterns and the message count is correctly updated.

x/evm/keeper/funtoken_evm_to_coin_test.go (4)

30-159: Well-structured test coverage for coin-originated token conversions!

The test comprehensively covers the conversion flow for coin-originated FunTokens, including:

  • Proper setup with bank metadata and FunToken creation
  • Happy path conversion with balance assertions
  • Error cases for insufficient balance and non-existent mappings
  • Correct use of fresh EVM instances for state assertions

161-319: Excellent test coverage for ERC20-originated token conversions!

The test properly validates:

  • ERC20 token deployment and FunToken creation
  • Required approval mechanism before conversion
  • Balance tracking for sender, recipient, and EVM module
  • Failure case when approval is missing

The approval requirement is critical for security and is well-tested here.


385-406: Good event verification approach with proper attribute checking!

The event verification correctly:

  • Iterates through events to find the specific event type
  • Parses attributes into a map for easy verification
  • Handles quoted attribute values appropriately
  • Validates all expected attributes including serialized bank_coin

The commented EventTxLog check with explanation is acceptable given the timing issues mentioned.


418-501: Comprehensive test for multiple recipient conversions!

The test effectively validates:

  • Sequential conversions to different recipients
  • Correct balance updates for each recipient
  • Accurate remaining ERC20 balance calculation
  • Proper use of fresh EVM instance for state verification
x/evm/keeper/msg_server.go (3)

756-792: Clean implementation of the main conversion handler!

The method properly:

  • Validates addresses using MustAccAddressFromBech32
  • Ensures exactly one FunToken mapping exists
  • Correctly dispatches based on token origin
  • Follows the established pattern from ConvertCoinToEvm

794-882: Correct implementation for coin-originated token conversion!

The method properly:

  • Burns ERC20 tokens using burnFromAuthority (appropriate for module-owned contracts)
  • Transfers equivalent bank coins from module to recipient
  • Manages StateDB lifecycle with proper cleanup
  • Emits both conversion event and EVM logs

884-978: Proper handling of ERC20-originated token conversion!

The method correctly:

  • Transfers ERC20 tokens to EVM module using transferFrom
  • Mints equivalent bank coins to maintain supply invariant
  • Sends minted coins to recipient
  • Manages StateDB and emits appropriate events

Note: This implementation requires the user to have approved the EVM module to spend their tokens before calling this method. The approval check happens within the ERC20 contract's transferFrom method.

api/eth/evm/v1/events.pulsar.go (1)

1-5874: Skip reviewing auto-generated protobuf files

This file is auto-generated from protobuf definitions and should not be manually edited. Any changes should be made to the source .proto files instead.

Copy link
Contributor

@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: 2

♻️ Duplicate comments (2)
x/evm/keeper/msg_server.go (2)

823-824: Consider the sender address in the EVM message.

Based on past feedback, the EVM module address should represent who is executing the burn operation, while the actual token holder (senderEthAddr) is specified as the burn target in the contract call parameters. The current implementation appears correct as the EVM module is authorized to burn tokens from any holder for coin-originated tokens.


928-967: Verify the transferFrom pattern for ERC20-originated tokens.

The implementation uses transferFrom after checking allowance, which is a standard ERC20 pattern. However, this requires users to first approve the EVM module before calling the conversion function.

Consider if this two-step process (approve + convert) aligns with the intended UX, or if there's a need for a different approach like having users call transfer directly to the EVM module address.

The current pattern requires:

  1. User calls approve(EVM_MODULE_ADDRESS, amount) on the ERC20 contract
  2. User calls ConvertEvmToCoin message
  3. EVM module calls transferFrom(user, module, amount)

Please confirm this is the intended flow for ERC20-originated token conversions.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae94ac9 and 335862b.

📒 Files selected for processing (1)
  • x/evm/keeper/msg_server.go (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2315
File: app/upgrades/v2_5_0/v2_5_0.go:0-0
Timestamp: 2025-05-21T21:47:04.451Z
Learning: In the v2.5.0 upgrade handler for Nibiru Chain, excess stNIBI ERC20 token balances (those not belonging to explicitly tracked holders) are sent to a Nibiru team-controlled Gnosis Safe for redistribution to ensure no tokens are lost during contract upgrades.
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2095
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-10-31T18:28:26.466Z
Learning: When implementing the `bankkeeper.Keeper` interface in `x/evm/keeper/bank_extension.go`, methods must use non-pointer receivers to satisfy the interface requirements.
x/evm/keeper/msg_server.go (8)

Learnt from: Unique-Divine
PR: #2095
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-10-31T18:28:26.466Z
Learning: When implementing the bankkeeper.Keeper interface in x/evm/keeper/bank_extension.go, methods must use non-pointer receivers to satisfy the interface requirements.

Learnt from: Unique-Divine
PR: #2119
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-12-06T16:10:34.174Z
Learning: In x/evm/keeper/bank_extension.go, assigning a new GasMeter to the context within the NibiruBankKeeper.ForceGasInvariant function is intentional and acceptable. This ensures that subsequent operations do not affect the parent ctx's gas consumption.

Learnt from: Unique-Divine
PR: #2076
File: x/evm/keeper/msg_server.go:115-116
Timestamp: 2024-10-13T05:48:51.771Z
Learning: In the ApplyEvmTx function within x/evm/keeper/msg_server.go, a zero value for weiPerGas is acceptable as it signifies that gas is free. There is no need to check for zero before performing the gas refund in this context.

Learnt from: Unique-Divine
PR: #2119
File: x/evm/keeper/bank_extension_test.go:161-165
Timestamp: 2024-12-06T16:09:01.767Z
Learning: In Go test assertions within x/evm/keeper/bank_extension_test.go, when comparing uint64 values using s.Equalf, prefer formatting the values as decimal strings with fmt.Sprintf("%d", value) to avoid them being printed in hexadecimal.

Learnt from: Unique-Divine
PR: #1687
File: x/perp/v2/keeper/amm.go:0-0
Timestamp: 2024-10-08T15:45:59.488Z
Learning: The user has addressed the issue of explicitly handling the case where costAmt is zero in the function handleMarketUpdateCost in the file x/perp/v2/keeper/amm.go.

Learnt from: k-yang
PR: #2093
File: x/evm/precompile/funtoken.go:85-87
Timestamp: 2024-11-04T20:38:45.909Z
Learning: In the Run methods of precompiled contracts in x/evm/precompile/, after handling errors from OnRunStart, it's unnecessary to check if err is nil before consuming gas with contract.UseGas(), as err will always be nil at that point.

Learnt from: onikonychev
PR: #2150
File: x/evm/embeds/contracts/TestERC20MaliciousTransfer.sol:36-38
Timestamp: 2025-01-31T07:16:54.084Z
Learning: Mint functions in ERC20 tokens should have access control (e.g., using OpenZeppelin's Ownable) to prevent unauthorized minting. The function should check for owner privileges using require(msg.sender == owner(), "must be owner").

Learnt from: Unique-Divine
PR: #1680
File: x/perp/v2/keeper/msg_server.go:198-198
Timestamp: 2024-10-08T15:45:59.488Z
Learning: The user Unique-Divine has indicated that the error check for sdk.AccAddressFromBech32(msg.Sender) is not necessary in the context of their code because it is covered by the ValidateBasic method.

🪛 GitHub Check: e2e-evm
x/evm/keeper/msg_server.go

[failure] 924-924:
approvalResp.ReturnData undefined (type *evm.MsgEthereumTxResponse has no field or method ReturnData)


[failure] 912-912:
undefined: evmMsg

🪛 GitHub Check: unit-tests
x/evm/keeper/msg_server.go

[failure] 924-924:
approvalResp.ReturnData undefined (type *evm.MsgEthereumTxResponse has no field or method ReturnData)


[failure] 912-912:
undefined: evmMsg


[failure] 924-924:
approvalResp.ReturnData undefined (type *evm.MsgEthereumTxResponse has no field or method ReturnData)


[failure] 912-912:
undefined: evmMsg


[failure] 924-924:
approvalResp.ReturnData undefined (type *evm.MsgEthereumTxResponse has no field or method ReturnData)


[failure] 912-912:
undefined: evmMsg

🪛 GitHub Check: build
x/evm/keeper/msg_server.go

[failure] 924-924:
approvalResp.ReturnData undefined (type *evm.MsgEthereumTxResponse has no field or method ReturnData)


[failure] 912-912:
undefined: evmMsg

🪛 GitHub Actions: Unit Tests
x/evm/keeper/msg_server.go

[error] 912-912: Compilation error: undefined identifier 'evmMsg' at line 912.

🪛 GitHub Actions: EVM E2E tests
x/evm/keeper/msg_server.go

[error] 912-912: undefined: evmMsg

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: integration-tests
🔇 Additional comments (1)
x/evm/keeper/msg_server.go (1)

756-792: LGTM! Well-structured conversion entry point.

The main conversion function correctly handles address parsing, FunToken lookup validation, and delegates appropriately to specialized handlers based on token origin. The error handling for missing or duplicate FunToken mappings is comprehensive.

@codecov
Copy link

codecov bot commented Jul 24, 2025

Codecov Report

❌ Patch coverage is 59.32961% with 364 lines in your changes missing coverage. Please review.
✅ Project coverage is 56.65%. Comparing base (828bddf) to head (d5f217d).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
x/evm/evmtest/test_deps.go 0.00% 88 Missing ⚠️
x/evm/keeper/msg_convert_coin_to_evm.go 75.18% 49 Missing and 19 partials ⚠️
x/evm/keeper/msg_convert_evm_to_coin.go 71.24% 47 Missing and 20 partials ⚠️
x/common/testutil/genesis/genesis.go 0.00% 46 Missing ⚠️
x/evm/evmtest/erc20.go 0.00% 25 Missing ⚠️
app/upgrades/v2_7_0/v2_7_0.go 0.00% 20 Missing ⚠️
x/evm/keeper/msg_update_params.go 0.00% 12 Missing ⚠️
x/evm/msg.go 75.00% 10 Missing and 2 partials ⚠️
x/evm/params.go 38.46% 5 Missing and 3 partials ⚠️
x/evm/keeper/msg_server.go 87.50% 4 Missing and 1 partial ⚠️
... and 7 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2345      +/-   ##
==========================================
+ Coverage   56.51%   56.65%   +0.13%     
==========================================
  Files         330      334       +4     
  Lines       26425    27154     +729     
==========================================
+ Hits        14935    15385     +450     
- Misses      10461    10701     +240     
- Partials     1029     1068      +39     
Files with missing lines Coverage Δ
app/appconst/appconst.go 27.27% <ø> (ø)
app/upgrades.go 80.48% <ø> (ø)
x/evm/codec.go 80.43% <100.00%> (+0.43%) ⬆️
x/evm/embeds/embeds.go 78.94% <100.00%> (+1.16%) ⬆️
x/evm/errors.go 0.00% <ø> (ø)
x/evm/keeper/call_contract.go 100.00% <100.00%> (ø)
x/evm/keeper/erc20.go 74.84% <100.00%> (+1.76%) ⬆️
x/evm/keeper/funtoken_from_coin.go 83.14% <100.00%> (ø)
x/evm/keeper/keeper.go 91.48% <ø> (+4.25%) ⬆️
app/upgrades/v2_5_0/v2_5_0.go 79.20% <50.00%> (ø)
... and 16 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Collaborator

@k-yang k-yang left a comment

Choose a reason for hiding this comment

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

Left a comment on how to skip the approve step

Copy link
Contributor

@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: 0

♻️ Duplicate comments (1)
CHANGELOG.md (1)

52-52: Fix the typo in the GitHub URL for PR #2345.

gittub.com should be github.com to avoid a broken link.

- - [#2345](https://gittub.com/NibiruChain/nibiru/pull/2345) - feat(evm): add convertEvmToCoin functions
+ - [#2345](https://github.com/NibiruChain/nibiru/pull/2345) - feat(evm): add convertEvmToCoin functions
🧹 Nitpick comments (1)
CHANGELOG.md (1)

43-47: Stray tilde markers left in comment block.

The trailing ~ after lines inside the HTML comment appears to be a leftover diff marker and should be removed to keep the comment clean.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b8d1cc and 48d66bb.

📒 Files selected for processing (3)
  • .gitignore (0 hunks)
  • CHANGELOG.md (1 hunks)
  • token-registry/official_erc20s.json (1 hunks)
💤 Files with no reviewable changes (1)
  • .gitignore
✅ Files skipped from review due to trivial changes (1)
  • token-registry/official_erc20s.json
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2315
File: app/upgrades/v2_5_0/v2_5_0.go:0-0
Timestamp: 2025-05-21T21:47:04.451Z
Learning: In the v2.5.0 upgrade handler for Nibiru Chain, excess stNIBI ERC20 token balances (those not belonging to explicitly tracked holders) are sent to a Nibiru team-controlled Gnosis Safe for redistribution to ensure no tokens are lost during contract upgrades.
CHANGELOG.md (1)

Learnt from: Unique-Divine
PR: #2315
File: app/upgrades/v2_5_0/v2_5_0.go:0-0
Timestamp: 2025-05-21T21:47:04.451Z
Learning: In the v2.5.0 upgrade handler for Nibiru Chain, excess stNIBI ERC20 token balances (those not belonging to explicitly tracked holders) are sent to a Nibiru team-controlled Gnosis Safe for redistribution to ensure no tokens are lost during contract upgrades.

🪛 LanguageTool
CHANGELOG.md

[style] ~55-~55: Replace ‘get’ with a more descriptive word to set your writing apart from others.
Context: ...inding generation script robustness and get it to work with a forked Cosmos-SDK dependenc...

(GET_STYLISH)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: lint
  • GitHub Check: integration-tests
  • GitHub Check: proto-gen-rs
  • GitHub Check: e2e-evm
  • GitHub Check: unit-tests
  • GitHub Check: build

@Unique-Divine Unique-Divine changed the title feat: add convertEvmToCoin feat(evm): add "eth.evm.v1.MsgConvertCoinToEvm" tx for ERC20 to bank coin conversions with a non-Ethereum transaction Jul 29, 2025
…rFrom to transfer

This change simplifies the `ConvertEvmToCoin` flow for ERC20-originated tokens
by replacing the use of `transferFrom` with `transfer`. As a result, token
approvals are no longer needed prior to conversion.
Copy link
Contributor

@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

♻️ Duplicate comments (1)
x/evm/keeper/msg_server.go (1)

823-824: Use the actual sender address for burn operation.

The EVM module address should not be the sender for burnFromAuthority call on coin-originated tokens. The actual sender should be used as the From address to properly represent who is initiating the burn operation.

Apply this diff to fix the sender address:

-		From:             evm.EVM_MODULE_ADDRESS,
-		Nonce:            k.GetAccNonce(ctx, evm.EVM_MODULE_ADDRESS),
+		From:             senderEthAddr,
+		Nonce:            k.GetAccNonce(ctx, senderEthAddr),
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54e4b64 and bca8095.

📒 Files selected for processing (3)
  • CHANGELOG.md (1 hunks)
  • x/evm/keeper/funtoken_evm_to_coin_test.go (1 hunks)
  • x/evm/keeper/msg_server.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • x/evm/keeper/funtoken_evm_to_coin_test.go
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2315
File: app/upgrades/v2_5_0/v2_5_0.go:0-0
Timestamp: 2025-05-21T21:47:04.451Z
Learning: In the v2.5.0 upgrade handler for Nibiru Chain, excess stNIBI ERC20 token balances (those not belonging to explicitly tracked holders) are sent to a Nibiru team-controlled Gnosis Safe for redistribution to ensure no tokens are lost during contract upgrades.
x/evm/keeper/msg_server.go (12)

Learnt from: Unique-Divine
PR: #2095
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-10-31T18:28:26.466Z
Learning: When implementing the bankkeeper.Keeper interface in x/evm/keeper/bank_extension.go, methods must use non-pointer receivers to satisfy the interface requirements.

Learnt from: Unique-Divine
PR: #2119
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-12-06T16:10:34.174Z
Learning: In x/evm/keeper/bank_extension.go, assigning a new GasMeter to the context within the NibiruBankKeeper.ForceGasInvariant function is intentional and acceptable. This ensures that subsequent operations do not affect the parent ctx's gas consumption.

Learnt from: Unique-Divine
PR: #2119
File: x/evm/keeper/bank_extension_test.go:161-165
Timestamp: 2024-12-06T16:09:01.767Z
Learning: In Go test assertions within x/evm/keeper/bank_extension_test.go, when comparing uint64 values using s.Equalf, prefer formatting the values as decimal strings with fmt.Sprintf("%d", value) to avoid them being printed in hexadecimal.

Learnt from: Unique-Divine
PR: #2076
File: x/evm/keeper/msg_server.go:115-116
Timestamp: 2024-10-13T05:48:51.771Z
Learning: In the ApplyEvmTx function within x/evm/keeper/msg_server.go, a zero value for weiPerGas is acceptable as it signifies that gas is free. There is no need to check for zero before performing the gas refund in this context.

Learnt from: Unique-Divine
PR: #2097
File: x/oracle/keeper/keeper.go:195-195
Timestamp: 2024-10-31T09:55:25.630Z
Learning: In x/oracle/keeper/keeper.go, within the GetDatedExchangeRate method, it's not possible for exchangeRate.CreatedBlock to be in the future relative to ctx.BlockHeight(), so additional validation before time calculation is unnecessary.

Learnt from: Unique-Divine
PR: #1687
File: x/perp/v2/keeper/amm.go:0-0
Timestamp: 2024-10-08T15:45:59.488Z
Learning: The user has addressed the issue of explicitly handling the case where costAmt is zero in the function handleMarketUpdateCost in the file x/perp/v2/keeper/amm.go.

Learnt from: k-yang
PR: #2093
File: x/evm/precompile/funtoken.go:85-87
Timestamp: 2024-11-04T20:38:45.909Z
Learning: In the Run methods of precompiled contracts in x/evm/precompile/, after handling errors from OnRunStart, it's unnecessary to check if err is nil before consuming gas with contract.UseGas(), as err will always be nil at that point.

Learnt from: onikonychev
PR: #2150
File: x/evm/embeds/contracts/TestERC20MaliciousTransfer.sol:36-38
Timestamp: 2025-01-31T07:16:54.084Z
Learning: Mint functions in ERC20 tokens should have access control (e.g., using OpenZeppelin's Ownable) to prevent unauthorized minting. The function should check for owner privileges using require(msg.sender == owner(), "must be owner").

Learnt from: Unique-Divine
PR: #1680
File: x/perp/v2/keeper/msg_server.go:198-198
Timestamp: 2024-10-08T15:45:59.488Z
Learning: The user Unique-Divine has indicated that the error check for sdk.AccAddressFromBech32(msg.Sender) is not necessary in the context of their code because it is covered by the ValidateBasic method.

Learnt from: Unique-Divine
PR: #2119
File: x/evm/keeper/bank_extension_test.go:67-71
Timestamp: 2024-12-06T11:54:38.275Z
Learning: When writing tests in x/evm/keeper/bank_extension_test.go, prefer comparing gas consumption values as formatted strings using fmt.Sprintf("%d", value) instead of numeric values directly in s.Equalf. This avoids hexadecimal representations that are hard to read in shell outputs.

Learnt from: Unique-Divine
PR: #1680
File: x/perp/v2/keeper/msg_server.go:210-210
Timestamp: 2024-10-08T15:45:59.488Z
Learning: The user indicated that error handling for sdk.AccAddressFromBech32(msg.Sender) is covered by prior ValidateBasic checks in the code flow.

Learnt from: Unique-Divine
PR: #1680
File: x/perp/v2/keeper/msg_server.go:210-210
Timestamp: 2024-07-27T01:11:54.422Z
Learning: The user indicated that error handling for sdk.AccAddressFromBech32(msg.Sender) is covered by prior ValidateBasic checks in the code flow.

CHANGELOG.md (2)

Learnt from: Unique-Divine
PR: #2315
File: app/upgrades/v2_5_0/v2_5_0.go:0-0
Timestamp: 2025-05-21T21:47:04.451Z
Learning: In the v2.5.0 upgrade handler for Nibiru Chain, excess stNIBI ERC20 token balances (those not belonging to explicitly tracked holders) are sent to a Nibiru team-controlled Gnosis Safe for redistribution to ensure no tokens are lost during contract upgrades.

Learnt from: CalicoNino
PR: #2317
File: token-registry/official_erc20s.json:31-31
Timestamp: 2025-05-30T17:11:36.101Z
Learning: In the Nibiru token registry, the USDC (Stargate) token intentionally uses different symbols in different files: "USDC.e" in token-registry/official_erc20s.json and "USDC" in token-registry/cosmos_assetlist.go. This difference is deliberate and should not be flagged as an inconsistency.

🪛 LanguageTool
CHANGELOG.md

[style] ~55-~55: Replace ‘get’ with a more descriptive word to set your writing apart from others.
Context: ...inding generation script robustness and get it to work with a forked Cosmos-SDK dependenc...

(GET_STYLISH)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: e2e-evm
  • GitHub Check: unit-tests
  • GitHub Check: lint
  • GitHub Check: integration-tests
  • GitHub Check: proto-gen-rs
  • GitHub Check: build
🔇 Additional comments (2)
x/evm/keeper/msg_server.go (2)

756-792: LGTM! Well-structured delegation pattern.

The main ConvertEvmToCoin method properly validates the FunToken mapping and delegates to appropriate handlers based on token origin. The implementation follows established patterns in the codebase.


904-944: Verify the transfer approach for ERC20-originated tokens.

The current implementation uses transfer with the sender's Ethereum address as fromAcc in CallContractWithInput. This approach avoids the approval mechanism complexity mentioned in past reviews. However, ensure this correctly represents the user transferring their tokens to the EVM module.

The implementation looks correct as it uses:

  • senderEthAddr as the fromAcc parameter, representing the actual token holder
  • Direct transfer call to move tokens from sender to EVM module
  • Proper error handling and state management

Copy link
Member

@Unique-Divine Unique-Divine left a comment

Choose a reason for hiding this comment

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

  • Handle the case of WNIBI in ConvertEvmToCoin
  • Make ToAddr work the same way is in the IFuntoken.sendToEvm method,
    where the address can be Bech32 or an Eth address.
  • Remove need for approvals by setting the msg sender as the ERC20.transfer sender.
  • There's a bug in the amount of coins transferred in convertEvmToCoinForERC20Originated because the amount of tokens transferred in ERC20.transfer is not the same as the input amount.

Edit: All changes addressed

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bca8095 and 5cc90da.

📒 Files selected for processing (1)
  • CHANGELOG.md (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2315
File: app/upgrades/v2_5_0/v2_5_0.go:0-0
Timestamp: 2025-05-21T21:47:04.451Z
Learning: In the v2.5.0 upgrade handler for Nibiru Chain, excess stNIBI ERC20 token balances (those not belonging to explicitly tracked holders) are sent to a Nibiru team-controlled Gnosis Safe for redistribution to ensure no tokens are lost during contract upgrades.
CHANGELOG.md (2)

Learnt from: Unique-Divine
PR: #2315
File: app/upgrades/v2_5_0/v2_5_0.go:0-0
Timestamp: 2025-05-21T21:47:04.451Z
Learning: In the v2.5.0 upgrade handler for Nibiru Chain, excess stNIBI ERC20 token balances (those not belonging to explicitly tracked holders) are sent to a Nibiru team-controlled Gnosis Safe for redistribution to ensure no tokens are lost during contract upgrades.

Learnt from: CalicoNino
PR: #2317
File: token-registry/official_erc20s.json:31-31
Timestamp: 2025-05-30T17:11:36.101Z
Learning: In the Nibiru token registry, the USDC (Stargate) token intentionally uses different symbols in different files: "USDC.e" in token-registry/official_erc20s.json and "USDC" in token-registry/cosmos_assetlist.go. This difference is deliberate and should not be flagged as an inconsistency.

🪛 LanguageTool
CHANGELOG.md

[style] ~55-~55: Replace ‘get’ with a more descriptive word to set your writing apart from others.
Context: ...inding generation script robustness and get it to work with a forked Cosmos-SDK dependenc...

(GET_STYLISH)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: proto-gen-rs
  • GitHub Check: build
  • GitHub Check: unit-tests
  • GitHub Check: e2e-evm
  • GitHub Check: lint
  • GitHub Check: integration-tests
🔇 Additional comments (3)
CHANGELOG.md (3)

42-47: Unreleased block formatting looks good
Whitespace/comment tweak restores compliance with Keep a Changelog rules.


51-53: Accurate changelog entry for PR #2345
Correct message name and working GitHub URL.


60-61: Extra blank line before “Dependencies” is fine
Improves visual separation of sections.

Copy link
Contributor

@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: 0

♻️ Duplicate comments (1)
CHANGELOG.md (1)

62-62: Broken PR link fixed — thanks

This addresses the earlier 23344→2344 typo.

🧹 Nitpick comments (3)
CHANGELOG.md (3)

59-59: Grammar/clarity nit

Tighten the sentence and avoid “new version for …”.

Apply:

- - [#2334](https://github.com/NibiruChain/nibiru/pull/2334) - feat(evm-embeds): Publish new version for `@nibiruchain/[email protected]`, which updates `NibiruOracleChainLinkLike.sol` to have additional methods used by Aave.
+ - [#2334](https://github.com/NibiruChain/nibiru/pull/2334) - feat(evm-embeds): Publish `@nibiruchain/[email protected]`, updating `NibiruOracleChainLinkLike.sol` with additional methods used by Aave.

60-60: Wrap message name in backticks for consistency with code identifiers

Other code-like terms use backticks; do the same here.

- - [#2345](https://github.com/NibiruChain/nibiru/pull/2345) - feat(evm): add "eth.evm.v1.MsgConvertEvmToCoin" tx for ERC20 to bank coin conversions with a non-Ethereum transaction
+ - [#2345](https://github.com/NibiruChain/nibiru/pull/2345) - feat(evm): add `eth.evm.v1.MsgConvertEvmToCoin` tx for ERC20 to bank coin conversions with a non-Ethereum transaction

63-63: Wording nit: replace “get it to work”

Reads more polished in release notes.

- - [#2346](https://github.com/NibiruChain/nibiru/pull/2346) - fix(buf-gen-rs): improve Rust proto binding generation script robustness and get it to work with a forked Cosmos-SDK dependency and exit correctly on failure
+ - [#2346](https://github.com/NibiruChain/nibiru/pull/2346) - fix(buf-gen-rs): improve Rust proto binding generation script robustness and make it work with a forked Cosmos-SDK dependency and exit correctly on failure
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 57ad7db and 5193bfd.

📒 Files selected for processing (1)
  • CHANGELOG.md (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-05-21T21:47:04.451Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2315
File: app/upgrades/v2_5_0/v2_5_0.go:0-0
Timestamp: 2025-05-21T21:47:04.451Z
Learning: In the v2.5.0 upgrade handler for Nibiru Chain, excess stNIBI ERC20 token balances (those not belonging to explicitly tracked holders) are sent to a Nibiru team-controlled Gnosis Safe for redistribution to ensure no tokens are lost during contract upgrades.

Applied to files:

  • CHANGELOG.md
🪛 LanguageTool
CHANGELOG.md

[grammar] ~59-~59: There might be a mistake here.
Context: ...to have additional methods used by Aave. - [#2345](https://github.com/NibiruChain/ni...

(QB_NEW_EN)


[style] ~63-~63: Replace ‘get’ with a more descriptive word to set your writing apart from others.
Context: ...inding generation script robustness and get it to work with a forked Cosmos-SDK dependenc...

(GET_STYLISH)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: integration-tests
  • GitHub Check: proto-gen-rs
  • GitHub Check: unit-tests
  • GitHub Check: build
  • GitHub Check: lint
  • GitHub Check: e2e-evm
🔇 Additional comments (3)
CHANGELOG.md (3)

42-42: Blank line after Unreleased — LGTM

Improves readability and keeps sections separated.


43-47: Fixed HTML comment opener spacing — LGTM

Removing the stray space before the comment tag is correct and keeps CI happy.


71-71: Blank line before Dependencies — LGTM

Matches surrounding sections and improves scannability.

@Unique-Divine Unique-Divine changed the title feat(evm): add "eth.evm.v1.MsgConvertCoinToEvm" tx for ERC20 to bank coin conversions with a non-Ethereum transaction feat(evm): add "eth.evm.v1.MsgConvertEvmToCoin" tx for ERC20 to bank coin conversions with a non-Ethereum transaction Sep 5, 2025
Copy link
Contributor

@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: 7

Caution

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

⚠️ Outside diff range comments (4)
x/evm/errors.go (1)

13-24: Add a code for the new error.

All module errors use registered codes. Introduce a code for ErrCanonicalWnibi to keep parity with the rest.

 const (
   codeErrInvalidState = uint32(iota) + 2 // NOTE: code 1 is reserved for internal errors
   codeErrZeroAddress
   codeErrInvalidAmount
   codeErrInvalidGasPrice
   codeErrInvalidGasFee
   codeErrInvalidRefund
   codeErrInvalidGasCap
   codeErrInvalidBaseFee
   codeErrInvalidAccount
   codeErrInactivePrecompile
+  codeErrCanonicalWnibi
 )
app/upgrades/v2_5_0/v2_5_0.go (2)

185-191: Nil/contract check needed before dereferencing newErc20Acc

Guard against unexpected deployment failures to avoid panics when reading CodeHash.

Apply this diff:

 newErc20Acc := keepers.EvmKeeper.GetAccount(ctx, newErc20Addr)
+if newErc20Acc == nil || !newErc20Acc.IsContract() {
+  return fmt.Errorf("new ERC20 account not found or not a contract at %s", newErc20Addr.Hex())
+}
 originalErc20Account.CodeHash = newErc20Acc.CodeHash
 err = keepers.EvmKeeper.SetAccount(ctx, originalErc20Addr, *originalErc20Account)
 if err != nil {
   return fmt.Errorf("overwrite of contract bytecode failed: %w", err)
 }

200-213: Remove stale storage entries after copying new contract state
The upgrade only copies new slots under the originalErc20Addr prefix but never purges keys that existed on the old contract and are absent in the new one, which can lead to orphaned allowances or metadata corrupting ERC20 behavior. After the copy loop, track the new keys and then iterate over the original prefix to delete any key not in that set.

Example diff:

   iter := accState.Iterate(ctx, collections.PairRange[gethcommon.Address, gethcommon.Hash]{}.Prefix(newErc20Addr))
   defer iter.Close()
+  newKeys := map[gethcommon.Hash]struct{}{}
   for ; iter.Valid(); iter.Next() {
     k2 := iter.Key().K2()
+    newKeys[k2] = struct{}{}
     accState.Insert(ctx, collections.Join(originalErc20Addr, k2), iter.Value())
   }
+  // Remove stale keys on original
+  oldIter := accState.Iterate(ctx, collections.PairRange[gethcommon.Address, gethcommon.Hash]{}.Prefix(originalErc20Addr))
+  defer oldIter.Close()
+  for ; oldIter.Valid(); oldIter.Next() {
+    k2 := oldIter.Key().K2()
+    if _, ok := newKeys[k2]; !ok {
+      _ = accState.Delete(ctx, collections.Join(originalErc20Addr, k2))
+    }
+  }
x/evm/keeper/msg_server.go (1)

635-657: Remove redundant StateDB commit
In x/evm/keeper/msg_server.go the call

evmResp, err := k.CallContractWithInput(..., true /*commit*/, ...)

already commits stateDB via ApplyEvmMsg. Delete the subsequent explicit commit:

- if err = stateDB.Commit(); err != nil {
-     return nil, sdkioerrors.Wrap(err, "failed to commit stateDB")
- }
♻️ Duplicate comments (1)
x/evm/keeper/msg_convert_evm_to_coin.go (1)

23-26: Fix typo in function comment.

The comment incorrectly references "MsgConvertCoinToEvm" when it should be "MsgConvertEvmToCoin".

 // convertEvmToCoinForCoinOriginated is part of the
-// "eth.evm.v1.MsgConvertCoinToEvm" tx. This function handles conversion of ERC20
+// "eth.evm.v1.MsgConvertEvmToCoin" tx. This function handles conversion of ERC20
 // tokens that were originally bank coins back into coin form. The EVM module
🧹 Nitpick comments (43)
x/evm/vmtracer.go (1)

31-33: Disable debug by default — LGTM; consider a runtime override.

Looks good to quiet logs. Consider wiring an env/config flag to flip this in tests or local dev without code changes.

Also applies to: 58-60

CHANGELOG.md (1)

64-65: Consistency and tone nits.

  • “evm” → “EVM”.
  • Replace “get it to work” with “make it work”.
- - [#2344](https://github.com/NibiruChain/nibiru/pull/2344) - feat(evm): Add some evm messages into the evm codec.
+ - [#2344](https://github.com/NibiruChain/nibiru/pull/2344) - feat(evm): Add some EVM messages into the EVM codec.
- - [#2346](https://github.com/NibiruChain/nibiru/pull/2346) - fix(buf-gen-rs): improve Rust proto binding generation script robustness and get it to work with a forked Cosmos-SDK dependency and exit correctly on failure
+ - [#2346](https://github.com/NibiruChain/nibiru/pull/2346) - fix(buf-gen-rs): improve Rust proto binding generation script robustness and make it work with a forked Cosmos-SDK dependency; exit correctly on failure
x/evm/evmtest/test_deps.go (1)

90-180: WNIBI deployment helper: tighten state moves and assertions.

Solid approach. Two follow-ups:

  • After copying storage from tempWnibiAddr to wnibiAddr, consider removing the temp prefix to avoid stale state that could interfere with reads/tests.
  • Optionally assert the expected deploy address to catch nonce drift.
@@
   evmResp, err := deps.EvmKeeper.CallContractWithInput(
@@
   }
+  // Optional: sanity-check expected deploy address
+  // if evmResp.ContractAddr != tempWnibiAddr { return fmt.Errorf("unexpected deploy addr: got %s want %s", evmResp.ContractAddr.Hex(), tempWnibiAddr.Hex()) }
@@
   {
     iter := evmAccState.Iterate(ctx, collections.PairRange[gethcommon.Address, gethcommon.Hash]{}.Prefix(tempWnibiAddr))
     defer iter.Close()
     for ; iter.Valid(); iter.Next() {
       evmAccState.Insert(
         ctx,
         collections.Join(wnibiAddr, iter.Key().K2()),
         iter.Value(),
       )
+      // Optional: clean up temp storage to prevent stale state in tests.
+      // evmAccState.Remove(ctx, collections.Join(tempWnibiAddr, iter.Key().K2()))
     }
   }

If Bank.StateDB indirection here is relied on for journaling, please confirm no parallel tests touch it; otherwise, consider passing a fresh StateDB instead of mutating the keeper field.

api/eth/evm/v1/tx.pulsar.go (2)

9476-9573: API naming symmetry: consider aligning “to_addr” with existing “to_eth_addr”.

Today: Coin→EVM uses to_eth_addr; EVM→Coin uses to_addr (dual-format). For discoverability, consider either:

  • keep to_addr but document clearly in tx.proto that both hex and bech32 are accepted, or
  • use a oneof { to_eth_addr | to_bech32_addr } to make intent explicit.

Proto change only; regenerate this file afterwards.


9496-9498: Clarify wasm-address caveat relative to this message.

The caveat mentions wasm contracts cannot hold ERC20 (true), but this Tx mints bank coins to to_addr. If to_addr is a wasm bech32 address, that should be safe. Consider rewording in tx.proto to avoid implying this Tx is unsafe with wasm addresses; the asymmetry matters when target is ERC20.

x/evm/precompile/test/export.go (1)

200-201: CallContractWithInput call-sites updated for new arg

Tests pass nil for weiValue consistently; good for queries/execs that don’t use msg.value.

If msg.value ever matters for Wasm precompile, consider adding one test that passes a non-zero value to validate forwarding semantics.

Also applies to: 305-306

x/evm/keeper/erc20.go (3)

79-80: Consistent zero-value ERC20 calls; consider a small helper to reduce repetition

All ERC20 Mint/Transfer/Burn and loaders now pass nil weiValue (i.e., 0), which is correct. To avoid footguns and repeated literals, consider thin helpers for “no-value” calls.

Example (outside this hunk) to centralize the pattern:

// in keeper
func (k *Keeper) callNoValue(ctx sdk.Context, evmObj *vm.EVM, from gethcommon.Address, to *gethcommon.Address, commit bool, input []byte, gas uint64) (*evm.MsgEthereumTxResponse, error) {
	return k.CallContractWithInput(ctx, evmObj, from, to, commit, input, gas, nil)
}

Then here:

return e.callNoValue(ctx, evmObj, sender, &erc20Contract, false, contractInput, getCallGasWithLimit(ctx, Erc20GasLimitExecute))

Also applies to: 105-106, 177-178, 217-218, 261-262, 305-306


33-39: Doc nit: function name mismatch

Comment says “getCallGas returns…” but the function is getCallGasWithLimit. Align the doc to the actual name.

-// getCallGas returns the gas limit for a call to an ERC20 contract following 63/64 rule (EIP-150)
+// getCallGasWithLimit returns the gas limit for a call to an ERC20 contract following the 63/64 rule (EIP-150)

152-159: Typo in comment

“amaount” → “amount”.

-// TotalSupply returns the amaount of ERC20 tokens in existence.
+// TotalSupply returns the amount of ERC20 tokens in existence.
app/upgrades/v2_5_0/v2_5_0.go (1)

183-191: Log code hashes as hex, not raw bytes

Current string() prints unreadable/binary data. Prefer hex for diagnostics.

-fmt.Println("old code hash", string(originalErc20Account.CodeHash))
+fmt.Printf("old code hash %x\n", originalErc20Account.CodeHash)
 ...
-fmt.Println("new code hash", string(newErc20Acc.CodeHash))
+fmt.Printf("new code hash %x\n", newErc20Acc.CodeHash)
x/evm/keeper/call_contract.go (1)

30-31: Docstring is stale about gas limit.

It says “uses a default gas limit” but the function takes gasLimit.

-// Note: This function handles both contract method calls and simulations,
-// depending on the 'commit' parameter. It uses a default gas limit.
+// Note: This function handles both contract method calls and simulations,
+// depending on the 'commit' parameter. The provided gasLimit is used as-is.
x/evm/keeper/msg_convert_coin_to_evm.go (7)

25-31: Fix error prefix for insufficient funds.

Wrong flow name in the message.

-		err = fmt.Errorf(
-			"ConvertEvmToCoin: insufficient funds to send WNIBI, balance %s, msg.BankCoin %s", balMicronibi, msg.BankCoin,
-		)
+		err = fmt.Errorf("ConvertCoinToEvm: insufficient funds to send WNIBI, balance %s, msg.BankCoin %s",
+			balMicronibi, msg.BankCoin)

72-73: Typo in error message (“despoit”).

-		err = fmt.Errorf("ABI packing error in WNIBI.despoit: %w", err)
+		err = fmt.Errorf("ABI packing error in WNIBI.deposit: %w", err)

98-103: Fix error prefix when querying balance.

-		err = fmt.Errorf("ConvertEvmToCoin: failed to query ERC20 balance: %w", err)
+		err = fmt.Errorf("ConvertCoinToEvm: failed to query ERC20 balance: %w", err)

114-120: Correct direction in error messages for deposit.

It’s a NIBI→WNIBI deposit.

-		return resp, fmt.Errorf("failed to convert WNIBI to NIBI: %w", err)
+		return resp, fmt.Errorf("WNIBI deposit failed: %w", err)
@@
-		err = fmt.Errorf("failed to convert WNIBI to NIBI: VmError: %s", evmResp.VmError)
+		err = fmt.Errorf("WNIBI deposit failed: VmError: %s", evmResp.VmError)

121-126: Fix error prefix when querying post-deposit balance.

-		err = fmt.Errorf("ConvertEvmToCoin: failed to query ERC20 balance: %w", err)
+		err = fmt.Errorf("ConvertCoinToEvm: failed to query ERC20 balance: %w", err)

132-135: Comment typo (“tranfers”).

-// STEP 2: Sender tranfers WNIBI to intended recipient
+// STEP 2: Sender transfers WNIBI to intended recipient

143-147: Correct direction and typos in transfer errors.

-		return resp, fmt.Errorf("failed to convert WNIBI to NIBI: %w", err)
+		return resp, fmt.Errorf("WNIBI transfer failed: %w", err)
@@
-		err = fmt.Errorf("failed to convert WNIBI to NIBI: VmError: %s", evmResp.VmError)
+		err = fmt.Errorf("WNIBI transfer failed: VmError: %s", evmResp.VmError)
@@
-			"ConvertCoinToEvm: transfer of WNIBI succeeded but did not deliver the correct number of tokens: transfer amount %s, balance increase %s, senderHex %s, receipient %s",
+			"ConvertCoinToEvm: transfer of WNIBI succeeded but did not deliver the correct number of tokens: transfer amount %s, balance increase %s, senderHex %s, recipient %s",

Also applies to: 150-155

x/evm/evmtest/erc20.go (3)

124-131: Nice helper type; consider documenting zero/absent ERC20 case.
Document that BalanceERC20=nil skips the ERC20 assertion; if non-nil and CanonicalWnibi is unset, the helper will fail on BalanceOf. Clarify expected behavior in such networks.


132-151: Handle EVM creation errors in tests.
deps.NewEVM() error is ignored; assert it to avoid silent failures.

Apply this diff:

-    } else {
-        evmObj, _ = deps.NewEVM()
+    } else {
+        var err error
+        evmObj, err = deps.NewEVM()
+        assert.NoError(t, err, "NewEVM failed")
     }

145-151: Prefer direct address comparison.
When checking CanonicalWnibi, compare addresses rather than hex strings to avoid case/encoding pitfalls.

Example:

erc20 := deps.EvmKeeper.GetParams(deps.Ctx).CanonicalWnibi.Address
x/evm/keeper/msg_convert_coin_to_evm_test.go (3)

66-88: Fragile assertions on EventTxLog dynamic fields.
Hard-coding BlockNumber/TxHash/Index can flake as internals evolve. Prefer asserting address, topics, and data only.

Example approach: fetch evm.EventTxLog from ctx and check first log fields without asserting block/tx metadata.


293-331: Gas comparison is brittle.
Requiring SDK gas > EVM gas could fail with benign changes. Assert both are non-zero and that SDK gas >= EVM gas, or just non-zero.

Apply this diff:

- s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM")
+ s.Require().GreaterOrEqualf(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed, "cosmos gas should be >= EVM gas")

743-764: Misleading error prefix for WNIBI coin→EVM path.
The expected error contains “ConvertEvmToCoin…”, but the code path is ConvertCoinToEvmForWNIBI. Standardize the message (recommend “ConvertCoinToEvm: insufficient funds to send WNIBI”) or relax the test to assert only the “insufficient funds to send WNIBI” substring.

Apply this diff to the test for robustness:

- s.Require().ErrorContains(err, "ConvertEvmToCoin: insufficient funds to send WNIBI")
+ s.Require().ErrorContains(err, "insufficient funds to send WNIBI")
x/evm/keeper/msg_server.go (2)

779-825: ConvertEvmToCoin: solid routing; prefer address equality over Hex().
Compare geth addresses directly to avoid checksum/case issues.

Apply this diff:

- if erc20.Hex() == evmParams.CanonicalWnibi.Hex() {
+ if erc20.Address == evmParams.CanonicalWnibi.Address {

807-813: Improve mapping error message.
Return count to aid diagnostics when duplicates exist.

- err = fmt.Errorf("no FunToken mapping exists for ERC20 \"%s\"", erc20.Hex())
+ err = fmt.Errorf("expected 1 FunToken mapping for ERC20 %s, found %d", erc20.Hex(), len(funTokens))
x/evm/keeper/funtoken_from_coin_test.go (4)

81-90: Potential nonce-derived address flakiness.

You pre-compute expectedErc20Addr from the module account nonce before CreateFunToken. If any prior EVM tx from the module increments the nonce, this assertion could flake. Consider scoping this test to guarantee no earlier module EVM tx or derive/compare the deployed address only after CreateFunToken.


140-168: Prefer typed event assertion over manual topic hashing.

Manual EventTxLog topic construction is brittle. Use the existing typed event helper for stability.

Apply:

- testutil.RequireContainsTypedEvent(
-   s.T(),
-   deps.Ctx,
-   &evm.EventTxLog{
-     Logs: []evm.Log{ ... },
-   },
- )
+testutil.RequireContainsTypedEvent(
+  s.T(),
+  deps.Ctx,
+  &evm.EventTxLog{}, // presence is sufficient; decode and assert fields selectively if needed
+)

197-208: Fix units in comments for clarity.

The narrative mixes “1” vs “1e6”. Use explicit magnitudes to avoid confusion.

-//   - Test contract funds: 0 EVM
-//   - Alice: 1 EVM, 9 BC
-//   - Module account: 1 BC escrowed (which Alice holds as 1 EVM)
+//   - Test contract funds: 0 ERC20
+//   - Alice: 1,000,000 ERC20, 9,000,000 Bank
+//   - Module account: 1,000,000 Bank escrowed (Alice holds 1,000,000 as ERC20)

270-279: New CallContractWithInput arg.

Passing nil for the new trailing param is consistent with the updated signature. Consider a brief comment indicating it’s the optional value/override for readability.

x/evm/precompile/funtoken_test.go (3)

83-98: Use test logger instead of fmt.Printf.

Avoid stdout noise; prefer t.Logf.

- fmt.Printf("arg: %s", arg)
+ t.Logf("arg: %s", arg)

443-453: Minor: expand subtest name.

Clarify the recipient context in the title for quick scanning in CI logs.

-s.Run("Sending 400 tokens back from EVM to Cosmos bank => recipient:", func() {
+s.Run("sendToBank: send 400 from EVM to random Cosmos recipient", func() {

571-575: Use bigTokens helper for 18-dec amounts.

Consistent units improve readability in ERC20-originated tests.

- big.NewInt(1000)
+ bigTokens(1000)
x/evm/keeper/msg_convert_evm_to_coin_test.go (3)

185-229: ERC20-originated: unit consistency suggestion.

convertAmount is raw units (not human 18-dec). Consider using bigTokens helper to make intent obvious, or add a comment that amounts are in raw ERC20 units.

- convertAmount := sdkmath.NewInt(100000)
+ // Amounts are in raw ERC20 units (18 decimals). 100k raw = 1e5.
+ convertAmount := sdkmath.NewInt(100_000) // raw units

231-290: Subtest naming mismatch.

Title says “no approval to transfer” (sad path) but the flow intends success because the call origin is the sender. Rename for clarity.

-s.Run("sad: no approval to transfer", func() {
+s.Run("no ERC20 approve required: call originates from sender", func() {

356-378: Use typed event assertion instead of scanning attributes-as-strings.

Manual string/quote handling is brittle. Prefer testutil.RequireContainsTypedEvent with the generated event type and then assert fields.

- foundEvent := false
- for _, event := range deps.Ctx.EventManager().Events() {
-   if event.Type == "eth.evm.v1.EventConvertEvmToCoin" {
-     foundEvent = true
-     ...
-   }
- }
- s.Require().True(foundEvent, "EventConvertEvmToCoin not found")
+ testutil.RequireContainsTypedEvent(
+   s.T(),
+   deps.Ctx,
+   &evm.EventConvertEvmToCoin{
+     Sender:               deps.Sender.NibiruAddr.String(),
+     Erc20ContractAddress: erc20Addr.String(),
+     ToAddress:            toAddr.String(),
+     // BankCoin: check denom and amount only if needed
+   },
+ )
x/evm/msg.go (1)

513-520: Improved error message clarity.

The updated error messages now include a descriptive prefix "error in MsgConvertCoinToEvm" which helps with debugging and log analysis.

For consistency, consider using the same error wrapping pattern across all validation methods:

-		return fmt.Errorf("error in MsgConvertCoinToEvm: invalid sender addr: %w", err)
+		return sdkioerrors.Wrapf(err, "error in MsgConvertCoinToEvm: invalid sender addr")
x/evm/keeper/msg_convert_evm_to_coin.go (1)

290-291: Consider using more descriptive variable names in error message.

The error message could be clearer with formatted values showing the units.

-		err = fmt.Errorf("WNIBI withdraw failed: withdraw amount %s, balBefore %s, balAfter %s", withdrawWei, wnibiBalBefore, wnibiBalAfter)
+		err = fmt.Errorf("WNIBI withdraw failed: expected withdraw %s wei, balance before %s wei, balance after %s wei", withdrawWei, wnibiBalBefore, wnibiBalAfter)
x/evm/evmmodule/genesis_test.go (5)

59-61: Don't ignore NewEVM error; fail fast in tests.

Capture and assert the error to avoid silent misconfigurations.

-evmObj, _ := deps.NewEVM()
+evmObj, err := deps.NewEVM()
+s.Require().NoError(err)

125-131: *Typo and redundant &; pass the correct value directly.

Rename bankGensisState → bankGenesisState and drop the no-op *&.

-authGenesisState := deps.App.AccountKeeper.ExportGenesis(deps.Ctx)
-bankGensisState := deps.App.BankKeeper.ExportGenesis(deps.Ctx)
+authGenesisState := deps.App.AccountKeeper.ExportGenesis(deps.Ctx)
+bankGenesisState := deps.App.BankKeeper.ExportGenesis(deps.Ctx)
@@
 deps = evmtest.NewTestDeps()
 deps.App.AccountKeeper.InitGenesis(deps.Ctx, *authGenesisState)
-deps.App.BankKeeper.InitGenesis(deps.Ctx, *&bankGensisState)
+deps.App.BankKeeper.InitGenesis(deps.Ctx, bankGenesisState)

74-87: Fix typos in log and comment.

  • “er20” → “ERC20”
  • Duplicate “needs” in comment.
-s.T().Logf("Create FunToken from the er20 %s", erc20Addr)
+s.T().Logf("Create FunToken from the ERC20 %s", erc20Addr)
@@
-// There's a fee for "evm.MsgCreateFunToken". The sender account needs
-// needs funds for that.
+// There's a fee for "evm.MsgCreateFunToken". The sender account needs funds for that.

142-147: Avoid int64 truncation; use big.Int arithmetic for expectations.

Using Int64() then adding can overflow/truncate for large amounts.

-s.Require().Equal(
-  new(big.Int).Sub(totalSupply, big.NewInt(amountToSendA.Int64()+amountToSendB.Int64())),
-  balance,
-)
+sumAB := new(big.Int).Add(new(big.Int).Set(amountToSendA), amountToSendB)
+expected := new(big.Int).Sub(new(big.Int).Set(totalSupply), sumAB)
+s.Require().Equal(expected, balance)

154-155: Normalize address comparison to a single representation.

Use the same formatter on both sides to avoid case/checksum mismatches.

-s.Equal(ft.Erc20Addr.Hex(), funTokens[0].Erc20Addr.String())
+s.Equal(ft.Erc20Addr.String(), funTokens[0].Erc20Addr.String())
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5193bfd and b5d3b17.

⛔ Files ignored due to path filters (1)
  • x/evm/tx.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (31)
  • CHANGELOG.md (2 hunks)
  • api/eth/evm/v1/tx.pulsar.go (6 hunks)
  • app/appconst/appconst.go (2 hunks)
  • app/upgrades.go (2 hunks)
  • app/upgrades/v2_5_0/v2_5_0.go (3 hunks)
  • app/upgrades/v2_5_0/v2_5_0_test.go (1 hunks)
  • app/upgrades/v2_7_0/v2_7_0.go (1 hunks)
  • eth/eip55.go (1 hunks)
  • proto/eth/evm/v1/tx.proto (11 hunks)
  • x/evm/errors.go (1 hunks)
  • x/evm/evmmodule/genesis_test.go (5 hunks)
  • x/evm/evmtest/erc20.go (3 hunks)
  • x/evm/evmtest/test_deps.go (3 hunks)
  • x/evm/keeper/call_contract.go (1 hunks)
  • x/evm/keeper/erc20.go (7 hunks)
  • x/evm/keeper/funtoken_from_coin.go (1 hunks)
  • x/evm/keeper/funtoken_from_coin_test.go (6 hunks)
  • x/evm/keeper/funtoken_from_erc20_test.go (15 hunks)
  • x/evm/keeper/keeper_test.go (1 hunks)
  • x/evm/keeper/msg_convert_coin_to_evm.go (1 hunks)
  • x/evm/keeper/msg_convert_coin_to_evm_test.go (1 hunks)
  • x/evm/keeper/msg_convert_evm_to_coin.go (1 hunks)
  • x/evm/keeper/msg_convert_evm_to_coin_test.go (1 hunks)
  • x/evm/keeper/msg_server.go (5 hunks)
  • x/evm/msg.go (7 hunks)
  • x/evm/precompile/funtoken_test.go (22 hunks)
  • x/evm/precompile/oracle_test.go (2 hunks)
  • x/evm/precompile/test/export.go (2 hunks)
  • x/evm/precompile/wasm_test.go (10 hunks)
  • x/evm/statedb/journal_test.go (2 hunks)
  • x/evm/vmtracer.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • eth/eip55.go
  • proto/eth/evm/v1/tx.proto
🧰 Additional context used
🧠 Learnings (13)
📚 Learning: 2024-10-31T18:28:26.466Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2095
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-10-31T18:28:26.466Z
Learning: When implementing the `bankkeeper.Keeper` interface in `x/evm/keeper/bank_extension.go`, methods must use non-pointer receivers to satisfy the interface requirements.

Applied to files:

  • x/evm/keeper/msg_convert_coin_to_evm.go
  • x/evm/keeper/msg_convert_evm_to_coin.go
📚 Learning: 2025-01-17T17:32:27.341Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2165
File: x/evm/evmmodule/genesis_test.go:51-51
Timestamp: 2025-01-17T17:32:27.341Z
Learning: The `NewEVM()` method in TestDeps returns only `*vm.EVM` without an error value.

Applied to files:

  • x/evm/evmtest/test_deps.go
📚 Learning: 2025-01-17T17:32:33.412Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2165
File: x/evm/keeper/funtoken_from_coin_test.go:30-30
Timestamp: 2025-01-17T17:32:33.412Z
Learning: In test dependencies, `deps.NewEVM()` returns both an EVM object and a StateDB object with the signature `(*vm.EVM, *statedb.StateDB)`. The method is used in test files to create new EVM and StateDB instances for testing purposes.

Applied to files:

  • x/evm/evmtest/test_deps.go
📚 Learning: 2025-01-17T17:32:27.341Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2165
File: x/evm/evmmodule/genesis_test.go:51-51
Timestamp: 2025-01-17T17:32:27.341Z
Learning: The `NewEVM()` method in TestDeps returns `(*vm.EVM, *statedb.StateDB)` without an error value. The second return value is often discarded when only the EVM object is needed.

Applied to files:

  • x/evm/evmtest/test_deps.go
📚 Learning: 2025-01-17T17:32:33.412Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2165
File: x/evm/keeper/funtoken_from_coin_test.go:30-30
Timestamp: 2025-01-17T17:32:33.412Z
Learning: In test dependencies, `deps.NewEVM()` returns only an EVM object and does not return an error. The method is used in test files to create a new EVM instance for testing purposes.

Applied to files:

  • x/evm/evmtest/test_deps.go
📚 Learning: 2024-12-06T11:54:38.275Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2119
File: x/evm/keeper/bank_extension_test.go:67-71
Timestamp: 2024-12-06T11:54:38.275Z
Learning: When writing tests in `x/evm/keeper/bank_extension_test.go`, prefer comparing gas consumption values as formatted strings using `fmt.Sprintf("%d", value)` instead of numeric values directly in `s.Equalf`. This avoids hexadecimal representations that are hard to read in shell outputs.

Applied to files:

  • x/evm/evmtest/test_deps.go
  • x/evm/precompile/funtoken_test.go
📚 Learning: 2024-12-06T16:09:01.767Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2119
File: x/evm/keeper/bank_extension_test.go:161-165
Timestamp: 2024-12-06T16:09:01.767Z
Learning: In Go test assertions within `x/evm/keeper/bank_extension_test.go`, when comparing `uint64` values using `s.Equalf`, prefer formatting the values as decimal strings with `fmt.Sprintf("%d", value)` to avoid them being printed in hexadecimal.

Applied to files:

  • x/evm/evmtest/test_deps.go
  • x/evm/precompile/funtoken_test.go
  • x/evm/evmmodule/genesis_test.go
  • x/evm/keeper/funtoken_from_coin_test.go
📚 Learning: 2025-05-21T21:47:04.451Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2315
File: app/upgrades/v2_5_0/v2_5_0.go:0-0
Timestamp: 2025-05-21T21:47:04.451Z
Learning: In the v2.5.0 upgrade handler for Nibiru Chain, excess stNIBI ERC20 token balances (those not belonging to explicitly tracked holders) are sent to a Nibiru team-controlled Gnosis Safe for redistribution to ensure no tokens are lost during contract upgrades.

Applied to files:

  • app/upgrades/v2_5_0/v2_5_0.go
  • CHANGELOG.md
📚 Learning: 2024-12-26T04:43:43.966Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2129
File: x/evm/embeds/contracts/TestInfiniteRecursionERC20.sol:24-32
Timestamp: 2024-12-26T04:43:43.966Z
Learning: The `TestInfiniteRecursionERC20` contract is intentionally designed to test infinite recursion scenarios in ERC20-based contracts for the `FunToken` architecture.

Applied to files:

  • x/evm/keeper/funtoken_from_erc20_test.go
📚 Learning: 2024-10-13T05:48:51.771Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2076
File: x/evm/keeper/msg_server.go:115-116
Timestamp: 2024-10-13T05:48:51.771Z
Learning: In the `ApplyEvmTx` function within `x/evm/keeper/msg_server.go`, a zero value for `weiPerGas` is acceptable as it signifies that gas is free. There is no need to check for zero before performing the gas refund in this context.

Applied to files:

  • x/evm/keeper/funtoken_from_erc20_test.go
  • x/evm/precompile/funtoken_test.go
  • x/evm/keeper/msg_server.go
📚 Learning: 2024-11-04T20:38:45.909Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2093
File: x/evm/precompile/funtoken.go:85-87
Timestamp: 2024-11-04T20:38:45.909Z
Learning: In the `Run` methods of precompiled contracts in `x/evm/precompile/`, after handling errors from `OnRunStart`, it's unnecessary to check if `err` is nil before consuming gas with `contract.UseGas()`, as `err` will always be nil at that point.

Applied to files:

  • x/evm/keeper/funtoken_from_erc20_test.go
  • x/evm/precompile/funtoken_test.go
📚 Learning: 2024-10-08T15:45:59.488Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#1680
File: x/perp/v2/keeper/msg_server.go:210-210
Timestamp: 2024-10-08T15:45:59.488Z
Learning: The user indicated that error handling for `sdk.AccAddressFromBech32(msg.Sender)` is covered by prior `ValidateBasic` checks in the code flow.

Applied to files:

  • x/evm/msg.go
📚 Learning: 2024-10-08T15:45:59.488Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#1680
File: x/perp/v2/keeper/msg_server.go:198-198
Timestamp: 2024-10-08T15:45:59.488Z
Learning: The user Unique-Divine has indicated that the error check for `sdk.AccAddressFromBech32(msg.Sender)` is not necessary in the context of their code because it is covered by the `ValidateBasic` method.

Applied to files:

  • x/evm/msg.go
🧬 Code graph analysis (7)
app/upgrades/v2_7_0/v2_7_0.go (3)
app/upgrades/v2_6_0/v2_6_0.go (2)
  • Upgrade (11-15)
  • UpgradeName (9-9)
app/keepers/all_keepers.go (1)
  • PublicKeepers (35-75)
eth/eip55.go (1)
  • EIP55Addr (17-19)
x/evm/keeper/funtoken_from_coin.go (2)
x/evm/const.go (1)
  • EVM_MODULE_ADDRESS (106-106)
x/evm/keeper/erc20.go (1)
  • Erc20GasLimitDeploy (22-22)
x/evm/keeper/msg_convert_evm_to_coin_test.go (8)
eth/eip55.go (1)
  • EIP55Addr (17-19)
x/evm/evmtest/eth.go (1)
  • NewEthPrivAcc (27-37)
x/evm/evmtest/tx.go (2)
  • DeployContract (58-100)
  • TxTransferWei (186-191)
x/evm/embeds/embeds.go (2)
  • SmartContract_TestERC20 (101-104)
  • SmartContract_WNIBI (96-99)
x/evm/const.go (2)
  • EVM_MODULE_ADDRESS (106-106)
  • NativeToWei (123-126)
x/evm/keeper/erc20.go (1)
  • Erc20GasLimitExecute (30-30)
x/evm/params.go (1)
  • EVMBankDenom (25-25)
x/common/testutil/testapp/testapp.go (1)
  • FundFeeCollector (183-192)
x/evm/precompile/funtoken_test.go (3)
x/common/testutil/testapp/testapp.go (1)
  • FundAccount (156-165)
x/evm/const.go (1)
  • EVM_MODULE_ADDRESS (106-106)
eth/eth_account.go (1)
  • NibiruAddrToEthAddr (19-21)
x/evm/keeper/funtoken_from_coin_test.go (3)
x/evm/tx.pb.go (3)
  • MsgConvertCoinToEvm (582-589)
  • MsgConvertCoinToEvm (593-593)
  • MsgConvertCoinToEvm (594-596)
x/evm/evm.pb.go (3)
  • FunToken (32-41)
  • FunToken (45-45)
  • FunToken (46-48)
x/common/testutil/testapp/testapp.go (1)
  • FundAccount (156-165)
x/evm/keeper/msg_server.go (4)
x/evm/keeper/keeper.go (1)
  • Keeper (25-52)
x/evm/tx.pb.go (3)
  • MsgConvertEvmToCoin (675-693)
  • MsgConvertEvmToCoin (697-697)
  • MsgConvertEvmToCoin (698-700)
eth/eth_account.go (1)
  • NibiruAddrToEthAddr (19-21)
x/evm/statedb/statedb.go (1)
  • StateDB (45-85)
x/evm/msg.go (4)
x/evm/tx.pb.go (6)
  • MsgConvertEvmToCoin (675-693)
  • MsgConvertEvmToCoin (697-697)
  • MsgConvertEvmToCoin (698-700)
  • MsgEthereumTx (40-51)
  • MsgEthereumTx (55-55)
  • MsgEthereumTx (56-58)
eth/eip55.go (2)
  • EIP55Addr (17-19)
  • NewEIP55AddrFromStr (22-35)
eth/eth_account.go (2)
  • EthAddrToNibiruAddr (15-17)
  • NibiruAddrToEthAddr (19-21)
x/evm/codec.go (1)
  • AminoCdc (18-18)
🪛 LanguageTool
CHANGELOG.md

[grammar] ~61-~61: There might be a mistake here.
Context: ...to have additional methods used by Aave. - [#2345](https://github.com/NibiruChain/ni...

(QB_NEW_EN)


[style] ~65-~65: Replace ‘get’ with a more descriptive word to set your writing apart from others.
Context: ...inding generation script robustness and get it to work with a forked Cosmos-SDK dependenc...

(GET_STYLISH)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: unit-tests
  • GitHub Check: proto-gen-rs
  • GitHub Check: integration-tests
  • GitHub Check: build
  • GitHub Check: lint
  • GitHub Check: e2e-evm
🔇 Additional comments (64)
x/evm/precompile/wasm_test.go (1)

65-65: CallContractWithInput(nil) is safe
The keeper’s implementation checks for a nil weiValue and defaults to big.NewInt(0), so passing nil can’t cause a nil-dereference and correctly represents zero value.

x/evm/evmtest/test_deps.go (1)

64-66: Less-verbose EVM logger — LGTM.

api/eth/evm/v1/tx.pulsar.go (5)

8280-8634: Empty response type is fine; ensure events surface conversion outcomes.

If clients rely on events to learn minted bank coins/recipient, confirm EventConvertEvmToCoin is emitted with sufficient details and tests assert it.


9729-9778: Registration and service wiring changes look consistent.

  • Raw descriptor updated with the new RPC.
  • NumMessages bumped to 14; goTypes/depIdxs entries align with indices 12/13 for the new messages.

Also applies to: 9802-9843


10001-10024: Exporter hooks and TypeBuilder counts updated correctly.

No issues spotted.

Also applies to: 10032-10044


7668-7683: Generated file: avoid manual edits.

If anything changes, update proto/eth/evm/v1/tx.proto and re-run codegen (buf/protoc) to keep this artifact stable.


7668-8278: Verify signer annotation and message validation

  • Ensure the MsgConvertEvmToCoin proto has option (cosmos.msg.v1.signer) = "sender" inside its message definition (not a global “authority”)
  • Confirm MsgConvertEvmToCoin.Validate() (called by ValidateBasic) enforces:
    • erc20_addr is a 20-byte EVM address (EIP-55 or equivalent)
    • amount is a positive, non-zero integer
    • to_addr only allows permitted Cosmos/WASM mappings and rejects ambiguous contract addresses
  • Verify Keeper.ConvertEvmToCoin relies solely on msg.Validate() for these checks, and no additional keeper‐level safeguards are required
app/upgrades/v2_5_0/v2_5_0_test.go (1)

182-183: API update acknowledged: passing nil weiValue for a view call is correct

The signature change is reflected properly here; nil maps to 0 value for a read-only query.

x/evm/keeper/funtoken_from_coin.go (1)

110-112: Zero-value deploy: explicit default looks fine

Using nil for weiValue keeps the deploy at 0-value, which matches the constructed evmMsg.Value. No issues.

app/upgrades.go (1)

33-34: Wired v2_7_0 upgrade in order

Import and append look correct. Ensure the on-chain schedule places v2_7_0 after v2_6_0 by height as expected.

Also applies to: 52-53

x/evm/precompile/oracle_test.go (2)

71-80: Updated CallContractWithInput signature: OK

Passing nil for the new trailing arg keeps behavior unchanged here.


137-146: Second call site updated correctly

The extra nil aligns with the new signature; no functional change to this test.

x/evm/keeper/keeper_test.go (2)

27-29: New SuiteFunToken type: OK

Provides a clean receiver for FunToken tests used elsewhere.


18-21: Verified no duplicate TestAll entrypoints; other TestSuite definitions live in separate packages.

x/evm/statedb/journal_test.go (2)

41-51: Adaptation to new CallContractWithInput param: OK

nil for value is appropriate for this mint flow.


107-116: Adaptation to new CallContractWithInput param: OK

Consistent with zero-wei call semantics.

app/upgrades/v2_5_0/v2_5_0.go (3)

20-20: Use centralized mainnet constants

Importing appconst reduces drift in hardcoded addresses.


44-45: Prefer appconst.MAINNET_STNIBI_ADDR

Good replacement for the local var; fewer single-source-of-truth risks.


159-163: New CallContractWithInput arg: OK

nil value is correct for a no-value CREATE.

app/appconst/appconst.go (2)

13-14: Importing gethcommon: OK

Required for typed addresses; keeps call sites clean.


31-38: Mainnet address constants: OK

Exported typed addresses are helpful. Nice docstrings.

x/evm/evmtest/erc20.go (3)

14-14: LGTM: appconst import fits usage.


164-169: LGTM: clearer balance labels.


155-159: All FunTokenBalanceAssert literals use named fields; no positional initializations found.

x/evm/keeper/msg_convert_coin_to_evm_test.go (2)

398-399: No action needed: evmtest.DefaultEthCallGasLimit is defined in x/evm/evmtest/tx.go as an alias of srvconfig.DefaultEthCallGasLimit (it ultimately originates from app/server/config/server_config.go), so compilation will succeed.


769-801: Ignore type mismatch suggestion. ErrCanonicalWnibi is declared as a string constant, so s.Require().ErrorContains(err, evm.ErrCanonicalWnibi) is correct.

Likely an incorrect or invalid review comment.

x/evm/keeper/msg_server.go (2)

551-564: Good: early validation and sender decoding.


559-563: No changes needed: convertCoinToEvmForWNIBI already enforces “well-defined” WNIBI
The function immediately calls

if stateDB.GetCodeSize(erc20.Address) == 0 {
    return fmt.Errorf(…)
}

which guarantees non-empty bytecode (and thus a valid, non-zero contract) before proceeding.

x/evm/keeper/funtoken_from_coin_test.go (4)

21-21: Suite rename is consistent with new test scaffolding.

Receiver change to SuiteFunToken looks correct and aligns with the broader suite refactor.


36-50: Denom metadata setup LGTM.

Accurate base unit and symbol; matches expected Decimals=0 later.


116-126: ERC20 metadata assertions are precise.

Name/Symbol/Decimals checks match the configured bank metadata.


310-343: Helper’s funding logic is tidy and explicit.

Combining fee + test funding reduces setup noise and improves test determinism.

x/evm/precompile/funtoken_test.go (10)

122-131: Bank funding tied to mapping denom is correct.

Using funtoken.BankDenom avoids coupling to global EVMBankDenom.


156-174: Post-conversion balance checks are solid.

ERC20 balance and bank escrow assertions match ConvertCoinToEvm semantics.


176-190: Negative mint path is precise.

Expecting Ownable revert is appropriate for non-owner mint attempts.


219-224: sendToBank assertions: denom consistency.

Good use of funtoken.BankDenom for bank-side checks.


263-266: CreateFunTokenForBankCoin: testdenom

Switch to a local test denom is fine; keeps tests self-contained.


299-316: Local gas default path test reads clearly.

Commit=false in views and appropriate gas limits used.


321-341: Local gas sufficient path.

Covers the precompile-local-gas branch without over-provisioning.


343-364: Local gas insufficient path.

Asserting “execution reverted” is the right observable effect.


373-425: Made-from-coin sendToEvm path is thorough.

Checks escrow on bank side and mint on ERC20 side; balances line up.


727-766: getErc20Address view: robust and clear.

Happy/invalid/non-existent paths are exercised; using commit=false and explicit gas is good.

x/evm/keeper/msg_convert_evm_to_coin_test.go (11)

29-41: Bank metadata setup is correct.

Minimal denom unit with symbol; aligns with Decimals=0 expectations for coin-originated mapping.


50-61: CreateFunToken from bank coin: OK.

Capturing funtoken and erc20Addr for subsequent assertions is clear.


71-89: Bootstrap via ConvertCoinToEvm is well-structured.

Pre-load sender’s ERC20 and verify escrowed bank zero on the account.


90-118: Happy path: EVM→Bank (coin-originated).

Balance transitions and recipient bank credit are correct.


121-134: Insufficient ERC20 balance: good negative test.

Amount intentionally exceeds remaining ERC20; expecting error is appropriate.


135-150: Non-existent mapping: error contract is asserted.

Error text check is specific enough to catch regressions.


296-309: Event test: good metadata prep.

Denom metadata and initial funding are set atomically; reduces fixture noise.


389-472: Multiple recipients: looped conversions are clear.

Good coverage for sequential sends and final balance reconciliation.


477-499: WNIBI: precondition check reads well.

Asserting failure when canonical address points to non-contract is valuable.


513-548: WNIBI wrapping: balance pre/post checks are precise.

Funding fee collector and using TxTransferWei is realistic; verifying SDB and ERC20 balances is helpful.


550-575: WNIBI conversion happy path.

Post-conversion checks on WNIBI and native balance are consistent with NativeToWei semantics.

x/evm/keeper/funtoken_from_erc20_test.go (2)

24-24: LGTM! Test suite refactoring is clean and consistent.

The refactoring from FunTokenFromErc20Suite to SuiteFunToken has been applied consistently across all test methods. The changes follow the broader test suite reorganization pattern.

Also applies to: 168-168, 362-362, 402-402, 461-461, 537-537, 629-629


215-215: Approve CallContractWithInput signature and call-site updates
All CallContractWithInput calls in funtoken_from_erc20_test.go include the new nil weiValue argument, and the keeper’s signature in x/evm/keeper/call_contract.go has been updated to accept weiValue *big.Int.

x/evm/msg.go (5)

25-25: LGTM! Consistent usage of gethcommon alias throughout the file.

The introduction of the gethcommon alias for github.com/ethereum/go-ethereum/common improves code readability and is applied consistently across all relevant methods.

Also applies to: 300-300, 322-322, 326-326, 409-409, 541-543, 555-556, 564-564, 567-568


529-533: LGTM! GetSigners implementation follows SDK standards.

The implementation correctly extracts the sender address from the message and returns it as a single-element slice, consistent with other message types in the file.


536-578: Well-structured validation with comprehensive address handling.

The Validate method provides robust validation with support for both Ethereum hex and Bech32 address formats. The structured return type makes it convenient for callers to use the parsed values directly.


581-584: LGTM! ValidateBasic delegates to the comprehensive Validate method.

The implementation correctly delegates to the Validate method and discards the parsed values that are only needed by callers who want to use them.


586-589: LGTM! GetSignBytes implementation is consistent.

The implementation follows the standard pattern used by other message types in the file.

x/evm/keeper/msg_convert_evm_to_coin.go (3)

27-111: Well-structured conversion logic for coin-originated tokens.

The function correctly handles the conversion flow:

  1. Burns ERC20 tokens via the authority-controlled burn function
  2. Transfers equivalent bank coins from the module to the recipient
  3. Commits state and emits appropriate events

The error handling and event emission are comprehensive.


113-203: Clean implementation for ERC20-originated tokens.

The function properly handles the conversion by:

  1. Transferring ERC20 tokens to the EVM module
  2. Minting equivalent bank coins
  3. Sending the minted coins to the recipient

The use of balIncrease from the Transfer call ensures accurate coin minting.


205-303: Comprehensive WNIBI conversion with proper validation.

The function correctly handles WNIBI-specific conversion including:

  • Validation that the contract exists (non-zero code size)
  • Balance verification before and after withdrawal
  • Proper wei to micronibi conversion
x/evm/evmmodule/genesis_test.go (1)

142-147: Should the sender’s ERC20 balance reflect the EVM→bank conversion?

If ConvertEvmToCoin burns/transfers from Sender’s EVM balance, expected should subtract amountToSendC as well. The current assertion ignores it.

If the burn/transfer is intended, update:

-sumAB := new(big.Int).Add(new(big.Int).Set(amountToSendA), amountToSendB)
+sumAB := new(big.Int).Add(new(big.Int).Set(amountToSendA), amountToSendB)
+sumABC := new(big.Int).Add(sumAB, amountToSendC)
-expected := new(big.Int).Sub(new(big.Int).Set(totalSupply), sumAB)
+expected := new(big.Int).Sub(new(big.Int).Set(totalSupply), sumABC)

@NibiruChain NibiruChain deleted a comment from coderabbitai bot Sep 7, 2025
Copy link
Contributor

@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: 3

♻️ Duplicate comments (2)
app/upgrades/v2_7_0/v2_7_0.go (1)

50-58: Make CanonicalWnibi update idempotent and mainnet-only

Avoid overwriting non-mainnet/test settings and skip no-op writes. Add logging.

 func AddWnibiToNibiruEvm(
   keepers *keepers.PublicKeepers,
   ctx sdk.Context,
 ) error {
-  wnibiAddrMainnet := appconst.MAINNET_WNIBI_ADDR
-  evmParams := keepers.EvmKeeper.GetParams(ctx)
-  evmParams.CanonicalWnibi = eth.EIP55Addr{
-    Address: wnibiAddrMainnet,
-  }
-
-  err := keepers.EvmKeeper.SetParams(ctx, evmParams)
-  return err
+  if ctx.ChainID() != "nibiru-1" {
+    ctx.Logger().Info("Skipping CanonicalWnibi update: not mainnet", "chain_id", ctx.ChainID())
+    return nil
+  }
+  target := eth.EIP55Addr{Address: appconst.MAINNET_WNIBI_ADDR}
+  evmParams := keepers.EvmKeeper.GetParams(ctx)
+  if evmParams.CanonicalWnibi.Equal(target) {
+    return nil
+  }
+  // If already set to a different non-zero value, log and skip to be safe.
+  if !evmParams.CanonicalWnibi.Address.IsZero() && !evmParams.CanonicalWnibi.Equal(target) {
+    ctx.Logger().Info("CanonicalWnibi already set to non-target; skipping",
+      "existing", evmParams.CanonicalWnibi.Hex(), "target", target.Hex())
+    return nil
+  }
+  evmParams.CanonicalWnibi = target
+  if err := keepers.EvmKeeper.SetParams(ctx, evmParams); err != nil {
+    return err
+  }
+  ctx.Logger().Info("CanonicalWnibi parameter set", "address", target.Hex())
+  return nil
 }
x/evm/evmmodule/genesis_test.go (1)

128-133: Recreate EVM after reinitializing modules to avoid stale state

After deps is replaced and genesis reinitialized, evmObj still points to the old state/config.

 	evmmodule.InitGenesis(deps.Ctx, deps.EvmKeeper, deps.App.AccountKeeper, *evmGenesisState)
+	// Recreate EVM bound to the new deps/context
+	evmObj, _ = deps.NewEVM()
🧹 Nitpick comments (13)
x/evm/keeper/keeper_test.go (2)

20-23: Consider separate Test functions per suite for grepability and -run filtering.

Running two suites from a single TestAll works, but defining one TestXxx per suite improves dev ergonomics (go test -run TestSuiteFunToken etc.) and isolates reporting.

-func TestAll(t *testing.T) {
-  suite.Run(t, new(Suite))
-  suite.Run(t, new(SuiteFunToken))
-}
+func TestSuite(t *testing.T) {
+  suite.Run(t, new(Suite))
+}
+
+func TestSuiteFunToken(t *testing.T) {
+  suite.Run(t, new(SuiteFunToken))
+}

161-169: Harden TestGetHashFn: avoid uint64 underflow and strengthen assertions.

Casting to uint64 before subtracting can underflow when height is 0. Also, assert the header hash is non-zero to make the positive case stronger.

 func (s *Suite) TestGetHashFn() {
   deps := evmtest.NewTestDeps()
   fn := deps.EvmKeeper.GetHashFn(deps.Ctx)
   s.Equal(gethcommon.Hash{}, fn(math.MaxInt64+1))
-  s.Equal(gethcommon.BytesToHash(deps.Ctx.HeaderHash()), fn(uint64(deps.Ctx.BlockHeight())))
-  s.Equal(gethcommon.Hash{}, fn(uint64(deps.Ctx.BlockHeight())+1))
-  s.Equal(gethcommon.Hash{}, fn(uint64(deps.Ctx.BlockHeight())-1))
+  h := deps.Ctx.BlockHeight()
+  cur := gethcommon.BytesToHash(deps.Ctx.HeaderHash())
+  s.NotEqual(gethcommon.Hash{}, cur) // ensure header hash is non-zero
+  s.Equal(cur, fn(uint64(h)))
+  s.Equal(gethcommon.Hash{}, fn(uint64(h)+1))
+  if h > 0 {
+    s.Equal(gethcommon.Hash{}, fn(uint64(h-1)))
+  } else {
+    s.Equal(gethcommon.Hash{}, fn(0))
+  }
 }
x/evm/evmtest/genesis_inject.go (4)

27-31: Prefer app codec over stdlib for genesis decoding

Use the app’s codec to avoid drift with custom (de)serializers and future tag changes.

-    var genAcc evm.GenesisAccount
-    err := json.Unmarshal([]byte(WNIBI_GENESIS_ACC_STRING), &genAcc)
+    encCfg := app.MakeEncodingConfig()
+    var genAcc evm.GenesisAccount
+    err := encCfg.Codec.UnmarshalJSON([]byte(WNIBI_GENESIS_ACC_STRING), &genAcc)

47-55: Validate bech32 matches MAINNET_WNIBI_ADDR (or derive only from source of truth)

You hardcode the EthAccount JSON address string elsewhere and also derive it here from MAINNET_WNIBI_ADDR. Keep a single source to prevent drift; optionally assert they match at init.

         genAcc := eth.EthAccount{
             BaseAccount: &auth.BaseAccount{
-                Address:       eth.EthAddrToNibiruAddr(appconst.MAINNET_WNIBI_ADDR).String(),
+                Address:       eth.EthAddrToNibiruAddr(appconst.MAINNET_WNIBI_ADDR).String(),
                 PubKey:        nil,
                 AccountNumber: 13,
                 Sequence:      0,
             },
             CodeHash: "0xffb88e0eb48147949565e65de3ec8a54b746214da7b9dd5b9a8a3ae7df46193b",
         }
+        // Optional sanity check if you decide to keep the JSON constant below:
+        // if !strings.Contains(WNIBI_GENESIS_AUTH_ACC_STRING, genAcc.BaseAccount.Address) { panic("WNIBI bech32 mismatch") }

56-61: Remove commented-out code

Dead/commented parsing paths add noise and can get stale.

-        // err := encCfg.Codec.Unmarshal([]byte(WNIBI_GENESIS_AUTH_ACC_STRING), &genAcc)
-        // // err := json.Unmarshal([]byte(WNIBI_GENESIS_AUTH_ACC_STRING), &genAcc)
-        // if err != nil {
-        //  panic(fmt.Errorf("failed to unpack WNIBI_GENESIS_AUTH_ACC_STRING as eth.EthAccount: %w", err))
-        // }

65-76: Drop unused WNIBI_GENESIS_AUTH_ACC_STRING or use it consistently

This constant isn’t consumed and risks diverging from MAINNET_WNIBI_ADDR. Either delete it or make it the single source of truth.

-const WNIBI_GENESIS_AUTH_ACC_STRING = `
-{
-  "@type": "/eth.types.v1.EthAccount",
-  "base_account": {
-    "address": "nibi1pjk0v60cg347e2pxjyarc6uk4n2tq25hhymgg9",
-    "pub_key": null,
-    "account_number": "13",
-    "sequence": "0"
-  },
-  "code_hash": "0xffb88e0eb48147949565e65de3ec8a54b746214da7b9dd5b9a8a3ae7df46193b"
-}
-`
+// (Optional) keep no duplicate JSON sources for the same account to avoid drift.
x/common/testutil/genesis/genesis.go (5)

28-37: Guard against nil VotingPeriod pointer

Use an explicit duration value; don’t assume pointer is non-nil.

-    *govGenState.Params.VotingPeriod = 20 * time.Second
+    d := 20 * time.Second
+    govGenState.Params.VotingPeriod = &d

46-57: Avoid duplicate EVM genesis accounts (idempotency)

If tests call this more than once, duplicates break genesis. Check before append.

-    evmGenState.Accounts = append(evmGenState.Accounts, evmtest.WNIBI_GENESIS_EVM_ACC)
+    exists := false
+    for _, acc := range evmGenState.Accounts {
+        if strings.EqualFold(acc.Address, evmtest.WNIBI_GENESIS_EVM_ACC.Address) {
+            exists = true
+            break
+        }
+    }
+    if !exists {
+        evmGenState.Accounts = append(evmGenState.Accounts, evmtest.WNIBI_GENESIS_EVM_ACC)
+    }

Note: add strings to imports.


73-77: Check PackAccounts error and avoid append if packing fails

Don’t ignore the error even if it “can’t happen”.

-    protobufAnyAccs, _ := auth.PackAccounts(auth.GenesisAccounts{
-        &wnibiAcc,
-    })
+    protobufAnyAccs, err := auth.PackAccounts(auth.GenesisAccounts{&wnibiAcc})
+    if err != nil {
+        panic(fmt.Errorf("failed to pack WNIBI EthAccount: %w", err))
+    }

77-84: Avoid duplicate Auth accounts (same address)

Check existing accounts before appending WNIBI to keep genesis idempotent.

-    authGenState.Accounts = append(authGenState.Accounts, wnibiAccAsProtobufAny)
+    // naive check by bech32 string match to avoid unpacking all Any's in tests
+    addr := wnibiAcc.BaseAccount.Address
+    already := false
+    for _, anyAcc := range authGenState.Accounts {
+        if strings.Contains(string(anyAcc.Value), addr) {
+            already = true
+            break
+        }
+    }
+    if !already {
+        authGenState.Accounts = append(authGenState.Accounts, wnibiAccAsProtobufAny)
+    }

Note: add strings to imports.


20-23: Doc comment nit

Minor: collapse multi-line sentence into one line per Go doc convention.

-// NewTestGenesisState returns [app.GenesisState] using the default genesis as input.
-// The blockchain genesis state is represented as a map from module identifier
-// strings to raw json messages.
+// NewTestGenesisState returns a default app.GenesisState (map[module]json.RawMessage) with test-friendly tweaks.
x/evm/keeper/msg_convert_evm_to_coin.go (1)

48-64: Minor: deduplicate core.Message scaffolding
These three blocks share identical zero-gas scaffolding. Extract a small helper to build messages to reduce duplication and drift.

Also applies to: 134-150, 235-251

x/evm/evmmodule/genesis_test.go (1)

101-101: Fix direction in comment

This test invokes ConvertEvmToCoin (EVM → bank), not bank → EVM.

-	// Send fungible token coins from bank to evm
+	// Convert ERC20 tokens to bank coins (EVM → bank)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b5d3b17 and ba9109b.

📒 Files selected for processing (7)
  • app/upgrades/v2_7_0/v2_7_0.go (1 hunks)
  • x/common/testutil/genesis/genesis.go (1 hunks)
  • x/evm/evmmodule/genesis_test.go (5 hunks)
  • x/evm/evmtest/genesis_inject.go (1 hunks)
  • x/evm/keeper/keeper_test.go (2 hunks)
  • x/evm/keeper/msg_convert_evm_to_coin.go (1 hunks)
  • x/evm/keeper/msg_server.go (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • x/evm/keeper/msg_server.go
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-01-17T17:32:27.341Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2165
File: x/evm/evmmodule/genesis_test.go:51-51
Timestamp: 2025-01-17T17:32:27.341Z
Learning: The `NewEVM()` method in TestDeps returns only `*vm.EVM` without an error value.

Applied to files:

  • x/evm/evmmodule/genesis_test.go
📚 Learning: 2025-01-17T17:32:27.341Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2165
File: x/evm/evmmodule/genesis_test.go:51-51
Timestamp: 2025-01-17T17:32:27.341Z
Learning: The `NewEVM()` method in TestDeps returns `(*vm.EVM, *statedb.StateDB)` without an error value. The second return value is often discarded when only the EVM object is needed.

Applied to files:

  • x/evm/evmmodule/genesis_test.go
📚 Learning: 2025-01-17T17:32:33.412Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2165
File: x/evm/keeper/funtoken_from_coin_test.go:30-30
Timestamp: 2025-01-17T17:32:33.412Z
Learning: In test dependencies, `deps.NewEVM()` returns only an EVM object and does not return an error. The method is used in test files to create a new EVM instance for testing purposes.

Applied to files:

  • x/evm/evmmodule/genesis_test.go
📚 Learning: 2025-01-17T17:32:33.412Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2165
File: x/evm/keeper/funtoken_from_coin_test.go:30-30
Timestamp: 2025-01-17T17:32:33.412Z
Learning: In test dependencies, `deps.NewEVM()` returns both an EVM object and a StateDB object with the signature `(*vm.EVM, *statedb.StateDB)`. The method is used in test files to create new EVM and StateDB instances for testing purposes.

Applied to files:

  • x/evm/evmmodule/genesis_test.go
📚 Learning: 2024-12-06T16:09:01.767Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2119
File: x/evm/keeper/bank_extension_test.go:161-165
Timestamp: 2024-12-06T16:09:01.767Z
Learning: In Go test assertions within `x/evm/keeper/bank_extension_test.go`, when comparing `uint64` values using `s.Equalf`, prefer formatting the values as decimal strings with `fmt.Sprintf("%d", value)` to avoid them being printed in hexadecimal.

Applied to files:

  • x/evm/evmmodule/genesis_test.go
📚 Learning: 2024-10-31T18:28:26.466Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2095
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-10-31T18:28:26.466Z
Learning: When implementing the `bankkeeper.Keeper` interface in `x/evm/keeper/bank_extension.go`, methods must use non-pointer receivers to satisfy the interface requirements.

Applied to files:

  • x/evm/keeper/msg_convert_evm_to_coin.go
🧬 Code graph analysis (6)
x/common/testutil/genesis/genesis.go (6)
app/genesis.go (1)
  • GenesisState (74-74)
app/app.go (1)
  • ModuleBasics (122-161)
x/evm/const.go (1)
  • ModuleName (41-41)
x/common/denoms/denoms.go (1)
  • NIBI (23-23)
x/common/testutil/testapp/testapp.go (1)
  • SetDefaultSudoGenesis (79-91)
x/evm/evmtest/genesis_inject.go (2)
  • WNIBI_GENESIS_EVM_ACC (18-18)
  • WNIBI_GENESIS_AUTH_ACC (20-20)
x/evm/keeper/keeper_test.go (1)
x/evm/evmtest/test_deps.go (1)
  • NewTestDeps (36-47)
app/upgrades/v2_7_0/v2_7_0.go (4)
app/upgrades/v2_5_0/v2_5_0.go (2)
  • Upgrade (31-53)
  • UpgradeName (29-29)
app/keepers/all_keepers.go (1)
  • PublicKeepers (35-75)
app/appconst/appconst.go (1)
  • MAINNET_WNIBI_ADDR (34-34)
eth/eip55.go (1)
  • EIP55Addr (17-19)
x/evm/evmmodule/genesis_test.go (6)
x/evm/evmtest/test_deps.go (1)
  • NewTestDeps (36-47)
x/evm/evm.pb.go (3)
  • FunToken (32-41)
  • FunToken (45-45)
  • FunToken (46-48)
x/evm/tx.pb.go (6)
  • MsgCreateFunToken (480-487)
  • MsgCreateFunToken (491-491)
  • MsgCreateFunToken (492-494)
  • MsgConvertEvmToCoin (675-693)
  • MsgConvertEvmToCoin (697-697)
  • MsgConvertEvmToCoin (698-700)
eth/eip55.go (2)
  • EIP55Addr (17-19)
  • NewEIP55AddrFromStr (22-35)
x/evm/evmtest/erc20.go (1)
  • FunTokenBalanceAssert (153-159)
x/evm/evmmodule/genesis.go (2)
  • ExportGenesis (86-129)
  • InitGenesis (20-83)
x/evm/evmtest/genesis_inject.go (2)
eth/eth_account.go (1)
  • EthAddrToNibiruAddr (15-17)
app/appconst/appconst.go (1)
  • MAINNET_WNIBI_ADDR (34-34)
x/evm/keeper/msg_convert_evm_to_coin.go (8)
x/evm/statedb/statedb.go (1)
  • StateDB (45-85)
x/evm/embeds/embeds.go (2)
  • SmartContract_ERC20MinterWithMetadataUpdates (71-74)
  • SmartContract_WNIBI (96-99)
x/evm/const.go (2)
  • EVM_MODULE_ADDRESS (106-106)
  • WeiToNative (136-139)
x/evm/keeper/erc20.go (1)
  • Erc20GasLimitExecute (30-30)
eth/eip55.go (1)
  • EIP55Addr (17-19)
x/evm/keeper/msg_server.go (1)
  • ParseWeiAsMultipleOfMicronibi (436-464)
x/evm/errors.go (1)
  • ErrCanonicalWnibi (54-54)
app/appconst/appconst.go (1)
  • BondDenom (22-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: proto-gen-rs
  • GitHub Check: build
  • GitHub Check: unit-tests
  • GitHub Check: integration-tests
  • GitHub Check: e2e-evm
  • GitHub Check: lint
🔇 Additional comments (7)
x/evm/keeper/keeper_test.go (4)

4-4: Imports look correct for new assertions.

math is used for the out-of-range sentinel and gethcommon for hash conversions. No concerns.

Also applies to: 8-8


16-18: Section dividers are style-only.

No action needed; just noting the added comment separators.

Also applies to: 33-34


161-169: Confirm intended semantics vs EVM BLOCKHASH.

The test asserts zero for the previous height. If GetHashFn is meant to emulate the EVM BLOCKHASH opcode, prior blocks within the 256-block window typically return a non-zero hash (excluding the current block). If the zero-for-previous behavior is intentional for Nibiru, consider a brief comment in the test to document the divergence; otherwise, add coverage for the 256-block window.


29-31: No-op suite warning resolved SuiteFunToken has multiple Test* methods in other _test.go files (e.g. funtoken_from_erc20_test.go, msg_convert_coin_to_evm_test.go); no further action needed.

x/common/testutil/genesis/genesis.go (1)

63-84: Remove EthAccount shape verification EthAccount embeds BaseAccount, so AccountNumber is promoted and accessible—wnibiAcc.AccountNumber = … is valid.

Likely an incorrect or invalid review comment.

x/evm/keeper/msg_convert_evm_to_coin.go (1)

132-152: Confirm msg.sender for ERC20 transfer path (sender vs module addr)

evmObj is built with From: EVM_MODULE_ADDRESS and SkipFromEOACheck: true, but the subsequent ERC20().Transfer is semantically initiated by senderEthAddr. If Transfer relies on msg.sender, this can misattribute the call.

Consider aligning with the WNIBI path and constructing evmObj from senderEthAddr:

-		evmMsg := core.Message{
-			To:               &erc20Addr,
-			From:             evm.EVM_MODULE_ADDRESS,
+		evmMsg := core.Message{
+			To:               &erc20Addr,
+			From:             senderEthAddr,
 			Nonce:            k.GetAccNonce(ctx, evm.EVM_MODULE_ADDRESS),
 			Value:            unusedBigInt,
 			GasLimit:         Erc20GasLimitExecute,
 			GasPrice:         unusedBigInt,
 			GasFeeCap:        unusedBigInt,
 			GasTipCap:        unusedBigInt,
 			Data:             contractInput,
 			AccessList:       gethcore.AccessList{},
 			BlobGasFeeCap:    &big.Int{},
 			BlobHashes:       []gethcommon.Hash{},
-			SkipNonceChecks:  true,
-			SkipFromEOACheck: true,
+			SkipNonceChecks:  false,
+			SkipFromEOACheck: false,
 		}

If ERC20().Transfer internally sets the from-account irrespective of evmObj, please confirm and ignore this change. Otherwise, this prevents accidental transfers from the module’s balance.

x/evm/evmmodule/genesis_test.go (1)

104-110: Recipient format: verify ToAddr form used in tests

Proto allows hex or bech32 for ToAddr. You’re passing EIP-55 hex. If downstream assertions expect bech32 events/balances, consider using the bech32 string of the bank recipient to match keeper expectations and event payloads.

Optionally:

-			ToAddr:    eip55Addr.Hex(),
+			ToAddr:    deps.Sender.NibiruAddr.String(), // or the intended bech32 recipient

Copy link
Contributor

@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: 2

♻️ Duplicate comments (7)
x/evm/keeper/msg_convert_coin_to_evm.go (6)

52-58: StateDB lifecycle bug: clobbers existing pointer and never restores it.

You create a StateDB when nil, but you never set k.Bank.StateDB to it, and you always defer setting k.Bank.StateDB = nil, which can erase a previously-set StateDB from upstream callers. Restore previous pointer and only commit when you created the StateDB. (Same issue recurs below.)

-	stateDB := k.Bank.StateDB
-	if stateDB == nil {
-		stateDB = k.NewStateDB(ctx, k.TxConfig(ctx, gethcommon.Hash{}))
-	}
-	defer func() {
-		k.Bank.StateDB = nil
-	}()
+	prevStateDB := k.Bank.StateDB
+	stateDB := prevStateDB
+	createdState := false
+	if stateDB == nil {
+		stateDB = k.NewStateDB(ctx, k.TxConfig(ctx, gethcommon.Hash{}))
+		k.Bank.StateDB = stateDB
+		createdState = true
+	}
+	defer func() {
+		// Restore whatever was there before.
+		k.Bank.StateDB = prevStateDB
+	}()

160-164: Commit only if this function created the StateDB.

Avoid double-commits and committing someone else’s StateDB.

-	if err := stateDB.Commit(); err != nil {
-		return nil, fmt.Errorf("failed to commit stateDB: %w", err)
-	}
+	if createdState {
+		if err := stateDB.Commit(); err != nil {
+			return nil, fmt.Errorf("failed to commit stateDB: %w", err)
+		}
+	}

216-223: Repeat: StateDB lifecycle (born-coin path).

Same pointer clobber/restore issue as above. Apply the same pattern.

-	stateDB := k.Bank.StateDB
-	if stateDB == nil {
-		stateDB = k.NewStateDB(ctx, txConfig)
-	}
-	defer func() {
-		k.Bank.StateDB = nil
-	}()
+	prevStateDB := k.Bank.StateDB
+	stateDB := prevStateDB
+	createdState := false
+	if stateDB == nil {
+		stateDB = k.NewStateDB(ctx, txConfig)
+		k.Bank.StateDB = stateDB
+		createdState = true
+	}
+	defer func() { k.Bank.StateDB = prevStateDB }()

244-246: Commit only if created (born-coin path).

-	if err = stateDB.Commit(); err != nil {
+	if createdState {
+		if err = stateDB.Commit(); err != nil {
 			return nil, sdkioerrors.Wrap(err, "failed to commit stateDB")
-	}
+		}
+	}

276-283: Repeat: StateDB lifecycle (born-ERC20 path).

Mirror the same fix here.

-	stateDB := k.Bank.StateDB
-	if stateDB == nil {
-		stateDB = k.NewStateDB(ctx, k.TxConfig(ctx, gethcommon.Hash{}))
-	}
-	defer func() {
-		k.Bank.StateDB = nil
-	}()
+	prevStateDB := k.Bank.StateDB
+	stateDB := prevStateDB
+	createdState := false
+	if stateDB == nil {
+		stateDB = k.NewStateDB(ctx, k.TxConfig(ctx, gethcommon.Hash{}))
+		k.Bank.StateDB = stateDB
+		createdState = true
+	}
+	defer func() { k.Bank.StateDB = prevStateDB }()

348-350: Commit only if created (born-ERC20 path).

-	if err := stateDB.Commit(); err != nil {
-		return nil, sdkioerrors.Wrap(err, "failed to commit stateDB")
-	}
+	if createdState {
+		if err := stateDB.Commit(); err != nil {
+			return nil, sdkioerrors.Wrap(err, "failed to commit stateDB")
+		}
+	}
x/evm/keeper/msg_convert_evm_to_coin.go (1)

36-37: Fix 18→6 decimal conversion: over-crediting bank coins by 1e12

For coin-originated tokens, amount is in wei (18dp). Bank coins use micro (6dp). Convert with evm.WeiToNative before constructing the sdk.Coin.

-	bankCoins := sdk.NewCoins(sdk.NewCoin(bankDenom, sdkmath.NewIntFromBigInt(amount)))
+	// Convert 18-decimal ERC20 amount -> 6-decimal bank coin amount
+	bankAmt := sdkmath.NewIntFromBigInt(evm.WeiToNative(amount))
+	bankCoins := sdk.NewCoins(sdk.NewCoin(bankDenom, bankAmt))
🧹 Nitpick comments (12)
CHANGELOG.md (1)

50-54: Clarify wording and wrap continuation lines for consistency

  • Prefer “Cosmos SDK transaction (non-EVM)” over “non-Ethereum transaction” for precision.
  • Indent wrapped lines by two spaces to match multi-line bullets elsewhere in this file.
- - [#2345](https://github.com/NibiruChain/nibiru/pull/2345) - feat(evm): add
-"eth.evm.v1.MsgConvertEvmToCoin" tx for ERC20 to bank coin conversions with a
-non-Ethereum transaction. This change introduces new message types, CLI commands,
-and keeper logic to cover both directions of conversion, including special-case
-handling for NIBI via WNIBI.
+ - [#2345](https://github.com/NibiruChain/nibiru/pull/2345) - feat(evm): add
+  "eth.evm.v1.MsgConvertEvmToCoin" tx for ERC20 to bank coin conversions with a
+  Cosmos SDK transaction (non-EVM). This change introduces new message types, CLI commands,
+  and keeper logic to cover both directions of conversion, including special-case
+  handling for NIBI via WNIBI.
x/evm/msg.go (2)

409-419: Fix wrong value in error message (diagnostics).

On non-Ethereum messages, the error prints the sdk.Tx pointer type instead of the offending msg type.

-			return nil, fmt.Errorf("invalid tx type: %T", tx)
+			return nil, fmt.Errorf("invalid msg type: %T", msg)

542-584: Tighten Erc20 emptiness check and improve ToAddr error context.

  • Use the same emptiness idiom as other EIP55 fields for consistency.
  • Include the bad input in the ToAddr error for easier debugging.
-	if (m.Erc20Addr.Address == gethcommon.Address{}) {
-		err = fmt.Errorf("empty erc20_addr")
-		return
-	}
+	if m.Erc20Addr.String() == "" || m.Erc20Addr.Size() == 0 {
+		err = fmt.Errorf("empty erc20_addr")
+		return
+	}
@@
-		toAddr.Bech32, err = sdk.AccAddressFromBech32(m.ToAddr)
+		toAddr.Bech32, err = sdk.AccAddressFromBech32(m.ToAddr)
 		if err != nil {
-			err = fmt.Errorf("invalid bech32 or hex address: %w", err)
+			err = fmt.Errorf("invalid bech32 or hex address %q: %w", m.ToAddr, err)
 			return
 		}
x/evm/keeper/msg_convert_coin_to_evm.go (4)

116-121: Error message direction is inverted.

This path performs NIBI -> WNIBI deposit, but the message says “convert WNIBI to NIBI”.

-		return resp, fmt.Errorf("failed to convert WNIBI to NIBI: %w", err)
+		return resp, fmt.Errorf("failed to deposit NIBI into WNIBI: %w", err)
@@
-		err = fmt.Errorf("failed to convert WNIBI to NIBI: VmError: %s", evmResp.VmError)
+		err = fmt.Errorf("failed to deposit NIBI into WNIBI: VmError: %s", evmResp.VmError)

134-136: Typos in comment.

“tranfers” -> “transfers”.

-	// STEP 2: Sender tranfers WNIBI to intended recipient
+	// STEP 2: Sender transfers WNIBI to intended recipient

145-157: Error wording + typo (“receipient”).

This step is an ERC20 transfer of WNIBI. Adjust wording; fix spelling.

-		return resp, fmt.Errorf("failed to convert WNIBI to NIBI: %w", err)
+		return resp, fmt.Errorf("failed to transfer WNIBI to recipient: %w", err)
@@
-		err = fmt.Errorf("failed to convert WNIBI to NIBI: VmError: %s", evmResp.VmError)
+		err = fmt.Errorf("failed to transfer WNIBI to recipient: VmError: %s", evmResp.VmError)
@@
-			"ConvertCoinToEvm: transfer of WNIBI succeeded but did not deliver the correct number of tokens: transfer amount %s, balance increase %s, senderHex %s, receipient %s",
+			"ConvertCoinToEvm: WNIBI transfer succeeded but delivered incorrect amount: expected %s, got %s, sender %s, recipient %s",

136-144: Optional: emit tx logs for parity and indexing.

In WNIBI branch, you don’t emit EventTxLog nor update the bloom, unlike the other branches. Consider emitting logs to keep indexing behavior consistent.

 	evmResp, err := k.CallContractWithInput(
@@
 	if err != nil {
 		...
 	}
+	// Emit tx logs and update bloom like other paths
+	_ = ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs})
+	k.updateBlockBloom(ctx, evmResp, uint64(k.EvmState.BlockTxIndex.GetOr(ctx, 0)))

Also applies to: 256-260

x/evm/keeper/msg_convert_evm_to_coin.go (5)

318-324: Emit EventTxLog and update bloom for WNIBI path (parity with other paths)

Add tx-log emission and bloom update so indexers/observers see the WNIBI withdraw logs consistently.

 	_ = ctx.EventManager().EmitTypedEvent(&evm.EventConvertEvmToCoin{
 		Sender:               sender.Bech32.String(),
 		Erc20ContractAddress: erc20.Hex(),
 		ToAddress:            toAddrBech32.String(),
 		BankCoin:             withdrawnMicronibi,
 		SenderEthAddr:        sender.Eth.Hex(),
 	})
 
+	// Emit tx logs and update bloom (match other conversion paths)
+	if evmResp != nil {
+		if err := ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs}); err == nil {
+			k.updateBlockBloom(ctx, evmResp, uint64(k.EvmState.BlockTxIndex.GetOr(ctx, 0)))
+		}
+	}

95-103: Normalize contract address formatting in events (.Hex vs .String)

Use .Hex() consistently across paths to avoid mixed casing/formatting.

-		Erc20ContractAddress: erc20Addr.String(),
+		Erc20ContractAddress: erc20Addr.Hex(),
-		Erc20ContractAddress: erc20Addr.String(),
+		Erc20ContractAddress: erc20Addr.Hex(),

Also applies to: 188-194


104-108: Decouple bloom update from event emission error

Bloom should update regardless of EventTxLog emission outcome.

-	err = ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs})
-	if err == nil {
-		k.updateBlockBloom(ctx, evmResp, uint64(k.EvmState.BlockTxIndex.GetOr(ctx, 0)))
-	}
+	_ = ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs})
+	k.updateBlockBloom(ctx, evmResp, uint64(k.EvmState.BlockTxIndex.GetOr(ctx, 0)))

Also applies to: 196-200


23-27: Tighten Go doc comment style

Start with the function name and remove blank lines for proper GoDoc rendering.

-// convertEvmToCoinForCoinOriginated is part of the
-//
-// "eth.evm.v1.MsgConvertEvmToCoin" tx. This function handles conversion of ERC20
-//
-// tokens that were originally bank coins back into coin form. The EVM module
-//
-// owns the ERC20 contract and will burn the tokens
+// convertEvmToCoinForCoinOriginated handles "eth.evm.v1.MsgConvertEvmToCoin"
+// for tokens that were originally bank coins. The EVM module owns the ERC20
+// contract and burns the tokens before releasing bank coins.

47-63: Reduce duplication: factor EVM message builder

Three near-identical core.Message constructions. Consider a small helper to set zero-valued fee fields and common flags.

Also applies to: 134-150, 241-257

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba9109b and 74b4090.

⛔ Files ignored due to path filters (1)
  • x/evm/events.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (9)
  • CHANGELOG.md (2 hunks)
  • api/eth/evm/v1/events.pulsar.go (8 hunks)
  • proto/eth/evm/v1/events.proto (4 hunks)
  • x/evm/keeper/msg_convert_coin_to_evm.go (1 hunks)
  • x/evm/keeper/msg_convert_coin_to_evm_test.go (1 hunks)
  • x/evm/keeper/msg_convert_evm_to_coin.go (1 hunks)
  • x/evm/keeper/msg_convert_evm_to_coin_test.go (1 hunks)
  • x/evm/keeper/msg_server.go (7 hunks)
  • x/evm/msg.go (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • x/evm/keeper/msg_convert_evm_to_coin_test.go
  • x/evm/keeper/msg_convert_coin_to_evm_test.go
  • proto/eth/evm/v1/events.proto
  • x/evm/keeper/msg_server.go
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2024-10-08T15:45:59.488Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#1680
File: x/perp/v2/keeper/msg_server.go:210-210
Timestamp: 2024-10-08T15:45:59.488Z
Learning: The user indicated that error handling for `sdk.AccAddressFromBech32(msg.Sender)` is covered by prior `ValidateBasic` checks in the code flow.

Applied to files:

  • x/evm/msg.go
📚 Learning: 2024-10-08T15:45:59.488Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#1680
File: x/perp/v2/keeper/msg_server.go:198-198
Timestamp: 2024-10-08T15:45:59.488Z
Learning: The user Unique-Divine has indicated that the error check for `sdk.AccAddressFromBech32(msg.Sender)` is not necessary in the context of their code because it is covered by the `ValidateBasic` method.

Applied to files:

  • x/evm/msg.go
📚 Learning: 2024-12-06T16:09:01.767Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2119
File: x/evm/keeper/bank_extension_test.go:161-165
Timestamp: 2024-12-06T16:09:01.767Z
Learning: In Go test assertions within `x/evm/keeper/bank_extension_test.go`, when comparing `uint64` values using `s.Equalf`, prefer formatting the values as decimal strings with `fmt.Sprintf("%d", value)` to avoid them being printed in hexadecimal.

Applied to files:

  • x/evm/keeper/msg_convert_evm_to_coin.go
📚 Learning: 2024-12-06T11:54:38.275Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2119
File: x/evm/keeper/bank_extension_test.go:67-71
Timestamp: 2024-12-06T11:54:38.275Z
Learning: When writing tests in `x/evm/keeper/bank_extension_test.go`, prefer comparing gas consumption values as formatted strings using `fmt.Sprintf("%d", value)` instead of numeric values directly in `s.Equalf`. This avoids hexadecimal representations that are hard to read in shell outputs.

Applied to files:

  • x/evm/keeper/msg_convert_evm_to_coin.go
📚 Learning: 2024-10-31T18:28:26.466Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2095
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-10-31T18:28:26.466Z
Learning: When implementing the `bankkeeper.Keeper` interface in `x/evm/keeper/bank_extension.go`, methods must use non-pointer receivers to satisfy the interface requirements.

Applied to files:

  • x/evm/keeper/msg_convert_evm_to_coin.go
🧬 Code graph analysis (4)
api/eth/evm/v1/events.pulsar.go (1)
x/evm/events.pb.go (3)
  • EventConvertEvmToCoin (528-534)
  • EventConvertEvmToCoin (538-538)
  • EventConvertEvmToCoin (539-541)
x/evm/keeper/msg_convert_coin_to_evm.go (9)
x/evm/tx.pb.go (6)
  • MsgConvertCoinToEvm (582-589)
  • MsgConvertCoinToEvm (593-593)
  • MsgConvertCoinToEvm (594-596)
  • MsgConvertCoinToEvmResponse (638-639)
  • MsgConvertCoinToEvmResponse (643-643)
  • MsgConvertCoinToEvmResponse (644-646)
eth/eth_account.go (1)
  • NibiruAddrToEthAddr (19-21)
x/evm/statedb/statedb.go (1)
  • StateDB (45-85)
x/evm/statedb/config.go (1)
  • TxConfig (15-20)
x/evm/errors.go (1)
  • ErrCanonicalWnibi (54-54)
x/evm/const.go (2)
  • NativeToWei (123-126)
  • EVM_MODULE_ADDRESS (106-106)
x/evm/embeds/embeds.go (2)
  • SmartContract_WNIBI (96-99)
  • SmartContract_ERC20MinterWithMetadataUpdates (71-74)
x/evm/keeper/erc20.go (1)
  • Erc20GasLimitExecute (30-30)
x/evm/events.pb.go (6)
  • EventConvertCoinToEvm (291-296)
  • EventConvertCoinToEvm (300-300)
  • EventConvertCoinToEvm (301-303)
  • EventTxLog (128-131)
  • EventTxLog (135-135)
  • EventTxLog (136-138)
x/evm/msg.go (4)
x/evm/tx.pb.go (3)
  • MsgConvertEvmToCoin (675-693)
  • MsgConvertEvmToCoin (697-697)
  • MsgConvertEvmToCoin (698-700)
eth/eip55.go (2)
  • EIP55Addr (17-19)
  • NewEIP55AddrFromStr (22-35)
eth/eth_account.go (2)
  • NibiruAddrToEthAddr (19-21)
  • EthAddrToNibiruAddr (15-17)
x/evm/codec.go (1)
  • AminoCdc (18-18)
x/evm/keeper/msg_convert_evm_to_coin.go (8)
x/evm/msg.go (1)
  • Addrs (537-540)
x/evm/statedb/statedb.go (1)
  • StateDB (45-85)
x/evm/embeds/embeds.go (2)
  • SmartContract_ERC20MinterWithMetadataUpdates (71-74)
  • SmartContract_WNIBI (96-99)
x/evm/const.go (2)
  • EVM_MODULE_ADDRESS (106-106)
  • WeiToNative (136-139)
x/evm/keeper/erc20.go (1)
  • Erc20GasLimitExecute (30-30)
x/evm/keeper/msg_server.go (1)
  • ParseWeiAsMultipleOfMicronibi (435-463)
x/evm/errors.go (1)
  • ErrCanonicalWnibi (54-54)
app/appconst/appconst.go (1)
  • BondDenom (22-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build
  • GitHub Check: unit-tests
  • GitHub Check: e2e-evm
  • GitHub Check: integration-tests
  • GitHub Check: lint
🔇 Additional comments (10)
CHANGELOG.md (5)

42-42: LGTM: spacing after the Unreleased header

Keeps sections readable and matches existing style.


43-48: LGTM: CI note formatting

Comment is clear and properly closed. No action needed.


65-65: LGTM: corrected package link and description

Reads well and links look correct.


68-68: LGTM: PR link fix

The PR reference now points to the correct URL.


77-77: LGTM: blank line before Dependencies

Consistent with nearby sections; improves readability.

x/evm/msg.go (4)

300-301: LGTM: safer From-address extraction

Using gethcommon.HexToAddress(msg.From).Bytes() is consistent with upstream geth utils and ValidateBasic already guards invalid From.


321-331: LGTM: return type now gethcommon.Address

Changing GetSender to return gethcommon.Address aligns better with geth APIs and avoids extra conversions.


510-522: LGTM: clearer validation for MsgConvertCoinToEvm

Improved error messages and explicit empty checks read well.


586-595: LGTM: ValidateBasic delegates to Validate

Keeps basic checks self-contained and stateless.

api/eth/evm/v1/events.pulsar.go (1)

4480-5940: LGTM: protobuf surface for EventConvertEvmToCoin added correctly

New message, reflection scaffolding, descriptors, and dependency indices look consistent with the proto definition. No manual edits recommended in generated code.

Copy link
Contributor

@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: 2

♻️ Duplicate comments (3)
x/evm/keeper/msg_convert_evm_to_coin.go (3)

36-37: Fix 18→6 decimal conversion for coin-originated tokens (over-credit by 1e12).

Convert ERC20 wei to micro-denom before constructing bank coins.

-	bankCoins := sdk.NewCoins(sdk.NewCoin(bankDenom, sdkmath.NewIntFromBigInt(amount)))
+	bankAmt := sdkmath.NewIntFromBigInt(evm.WeiToNative(amount))
+	bankCoins := sdk.NewCoins(sdk.NewCoin(bankDenom, bankAmt))

168-174: Fix 18→6 decimal conversion when minting for ERC20-originated tokens.

Mint/send micro-denom, not wei.

-	bankCoin := sdk.NewCoin(bankDenom, sdkmath.NewIntFromBigInt(balIncrease))
+	bankAmt := sdkmath.NewIntFromBigInt(evm.WeiToNative(balIncrease))
+	bankCoin := sdk.NewCoin(bankDenom, bankAmt)

317-325: Emit EventTxLog and update bloom for WNIBI path (parity with other paths).

Improves observability and indexer parity.

 	_ = ctx.EventManager().EmitTypedEvent(&evm.EventConvertEvmToCoin{
 		Sender:               sender.Bech32.String(),
 		Erc20ContractAddress: erc20.Hex(),
 		ToAddress:            toAddrBech32.String(),
 		BankCoin:             withdrawnMicronibi,
 		SenderEthAddr:        sender.Eth.Hex(),
 	})
 
+	// Emit tx logs and update bloom (mirrors other conversion paths)
+	if err := ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs}); err == nil {
+		k.updateBlockBloom(ctx, evmResp, uint64(k.EvmState.BlockTxIndex.GetOr(ctx, 0)))
+	}
🧹 Nitpick comments (1)
x/evm/keeper/msg_convert_evm_to_coin.go (1)

95-102: Unify contract address formatting in events.

Use Hex() consistently across paths.

-		Erc20ContractAddress: erc20Addr.String(),
+		Erc20ContractAddress: erc20Addr.Hex(),
-		Erc20ContractAddress: erc20Addr.String(),
+		Erc20ContractAddress: erc20Addr.Hex(),

Also applies to: 187-194

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 74b4090 and 4860545.

📒 Files selected for processing (2)
  • CHANGELOG.md (2 hunks)
  • x/evm/keeper/msg_convert_evm_to_coin.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.md
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2024-12-06T16:09:01.767Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2119
File: x/evm/keeper/bank_extension_test.go:161-165
Timestamp: 2024-12-06T16:09:01.767Z
Learning: In Go test assertions within `x/evm/keeper/bank_extension_test.go`, when comparing `uint64` values using `s.Equalf`, prefer formatting the values as decimal strings with `fmt.Sprintf("%d", value)` to avoid them being printed in hexadecimal.

Applied to files:

  • x/evm/keeper/msg_convert_evm_to_coin.go
📚 Learning: 2024-12-06T11:54:38.275Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2119
File: x/evm/keeper/bank_extension_test.go:67-71
Timestamp: 2024-12-06T11:54:38.275Z
Learning: When writing tests in `x/evm/keeper/bank_extension_test.go`, prefer comparing gas consumption values as formatted strings using `fmt.Sprintf("%d", value)` instead of numeric values directly in `s.Equalf`. This avoids hexadecimal representations that are hard to read in shell outputs.

Applied to files:

  • x/evm/keeper/msg_convert_evm_to_coin.go
📚 Learning: 2024-10-31T18:28:26.466Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2095
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-10-31T18:28:26.466Z
Learning: When implementing the `bankkeeper.Keeper` interface in `x/evm/keeper/bank_extension.go`, methods must use non-pointer receivers to satisfy the interface requirements.

Applied to files:

  • x/evm/keeper/msg_convert_evm_to_coin.go
🧬 Code graph analysis (1)
x/evm/keeper/msg_convert_evm_to_coin.go (11)
x/evm/msg.go (1)
  • Addrs (537-540)
x/evm/statedb/statedb.go (1)
  • StateDB (45-85)
x/evm/embeds/embeds.go (2)
  • SmartContract_ERC20MinterWithMetadataUpdates (71-74)
  • SmartContract_WNIBI (96-99)
x/evm/const.go (2)
  • EVM_MODULE_ADDRESS (106-106)
  • WeiToNative (136-139)
x/evm/keeper/erc20.go (1)
  • Erc20GasLimitExecute (30-30)
x/evm/events.pb.go (6)
  • EventConvertEvmToCoin (528-534)
  • EventConvertEvmToCoin (538-538)
  • EventConvertEvmToCoin (539-541)
  • EventTxLog (128-131)
  • EventTxLog (135-135)
  • EventTxLog (136-138)
x/evm/keeper/evm_state.go (1)
  • EvmState (24-47)
eth/eip55.go (1)
  • EIP55Addr (17-19)
x/evm/keeper/msg_server.go (1)
  • ParseWeiAsMultipleOfMicronibi (435-463)
x/evm/errors.go (1)
  • ErrCanonicalWnibi (54-54)
app/appconst/appconst.go (1)
  • BondDenom (22-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: integration-tests
  • GitHub Check: e2e-evm

Copy link
Contributor

@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

♻️ Duplicate comments (2)
x/evm/keeper/msg_convert_evm_to_coin.go (2)

36-37: Fix 18→6 decimal mismatch when releasing bank coins (over-credit by 1e12).

amount is ERC20 wei (18dp). Bank coins use micro (6dp). Convert with evm.WeiToNative.

-	bankCoins := sdk.NewCoins(sdk.NewCoin(bankDenom, sdkmath.NewIntFromBigInt(amount)))
+	bankAmt := sdkmath.NewIntFromBigInt(evm.WeiToNative(amount))
+	bankCoins := sdk.NewCoins(sdk.NewCoin(bankDenom, bankAmt))

168-178: Fix 18→6 decimal mismatch when minting bank coins (over-credit by 1e12).

balIncrease is ERC20 delta in wei. Convert before mint/send.

-	bankCoin := sdk.NewCoin(bankDenom, sdkmath.NewIntFromBigInt(balIncrease))
+	bankAmt := sdkmath.NewIntFromBigInt(evm.WeiToNative(balIncrease))
+	bankCoin := sdk.NewCoin(bankDenom, bankAmt)
🧹 Nitpick comments (3)
x/evm/keeper/msg_convert_evm_to_coin.go (2)

131-151: Minor: NewEVM message To should target the ERC20 contract; ABI pack here is redundant.

k.ERC20().Transfer packs calldata itself and calls the ERC20 contract. Setting To to the module address and pre-packing contractInput adds noise.

-	contractInput, err := embeds.SmartContract_ERC20MinterWithMetadataUpdates.ABI.Pack("transfer", evm.EVM_MODULE_ADDRESS, amount)
-	if err != nil {
-		return nil, err
-	}
+	// No need to pre-pack input; ERC20().Transfer handles ABI packing.

 	var evmObj *vm.EVM
 	{
 		unusedBigInt := big.NewInt(0)
 		evmMsg := core.Message{
 			From:             sender.Eth,
-			To:               &evm.EVM_MODULE_ADDRESS,
+			To:               &erc20Addr,
 			Nonce:            k.GetAccNonce(ctx, sender.Eth),
 			Value:            unusedBigInt,
 			GasLimit:         Erc20GasLimitExecute,
 			GasPrice:         unusedBigInt,
 			GasFeeCap:        unusedBigInt,
 			GasTipCap:        unusedBigInt,
-			Data:             contractInput,
+			Data:             nil,

317-325: Emit tx logs and update bloom for WNIBI path to match other flows.

Improves observability and consistency with coin-/ERC20-originated paths.

 	_ = ctx.EventManager().EmitTypedEvent(&evm.EventConvertEvmToCoin{
 		Sender:               sender.Bech32.String(),
 		Erc20ContractAddress: erc20.Hex(),
 		ToAddress:            toAddrBech32.String(),
 		BankCoin:             withdrawnMicronibi,
 		SenderEthAddr:        sender.Eth.Hex(),
 	})
 
+	// Emit EVM logs and update bloom (mirrors other paths)
+	if evmResp != nil {
+		if err := ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs}); err == nil {
+			k.updateBlockBloom(ctx, evmResp, uint64(k.EvmState.BlockTxIndex.GetOr(ctx, 0)))
+		}
+	}
x/evm/evmmodule/genesis_test.go (1)

205-206: Comment nit: direction is ERC20 → bank coin.

Clarify to avoid confusion when scanning tests.

-	s.T().Log("Send fungible token coins from bank to evm")
+	s.T().Log("Convert ERC20 to bank coin (EVM → Coin)")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4860545 and bad895d.

📒 Files selected for processing (4)
  • x/evm/evmmodule/genesis_test.go (2 hunks)
  • x/evm/evmtest/erc20.go (3 hunks)
  • x/evm/evmtest/genesis_inject.go (1 hunks)
  • x/evm/keeper/msg_convert_evm_to_coin.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • x/evm/evmtest/genesis_inject.go
  • x/evm/evmtest/erc20.go
🧰 Additional context used
🧠 Learnings (9)
📚 Learning: 2024-12-06T16:09:01.767Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2119
File: x/evm/keeper/bank_extension_test.go:161-165
Timestamp: 2024-12-06T16:09:01.767Z
Learning: In Go test assertions within `x/evm/keeper/bank_extension_test.go`, when comparing `uint64` values using `s.Equalf`, prefer formatting the values as decimal strings with `fmt.Sprintf("%d", value)` to avoid them being printed in hexadecimal.

Applied to files:

  • x/evm/keeper/msg_convert_evm_to_coin.go
  • x/evm/evmmodule/genesis_test.go
📚 Learning: 2024-12-06T11:54:38.275Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2119
File: x/evm/keeper/bank_extension_test.go:67-71
Timestamp: 2024-12-06T11:54:38.275Z
Learning: When writing tests in `x/evm/keeper/bank_extension_test.go`, prefer comparing gas consumption values as formatted strings using `fmt.Sprintf("%d", value)` instead of numeric values directly in `s.Equalf`. This avoids hexadecimal representations that are hard to read in shell outputs.

Applied to files:

  • x/evm/keeper/msg_convert_evm_to_coin.go
  • x/evm/evmmodule/genesis_test.go
📚 Learning: 2024-10-31T18:28:26.466Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2095
File: x/evm/keeper/bank_extension.go:0-0
Timestamp: 2024-10-31T18:28:26.466Z
Learning: When implementing the `bankkeeper.Keeper` interface in `x/evm/keeper/bank_extension.go`, methods must use non-pointer receivers to satisfy the interface requirements.

Applied to files:

  • x/evm/keeper/msg_convert_evm_to_coin.go
📚 Learning: 2024-10-13T05:48:51.771Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2076
File: x/evm/keeper/msg_server.go:115-116
Timestamp: 2024-10-13T05:48:51.771Z
Learning: In the `ApplyEvmTx` function within `x/evm/keeper/msg_server.go`, a zero value for `weiPerGas` is acceptable as it signifies that gas is free. There is no need to check for zero before performing the gas refund in this context.

Applied to files:

  • x/evm/keeper/msg_convert_evm_to_coin.go
📚 Learning: 2025-09-07T16:02:13.718Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2345
File: x/evm/keeper/msg_convert_evm_to_coin.go:311-315
Timestamp: 2025-09-07T16:02:13.718Z
Learning: WNIBI (Wrapped NIBI) does not use module account escrow like regular FunTokens. WNIBI works through direct contract interactions where users deposit/withdraw native NIBI directly to/from the WNIBI contract. The fund transfers use SendCoins between user accounts, not SendCoinsFromModuleToAccount like regular FunToken conversions.

Applied to files:

  • x/evm/keeper/msg_convert_evm_to_coin.go
📚 Learning: 2025-01-17T17:32:27.341Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2165
File: x/evm/evmmodule/genesis_test.go:51-51
Timestamp: 2025-01-17T17:32:27.341Z
Learning: The `NewEVM()` method in TestDeps returns only `*vm.EVM` without an error value.

Applied to files:

  • x/evm/evmmodule/genesis_test.go
📚 Learning: 2025-01-17T17:32:27.341Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2165
File: x/evm/evmmodule/genesis_test.go:51-51
Timestamp: 2025-01-17T17:32:27.341Z
Learning: The `NewEVM()` method in TestDeps returns `(*vm.EVM, *statedb.StateDB)` without an error value. The second return value is often discarded when only the EVM object is needed.

Applied to files:

  • x/evm/evmmodule/genesis_test.go
📚 Learning: 2025-01-17T17:32:33.412Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2165
File: x/evm/keeper/funtoken_from_coin_test.go:30-30
Timestamp: 2025-01-17T17:32:33.412Z
Learning: In test dependencies, `deps.NewEVM()` returns only an EVM object and does not return an error. The method is used in test files to create a new EVM instance for testing purposes.

Applied to files:

  • x/evm/evmmodule/genesis_test.go
📚 Learning: 2025-01-17T17:32:33.412Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2165
File: x/evm/keeper/funtoken_from_coin_test.go:30-30
Timestamp: 2025-01-17T17:32:33.412Z
Learning: In test dependencies, `deps.NewEVM()` returns both an EVM object and a StateDB object with the signature `(*vm.EVM, *statedb.StateDB)`. The method is used in test files to create new EVM and StateDB instances for testing purposes.

Applied to files:

  • x/evm/evmmodule/genesis_test.go
🧬 Code graph analysis (2)
x/evm/keeper/msg_convert_evm_to_coin.go (10)
x/evm/msg.go (1)
  • Addrs (537-540)
x/evm/statedb/statedb.go (1)
  • StateDB (45-85)
x/evm/embeds/embeds.go (2)
  • SmartContract_ERC20MinterWithMetadataUpdates (71-74)
  • SmartContract_WNIBI (96-99)
x/evm/const.go (2)
  • EVM_MODULE_ADDRESS (106-106)
  • WeiToNative (136-139)
x/evm/keeper/erc20.go (1)
  • Erc20GasLimitExecute (30-30)
x/evm/events.pb.go (6)
  • EventConvertEvmToCoin (528-534)
  • EventConvertEvmToCoin (538-538)
  • EventConvertEvmToCoin (539-541)
  • EventTxLog (128-131)
  • EventTxLog (135-135)
  • EventTxLog (136-138)
eth/eip55.go (1)
  • EIP55Addr (17-19)
x/evm/keeper/msg_server.go (1)
  • ParseWeiAsMultipleOfMicronibi (435-463)
x/evm/errors.go (1)
  • ErrCanonicalWnibi (54-54)
app/appconst/appconst.go (1)
  • BondDenom (22-22)
x/evm/evmmodule/genesis_test.go (6)
x/evm/evmtest/test_deps.go (2)
  • NewTestDeps (36-47)
  • TestDeps (28-34)
x/evm/evm.pb.go (3)
  • FunToken (32-41)
  • FunToken (45-45)
  • FunToken (46-48)
x/evm/evmtest/erc20.go (1)
  • FunTokenBalanceAssert (154-160)
x/evm/evmtest/tx.go (1)
  • DeployContract (58-100)
x/evm/tx.pb.go (6)
  • MsgCreateFunToken (480-487)
  • MsgCreateFunToken (491-491)
  • MsgCreateFunToken (492-494)
  • MsgConvertEvmToCoin (675-693)
  • MsgConvertEvmToCoin (697-697)
  • MsgConvertEvmToCoin (698-700)
x/evm/evmmodule/genesis.go (2)
  • ExportGenesis (86-129)
  • InitGenesis (20-83)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: lint
  • GitHub Check: e2e-evm
  • GitHub Check: integration-tests
  • GitHub Check: proto-gen-rs
  • GitHub Check: build
  • GitHub Check: unit-tests
🔇 Additional comments (1)
x/evm/keeper/msg_convert_evm_to_coin.go (1)

269-273: Good: balance check uses truncated withdrawWei.

Prevents false rejections when amount isn’t a multiple of micronibi.

Copy link
Contributor

@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: 2

♻️ Duplicate comments (4)
x/evm/evmmodule/genesis_test.go (1)

84-90: Fix unit mismatch: Bank balance must be in micro (6dp), not ERC-20 wei (18dp).

Use the converter so the assertion matches Bank units.

 		evmtest.FunTokenBalanceAssert{
 			Account:      toUserC,
 			FunToken:     ft,
-			BalanceBank:  amountToSendC,
+			BalanceBank:  evm.WeiToNative(amountToSendC),
 			BalanceERC20: big.NewInt(0),
 			Description:  "C receives amountToSendC as Bank Coins",
 		}.Assert(s.T(), deps, evmObj)
x/evm/keeper/msg_convert_coin_to_evm.go (3)

52-58: StateDB lifecycle: restore previous pointer and only commit if we created it.

Currently you (a) never set k.Bank.StateDB when creating a new StateDB, (b) always nil it on defer (clearing a previously set pointer), and (c) always commit regardless of who created it. This can clobber an existing StateDB and cause unintended commits.

Apply this pattern (restore previous, set only when created, commit only if created):

-	stateDB := k.Bank.StateDB
-	if stateDB == nil {
-		stateDB = k.NewStateDB(ctx, k.TxConfig(ctx, gethcommon.Hash{}))
-	}
-	defer func() {
-		k.Bank.StateDB = nil
-	}()
+	prevStateDB := k.Bank.StateDB
+	stateDB := prevStateDB
+	createdState := false
+	if stateDB == nil {
+		stateDB = k.NewStateDB(ctx, k.TxConfig(ctx, gethcommon.Hash{}))
+		k.Bank.StateDB = stateDB
+		createdState = true
+	}
+	defer func() {
+		// Restore prior pointer regardless of our path.
+		k.Bank.StateDB = prevStateDB
+	}()
@@
-	if err := stateDB.Commit(); err != nil {
-		return nil, fmt.Errorf("failed to commit stateDB: %w", err)
-	}
+	if createdState {
+		if err := stateDB.Commit(); err != nil {
+			return nil, fmt.Errorf("failed to commit stateDB: %w", err)
+		}
+	}

Also applies to: 160-164


216-223: StateDB lifecycle: same issue as above in convertCoinToEvmBornCoin.

Restore previous pointer and only commit if we created the StateDB.

-	stateDB := k.Bank.StateDB
-	if stateDB == nil {
-		stateDB = k.NewStateDB(ctx, txConfig)
-	}
-	defer func() {
-		k.Bank.StateDB = nil
-	}()
+	prevStateDB := k.Bank.StateDB
+	stateDB := prevStateDB
+	createdState := false
+	if stateDB == nil {
+		stateDB = k.NewStateDB(ctx, txConfig)
+		k.Bank.StateDB = stateDB
+		createdState = true
+	}
+	defer func() { k.Bank.StateDB = prevStateDB }()
@@
-	if err = stateDB.Commit(); err != nil {
+	if createdState {
+		if err = stateDB.Commit(); err != nil {
 			return nil, sdkioerrors.Wrap(err, "failed to commit stateDB")
-	}
+		}
+	}

Also applies to: 244-246


276-283: StateDB lifecycle: same issue in convertCoinToEvmBornERC20.

Restore previous pointer and only commit if we created it.

-	stateDB := k.Bank.StateDB
-	if stateDB == nil {
-		stateDB = k.NewStateDB(ctx, k.TxConfig(ctx, gethcommon.Hash{}))
-	}
-	defer func() {
-		k.Bank.StateDB = nil
-	}()
+	prevStateDB := k.Bank.StateDB
+	stateDB := prevStateDB
+	createdState := false
+	if stateDB == nil {
+		stateDB = k.NewStateDB(ctx, k.TxConfig(ctx, gethcommon.Hash{}))
+		k.Bank.StateDB = stateDB
+		createdState = true
+	}
+	defer func() { k.Bank.StateDB = prevStateDB }()
@@
-	if err := stateDB.Commit(); err != nil {
-		return nil, sdkioerrors.Wrap(err, "failed to commit stateDB")
-	}
+	if createdState {
+		if err := stateDB.Commit(); err != nil {
+			return nil, sdkioerrors.Wrap(err, "failed to commit stateDB")
+		}
+	}

Also applies to: 348-350

🧹 Nitpick comments (10)
x/tokenfactory/cli/tx.go (1)

412-416: Prefer %q over %+q for user-facing file paths

%+q escapes non-ASCII into \uXXXX, which can reduce readability in CLI errors. Unless you explicitly want escaped Unicode, stick to %q (or %s) for clearer output and consistency with common Go CLI ergonomics.

-        return md, fmt.Errorf("read file %+q: %w", path, err)
+        return md, fmt.Errorf("read file %q: %w", path, err)

-        return md, fmt.Errorf("unmarshal %+q as bank.Metadata: %w", path, err)
+        return md, fmt.Errorf("unmarshal %q as bank.Metadata: %w", path, err)
x/evm/evmmodule/genesis_test.go (6)

188-197: Avoid Int64 casts with big.Int; use big arithmetic for A+B.

Prevents precision/overflow pitfalls and reads clearer.

 	{
 		evmObj, _ := deps.NewEVM()
 		assertBalsOfAB(deps, evmObj)
+		sentToAB := new(big.Int).Add(
+			new(big.Int).Set(amountToSendA),
+			new(big.Int).Set(amountToSendB),
+		)
 		evmtest.FunTokenBalanceAssert{
 			Account:     deps.Sender.EthAddr,
 			FunToken:    ft,
 			BalanceBank: big.NewInt(0),
-			BalanceERC20: new(big.Int).Sub(
-				totalSupply,
-				big.NewInt(amountToSendA.Int64()+amountToSendB.Int64()),
-			),
+			BalanceERC20: new(big.Int).Sub(new(big.Int).Set(totalSupply), sentToAB),
 			Description: "Sender has total supply minus A and B",
 		}.Assert(s.T(), deps, evmObj)

99-107: Avoid Int64 casts with big.Int; use big arithmetic for A+B+C.

Same rationale as above.

-		evmtest.FunTokenBalanceAssert{
+		sentToABC := new(big.Int).Add(
+			new(big.Int).Add(
+				new(big.Int).Set(amountToSendA),
+				new(big.Int).Set(amountToSendB),
+			),
+			new(big.Int).Set(amountToSendC),
+		)
+		evmtest.FunTokenBalanceAssert{
 			Account:     depsSender.EthAddr,
 			FunToken:    ft,
 			BalanceBank: big.NewInt(0),
-			BalanceERC20: new(big.Int).Sub(
-				totalSupply,
-				big.NewInt(amountToSendA.Int64()+amountToSendB.Int64()+amountToSendC.Int64()),
-			),
+			BalanceERC20: new(big.Int).Sub(new(big.Int).Set(totalSupply), sentToABC),
 			Description: "Sender should lose amountToSendC",
 		}.Assert(s.T(), deps, evmObj)

150-150: Typo in log message (“er20”).

Minor clarity fix.

-	s.T().Logf("Create FunToken from the er20 %s", erc20Addr)
+	s.T().Logf("Create FunToken from the erc20 %s", erc20Addr)

206-206: Incorrect direction in log message.

This flow is ERC-20 → Bank via MsgConvertEvmToCoin.

-	s.T().Log("Send fungible token coins from bank to evm")
+	s.T().Log("Convert ERC-20 to Bank coins (MsgConvertEvmToCoin)")

226-233: Variable name typo (“Gensis”).

Rename for readability.

-	bankGensisState := deps.App.BankKeeper.ExportGenesis(deps.Ctx)
+	bankGenesisState := deps.App.BankKeeper.ExportGenesis(deps.Ctx)
@@
-		deps.App.BankKeeper.InitGenesis(deps.Ctx, bankGensisState)
+		deps.App.BankKeeper.InitGenesis(deps.Ctx, bankGenesisState)

239-244: Minor: standardize EIP55 string compare.

Compare String() to String() (or Hex to Hex) on both sides to avoid format drift.

-	s.Equal(ft.Erc20Addr.Hex(), funTokens[0].Erc20Addr.String())
+	s.Equal(ft.Erc20Addr.String(), funTokens[0].Erc20Addr.String())
x/evm/keeper/msg_convert_coin_to_evm.go (3)

145-148: Fix user-facing error message (direction is wrong).

This path transfers WNIBI to the recipient; it does not convert WNIBI→NIBI.

-		return resp, fmt.Errorf("failed to convert WNIBI to NIBI: %w", err)
+		return resp, fmt.Errorf("failed to transfer WNIBI to recipient: %w", err)
-		err = fmt.Errorf("failed to convert WNIBI to NIBI: VmError: %s", evmResp.VmError)
+		err = fmt.Errorf("failed to transfer WNIBI to recipient: VmError: %s", evmResp.VmError)

239-243: Include VM error details for failed mint.

Adding VmError greatly improves diagnosability.

-	if evmResp.Failed() {
-		return nil,
-			fmt.Errorf("failed to mint erc-20 tokens of contract %s", erc20Addr.String())
-	}
+	if evmResp.Failed() {
+		return nil, fmt.Errorf("failed to mint ERC-20 tokens of contract %s: VmError: %s", erc20Addr.String(), evmResp.VmError)
+	}

295-301: Fix comment typos/encoding.

"ΓåÆ" artifacts and "ERc20" → "ERC20".

-	// 3 | In the FunToken ERC20 → BC conversion process that preceded this
+	// 3 | In the FunToken ERC20 -> Bank Coin conversion process that preceded this
@@
-	// on the sum of the FunToken's bank and ERC20 supply, we burn the coins here
-	// in the BC → ERC20 conversion.
+	// on the sum of the FunToken's bank and ERC20 supply, we burn the coins here
+	// in the Bank Coin -> ERC20 conversion.
@@
-	// This should never fail due to the EVM account lacking ERc20 fund because
+	// This should never fail due to the EVM account lacking ERC20 funds because

Also applies to: 303-306

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bad895d and d5f217d.

📒 Files selected for processing (5)
  • x/evm/evmmodule/genesis_test.go (2 hunks)
  • x/evm/keeper/msg_convert_coin_to_evm.go (1 hunks)
  • x/evm/keeper/msg_convert_evm_to_coin.go (1 hunks)
  • x/evm/msg.go (7 hunks)
  • x/tokenfactory/cli/tx.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • x/evm/msg.go
  • x/evm/keeper/msg_convert_evm_to_coin.go
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-01-17T17:32:27.341Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2165
File: x/evm/evmmodule/genesis_test.go:51-51
Timestamp: 2025-01-17T17:32:27.341Z
Learning: The `NewEVM()` method in TestDeps returns only `*vm.EVM` without an error value.

Applied to files:

  • x/evm/evmmodule/genesis_test.go
📚 Learning: 2025-01-17T17:32:27.341Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2165
File: x/evm/evmmodule/genesis_test.go:51-51
Timestamp: 2025-01-17T17:32:27.341Z
Learning: The `NewEVM()` method in TestDeps returns `(*vm.EVM, *statedb.StateDB)` without an error value. The second return value is often discarded when only the EVM object is needed.

Applied to files:

  • x/evm/evmmodule/genesis_test.go
📚 Learning: 2025-01-17T17:32:33.412Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2165
File: x/evm/keeper/funtoken_from_coin_test.go:30-30
Timestamp: 2025-01-17T17:32:33.412Z
Learning: In test dependencies, `deps.NewEVM()` returns only an EVM object and does not return an error. The method is used in test files to create a new EVM instance for testing purposes.

Applied to files:

  • x/evm/evmmodule/genesis_test.go
📚 Learning: 2025-01-17T17:32:33.412Z
Learnt from: k-yang
PR: NibiruChain/nibiru#2165
File: x/evm/keeper/funtoken_from_coin_test.go:30-30
Timestamp: 2025-01-17T17:32:33.412Z
Learning: In test dependencies, `deps.NewEVM()` returns both an EVM object and a StateDB object with the signature `(*vm.EVM, *statedb.StateDB)`. The method is used in test files to create new EVM and StateDB instances for testing purposes.

Applied to files:

  • x/evm/evmmodule/genesis_test.go
📚 Learning: 2024-12-06T16:09:01.767Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2119
File: x/evm/keeper/bank_extension_test.go:161-165
Timestamp: 2024-12-06T16:09:01.767Z
Learning: In Go test assertions within `x/evm/keeper/bank_extension_test.go`, when comparing `uint64` values using `s.Equalf`, prefer formatting the values as decimal strings with `fmt.Sprintf("%d", value)` to avoid them being printed in hexadecimal.

Applied to files:

  • x/evm/evmmodule/genesis_test.go
📚 Learning: 2024-12-06T11:54:38.275Z
Learnt from: Unique-Divine
PR: NibiruChain/nibiru#2119
File: x/evm/keeper/bank_extension_test.go:67-71
Timestamp: 2024-12-06T11:54:38.275Z
Learning: When writing tests in `x/evm/keeper/bank_extension_test.go`, prefer comparing gas consumption values as formatted strings using `fmt.Sprintf("%d", value)` instead of numeric values directly in `s.Equalf`. This avoids hexadecimal representations that are hard to read in shell outputs.

Applied to files:

  • x/evm/evmmodule/genesis_test.go
🧬 Code graph analysis (2)
x/evm/evmmodule/genesis_test.go (6)
x/evm/evmtest/test_deps.go (2)
  • NewTestDeps (36-47)
  • TestDeps (28-34)
x/evm/evm.pb.go (3)
  • FunToken (32-41)
  • FunToken (45-45)
  • FunToken (46-48)
x/evm/evmtest/erc20.go (1)
  • FunTokenBalanceAssert (154-160)
x/evm/evmtest/tx.go (1)
  • DeployContract (58-100)
x/evm/tx.pb.go (6)
  • MsgCreateFunToken (480-487)
  • MsgCreateFunToken (491-491)
  • MsgCreateFunToken (492-494)
  • MsgConvertEvmToCoin (675-693)
  • MsgConvertEvmToCoin (697-697)
  • MsgConvertEvmToCoin (698-700)
x/evm/evmmodule/genesis.go (2)
  • ExportGenesis (86-129)
  • InitGenesis (20-83)
x/evm/keeper/msg_convert_coin_to_evm.go (9)
x/evm/tx.pb.go (6)
  • MsgConvertCoinToEvm (582-589)
  • MsgConvertCoinToEvm (593-593)
  • MsgConvertCoinToEvm (594-596)
  • MsgConvertCoinToEvmResponse (638-639)
  • MsgConvertCoinToEvmResponse (643-643)
  • MsgConvertCoinToEvmResponse (644-646)
eth/eth_account.go (1)
  • NibiruAddrToEthAddr (19-21)
x/evm/statedb/statedb.go (1)
  • StateDB (45-85)
x/evm/statedb/config.go (1)
  • TxConfig (15-20)
x/evm/errors.go (1)
  • ErrCanonicalWnibi (54-54)
x/evm/const.go (2)
  • NativeToWei (123-126)
  • EVM_MODULE_ADDRESS (106-106)
x/evm/embeds/embeds.go (2)
  • SmartContract_WNIBI (96-99)
  • SmartContract_ERC20MinterWithMetadataUpdates (71-74)
x/evm/keeper/erc20.go (1)
  • Erc20GasLimitExecute (30-30)
x/evm/events.pb.go (6)
  • EventConvertCoinToEvm (291-296)
  • EventConvertCoinToEvm (300-300)
  • EventConvertCoinToEvm (301-303)
  • EventTxLog (128-131)
  • EventTxLog (135-135)
  • EventTxLog (136-138)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: integration-tests
  • GitHub Check: e2e-evm
  • GitHub Check: lint
  • GitHub Check: proto-gen-rs
  • GitHub Check: unit-tests
  • GitHub Check: build
🔇 Additional comments (1)
x/evm/evmmodule/genesis_test.go (1)

79-83: Good: recreate EVM instance before assertions.

Prevents stale state after (re)genesis or keeper resets.

Comment on lines +39 to +41
// isTx: value to use for commit in any EVM calls
isTx = true

Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Make the conversion atomic: avoid early commit in Step 1.

Passing commit=true to CallContractWithInput for the deposit can persist the WNIBI mint even if the subsequent transfer fails, breaking atomicity.

Proposed change: keep both deposit and transfer in the same StateDB transaction and commit once at the end.

-		// isTx: value to use for commit in any EVM calls
-		isTx = true
+		// isTx: do NOT commit mid-flow; commit once at the end for atomicity
+		isTx = false
@@
-	evmResp, err := k.CallContractWithInput(
+	evmResp, err := k.CallContractWithInput(
 		ctx,
 		evmObj,
 		senderEthAddr,  /* fromAcc */
 		&erc20.Address, /* contract */
-		isTx,           /* commit */
+		isTx,           /* commit */
 		contractInput,
 		Erc20GasLimitExecute,
 		depositWei,
 	)

To confirm Transfer does not commit on its own, please inspect its implementation:

Also applies to: 105-114


🏁 Script executed:

#!/bin/bash
# Verify whether ERC20.Transfer commits internally
rg -nP -C3 'func\s+\(.*\)\s+Transfer\(' x/evm
rg -nP -C3 'CallContractWithInput\s*\(' x/evm/keeper/erc20.go

Length of output: 1709


Defer state commit until after both mint and transfer
In x/evm/keeper/msg_convert_coin_to_evm.go (around lines 39–41 and 105–114), change the deposit call’s commit flag to false so the WNIBI mint and subsequent ERC-20 transfer run in a single StateDB transaction.

🤖 Prompt for AI Agents
In x/evm/keeper/msg_convert_coin_to_evm.go around lines 39–41 and 105–114, the
deposit call currently sets the commit flag to true which causes an immediate
state commit; change that deposit call’s commit parameter to false so the WNIBI
mint and the subsequent ERC‑20 transfer execute in the same StateDB transaction,
and ensure the state is committed only after both operations complete (i.e., set
commit = false on deposit and keep the existing commit/flush logic after
mint+transfer).

@Unique-Divine Unique-Divine merged commit 65a3c98 into main Sep 7, 2025
12 checks passed
@Unique-Divine Unique-Divine deleted the mat/evm-to-coin branch September 7, 2025 19:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet