Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(evm-precompile): Emit EVM events created to reflect the ABCI events that occur outside the EVM to make sure that block explorers and indexers can find indexed ABCI event information. #2125

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lts/jod
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ only on the "bankkeeper.BaseKeeper"'s gas consumption.
Remove unnecessary argument in the `VerifyFee` function, which returns the token
payment required based on the effective fee from the tx data. Improve
documentation.
- [#2125](https://github.com/NibiruChain/nibiru/pull/2125) - feat(evm-precompile):Emit EVM events created to reflect the ABCI events that occur outside the EVM to make sure that block explorers and indexers can find indexed ABCI event information.

#### Nibiru EVM | Before Audit 2 - 2024-12-06

Expand Down
10 changes: 5 additions & 5 deletions contrib/bashlib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,26 @@ export COLOR_BRIGHT_WHITE="\033[97m"

# log_debug: Simple wrapper for `echo` with a DEBUG prefix.
log_debug() {
echo "${COLOR_CYAN}DEBUG${COLOR_RESET}" "$@"
echo -e "${COLOR_CYAN}DEBUG${COLOR_RESET}" "$@"
}

# log_error: ERROR messages in red, output to stderr.
log_error() {
echo "❌ ${COLOR_RED}ERROR:${COLOR_RESET}" "$@" >&2
echo -e "❌ ${COLOR_RED}ERROR:${COLOR_RESET}" "$@" >&2
}

log_success() {
echo "${COLOR_GREEN}✅ SUCCESS:${COLOR_RESET}" "$@"
echo -e "${COLOR_GREEN}✅ SUCCESS:${COLOR_RESET}" "$@"
}

# log_warning: WARNING messages represent non-critical issues that might not
# require immediate action but should be noted as points of concern or failure.
log_warning() {
echo "${COLOR_YELLOW}WARNING${COLOR_RESET}" "$@" >&2
echo -e "${COLOR_YELLOW}WARNING${COLOR_RESET}" "$@" >&2
}

log_info() {
echo "${COLOR_MAGENTA}INFO${COLOR_RESET}" "$@"
echo -e "${COLOR_MAGENTA}INFO${COLOR_RESET}" "$@"
}

# —————————————————————————————————————————————————
Expand Down
24 changes: 18 additions & 6 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,29 @@ build:
clean-cache:
go clean -cache -testcache -modcache

alias b := build

# Generate protobuf code (Golang) for Nibiru
# Generate protobuf-based types in Golang
proto-gen:
#!/usr/bin/env bash
make proto-gen

alias proto := proto-gen
# Generate Solidity artifacts for x/evm/embeds
gen-embeds:
#!/usr/bin/env bash
source contrib/bashlib.sh

embeds_dir="x/evm/embeds"
log_info "Begin to compile Solidity in $embeds_dir"
which_ok npm
log_info "Using system node version: $(npm exec -- node -v)"

cd "$embeds_dir" || (log_error "path $embeds_dir not found" && exit)
npx hardhat compile
log_success "Compiled Solidity in $embeds_dir"

Comment on lines +28 to +41
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance error handling in gen-embeds recipe

The recipe should handle common failure scenarios:

  • Node.js version mismatch
  • Missing dependencies
  • Compilation errors
 gen-embeds:
   #!/usr/bin/env bash
   source contrib/bashlib.sh
 
   embeds_dir="x/evm/embeds"
   log_info "Begin to compile Solidity in $embeds_dir"
-  which_ok npm
+  if ! which_ok npm; then
+    log_error "npm not found. Please install Node.js"
+    exit 1
+  fi
+
+  if [ ! -f "package.json" ]; then
+    log_error "package.json not found in $embeds_dir"
+    exit 1
+  fi
+
   log_info "Using system node version: $(npm exec -- node -v)"
 
   cd "$embeds_dir" || (log_error "path $embeds_dir not found" && exit)
+  npm install || (log_error "Failed to install dependencies" && exit 1)
   npx hardhat compile
   log_success "Compiled Solidity in $embeds_dir"

Committable suggestion skipped: line range outside the PR's diff.

alias gen-proto := proto-gen

# Build protobuf types (Rust)
proto-rs:
# Generate protobuf-based types in Rust
gen-proto-rs:
bash proto/buf.gen.rs.sh

lint:
Expand Down
19 changes: 19 additions & 0 deletions x/evm/embeds/artifacts/contracts/IFunToken.sol/IFunToken.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,25 @@
"contractName": "IFunToken",
"sourceName": "contracts/IFunToken.sol",
"abi": [
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "string",
"name": "eventType",
"type": "string"
},
{
"indexed": false,
"internalType": "bytes",
"name": "attrs",
"type": "bytes"
}
],
"name": "AbciEvent",
"type": "event"
},
{
"inputs": [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"_format": "hh-sol-artifact-1",
"contractName": "INibiruEvm",
"sourceName": "contracts/NibiruEvmUtils.sol",
"abi": [
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "string",
"name": "eventType",
"type": "string"
},
{
"indexed": false,
"internalType": "bytes",
"name": "attrs",
"type": "bytes"
}
],
"name": "AbciEvent",
"type": "event"
}
],
"bytecode": "0x",
"deployedBytecode": "0x",
"linkReferences": {},
"deployedLinkReferences": {}
}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

