Skip to content

Commit

Permalink
feat(evm-precompile): Emit EVM events created to reflect the ABCI eve…
Browse files Browse the repository at this point in the history
…nts that occur outside the EVM to make sure that block explorers and indexers can find indexed ABCI event information. (#2125)

* test(evm): more bank extension tests

* chrore: changelog

* chore: TODO comments and revert blockGasMeter changes

* start ABCI event solidity

* 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.

* fix: use event log parsing rules from geth/.../abi/bind Go bindings and fix msg_server gas

* wip!: checkpoint on ABI-less data field in event log

* fix: use event log parsing rules from geth/.../abi/bind Go bindings
  • Loading branch information
Unique-Divine authored Dec 30, 2024
1 parent 1a256f2 commit 666a58d
Show file tree
Hide file tree
Showing 20 changed files with 497 additions and 85 deletions.
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.
- [#2129](https://github.com/NibiruChain/nibiru/pull/2129) - fix(evm): issue with infinite recursion in erc20 funtoken contracts

#### 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"

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": "string",
"name": "abciEvent",
"type": "string"
}
],
"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": "string",
"name": "abciEvent",
"type": "string"
}
],
"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": "string",
"name": "abciEvent",
"type": "string"
}
],
"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 abciEvent JSON object string with the event type and fields of an
/// ABCI event.
event AbciEvent(string indexed eventType, string abciEvent);
}
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);
}
13 changes: 13 additions & 0 deletions x/evm/precompile/funtoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ func (p precompileFunToken) Run(
// Gracefully handles "out of gas"
defer HandleOutOfGasPanic(&err)()

abciEventsStartIdx := len(startResult.CacheCtx.EventManager().Events())

method := startResult.Method
switch PrecompileMethod(method.Name) {
case FunTokenMethod_sendToBank:
Expand All @@ -80,6 +82,17 @@ func (p precompileFunToken) Run(
return nil, err
}

// Emit extra events for the EVM if this is a transaction
// https://github.com/NibiruChain/nibiru/issues/2121
if isMutation[PrecompileMethod(startResult.Method.Name)] {
EmitEventAbciEvents(
startResult.CacheCtx,
startResult.StateDB,
startResult.CacheCtx.EventManager().Events()[abciEventsStartIdx:],
p.Address(),
)
}

// Gas consumed by a local gas meter
contract.UseGas(startResult.CacheCtx.GasMeter().GasConsumed())
return bz, err
Expand Down
Loading

0 comments on commit 666a58d

Please sign in to comment.