25 changes: 22 additions & 3 deletions x/evm/embeds/artifacts/contracts/Wasm.sol/IWasm.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,25 @@
"contractName": "IWasm",
"sourceName": "contracts/Wasm.sol",
"abi": [
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "string",
"name": "eventType",
"type": "string"
},
{
"indexed": false,
"internalType": "bytes",
"name": "attrs",
"type": "bytes"
}
],
"name": "AbciEvent",
"type": "event"
},
{
"inputs": [
{
Expand All @@ -28,7 +47,7 @@
"type": "uint256"
}
],
"internalType": "struct IWasm.BankCoin[]",
"internalType": "struct INibiruEvm.BankCoin[]",
"name": "funds",
"type": "tuple[]"
}
Expand Down Expand Up @@ -71,7 +90,7 @@
"type": "uint256"
}
],
"internalType": "struct IWasm.BankCoin[]",
"internalType": "struct INibiruEvm.BankCoin[]",
"name": "funds",
"type": "tuple[]"
}
Expand Down Expand Up @@ -127,7 +146,7 @@
"type": "uint256"
}
],
"internalType": "struct IWasm.BankCoin[]",
"internalType": "struct INibiruEvm.BankCoin[]",
"name": "funds",
"type": "tuple[]"
}
Expand Down
4 changes: 3 additions & 1 deletion x/evm/embeds/contracts/IFunToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ pragma solidity >=0.8.19;
address constant FUNTOKEN_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000800;
IFunToken constant FUNTOKEN_PRECOMPILE = IFunToken(FUNTOKEN_PRECOMPILE_ADDRESS);

import "./NibiruEvmUtils.sol";

/// @dev Implements the functionality for sending ERC20 tokens and bank
/// coins to various Nibiru accounts using either the Nibiru Bech32 address
/// using the "FunToken" mapping between the ERC20 and bank.
interface IFunToken {
interface IFunToken is INibiruEvm {
/// @dev sendToBank sends ERC20 tokens as coins to a Nibiru base account
/// @param erc20 - the address of the ERC20 token contract
/// @param amount - the amount of tokens to send
Expand Down
24 changes: 24 additions & 0 deletions x/evm/embeds/contracts/NibiruEvmUtils.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

/// @notice Interface defining the AbciEvent for interoperability between
/// Ethereum and the ABCI (Application Blockchain Interface).
interface INibiruEvm {
struct BankCoin {
string denom;
uint256 amount;
}

/// @notice Event emitted to in precompiled contracts to relay information
/// from the ABCI to the EVM logs and indexers. Consumers of this event should
/// decode the `attrs` parameter based on the `eventType` context.
///
/// @param eventType An identifier type of the event, used for indexing.
/// Event types indexable with CometBFT indexer are in snake case like
/// "pending_ethereum_tx" or "message", while protobuf typed events use the
/// proto message name as their event type (e.g.
/// "eth.evm.v1.EventEthereumTx").
/// @param attrs Arbitrary data payload associated with the event, typically
/// encoding state changes or metadata.
event AbciEvent(string indexed eventType, bytes attrs);
}
114 changes: 55 additions & 59 deletions x/evm/embeds/contracts/Wasm.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,69 +5,65 @@ address constant WASM_PRECOMPILE_ADDRESS = 0x00000000000000000000000000000000000

IWasm constant WASM_PRECOMPILE = IWasm(WASM_PRECOMPILE_ADDRESS);

interface IWasm {
struct BankCoin {
string denom;
uint256 amount;
}
import "./NibiruEvmUtils.sol";

/// @notice Invoke a contract's "ExecuteMsg", which corresponds to
/// "wasm/types/MsgExecuteContract". This enables arbitrary smart contract
/// execution using the Wasm VM from the EVM.
/// @param contractAddr nibi-prefixed Bech32 address of the wasm contract
/// @param msgArgs JSON encoded wasm execute invocation
/// @param funds Optional funds to supply during the execute call. It's
/// uncommon to use this field, so you'll pass an empty array most of the time.
/// @dev The three non-struct arguments are more gas efficient than encoding a
/// single argument as a WasmExecuteMsg.
function execute(
string memory contractAddr,
bytes memory msgArgs,
BankCoin[] memory funds
) payable external returns (bytes memory response);
interface IWasm is INibiruEvm {
/// @notice Invoke a contract's "ExecuteMsg", which corresponds to
/// "wasm/types/MsgExecuteContract". This enables arbitrary smart contract
/// execution using the Wasm VM from the EVM.
/// @param contractAddr nibi-prefixed Bech32 address of the wasm contract
/// @param msgArgs JSON encoded wasm execute invocation
/// @param funds Optional funds to supply during the execute call. It's
/// uncommon to use this field, so you'll pass an empty array most of the time.
/// @dev The three non-struct arguments are more gas efficient than encoding a
/// single argument as a WasmExecuteMsg.
function execute(
string memory contractAddr,
bytes memory msgArgs,
INibiruEvm.BankCoin[] memory funds
) external payable returns (bytes memory response);

struct WasmExecuteMsg {
string contractAddr;
bytes msgArgs;
BankCoin[] funds;
}
struct WasmExecuteMsg {
string contractAddr;
bytes msgArgs;
INibiruEvm.BankCoin[] funds;
}

/// @notice Identical to "execute", except for multiple contract calls.
function executeMulti(
WasmExecuteMsg[] memory executeMsgs
) payable external returns (bytes[] memory responses);
/// @notice Identical to "execute", except for multiple contract calls.
function executeMulti(
WasmExecuteMsg[] memory executeMsgs
) external payable returns (bytes[] memory responses);

/// @notice Query the public API of another contract at a known address (with
/// known ABI).
/// Implements smart query, the "WasmQuery::Smart" variant from "cosmwas_std".
/// @param contractAddr nibi-prefixed Bech32 address of the wasm contract
/// @param req JSON encoded query request
/// @return response Returns whatever type the contract returns (caller should
/// know), wrapped in a JSON encoded contract result.
function query(
string memory contractAddr,
bytes memory req
) external view returns (bytes memory response);
/// @notice Query the public API of another contract at a known address (with
/// known ABI).
/// Implements smart query, the "WasmQuery::Smart" variant from "cosmwas_std".
/// @param contractAddr nibi-prefixed Bech32 address of the wasm contract
/// @param req JSON encoded query request
/// @return response Returns whatever type the contract returns (caller should
/// know), wrapped in a JSON encoded contract result.
function query(
string memory contractAddr,
bytes memory req
) external view returns (bytes memory response);

/// @notice Query the raw kv-store of the contract.
/// Implements raw query, the "WasmQuery::Raw" variant from "cosmwas_std".
/// @param contractAddr nibi-prefixed Bech32 address of the wasm contract
/// @param key contract state key. For example, a `cw_storage_plus::Item` of
/// value `Item::new("state")` creates prefix store with key, "state".
/// @return response JSON encoded, raw data stored at that key.
function queryRaw(
string memory contractAddr,
bytes memory key
) external view returns (bytes memory response);

/// @notice InstantiateContract creates a new smart contract instance for the
/// given code id.
function instantiate(
string memory admin,
uint64 codeID,
bytes memory msgArgs,
string memory label,
BankCoin[] memory funds
) payable external returns (string memory contractAddr, bytes memory data);
/// @notice Query the raw kv-store of the contract.
/// Implements raw query, the "WasmQuery::Raw" variant from "cosmwas_std".
/// @param contractAddr nibi-prefixed Bech32 address of the wasm contract
/// @param key contract state key. For example, a `cw_storage_plus::Item` of
/// value `Item::new("state")` creates prefix store with key, "state".
/// @return response JSON encoded, raw data stored at that key.
function queryRaw(
string memory contractAddr,
bytes memory key
) external view returns (bytes memory response);

/// @notice InstantiateContract creates a new smart contract instance for the
/// given code id.
function instantiate(
string memory admin,
uint64 codeID,
bytes memory msgArgs,
string memory label,
INibiruEvm.BankCoin[] memory funds
) external payable returns (string memory contractAddr, bytes memory data);
}
7 changes: 7 additions & 0 deletions x/evm/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper_test
import (
"testing"

"github.com/rs/zerolog/log"
"github.com/stretchr/testify/suite"
)

Expand All @@ -14,3 +15,9 @@ type Suite struct {
func TestSuite(t *testing.T) {
suite.Run(t, new(Suite))
}

var _ suite.SetupTestSuite = (*Suite)(nil)

func (s *Suite) SetupTest() {
log.Log().Msgf("SetupTest %v", s.T().Name())
}
3 changes: 0 additions & 3 deletions x/evm/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,6 @@ func (k *Keeper) EthereumTx(
return nil, errors.Wrapf(err, "error refunding leftover gas to sender %s", evmMsg.From())
}

// reset the gas meter for current TxMsg (EthereumTx)
k.ResetGasMeterAndConsumeGas(ctx, blockGasUsed)

err = k.EmitEthereumTxEvents(ctx, tx.To(), tx.Type(), evmMsg, evmResp)
if err != nil {
return nil, errors.Wrap(err, "error emitting ethereum tx events")
Expand Down
Loading
Loading