Near Learn

NEP-297: Events

NEP-297 events standard on NEAR: the EVENT_JSON: log prefix, standard/version/event/data fields, token event names, and how it differs from Solidity events.

Intermediate6 min read3-question check

NEAR has no native event type. A contract can only write log lines (strings) to the receipt outcome. NEP-297 standardises one format for those lines so indexers, explorers and wallets can recognise structured events without knowing anything about your contract.

An event is a single log line made of the prefix EVENT_JSON: followed immediately by a JSON object with four fields: standard, version, event and (optionally) data.

An NEP-141 transfer event (pretty-printed here; the real log is a single line)
JSON
EVENT_JSON:{
  "standard": "nep141",
  "version": "1.0.0",
  "event": "ft_transfer",
  "data": [
    { "old_owner_id": "alice.near", "new_owner_id": "bob.near", "amount": "1000000", "memo": "lunch" }
  ]
}
FieldMeaning
standardName of the standard, lowercase, e.g. nep141, nep171, nep245 — or your own name for custom events.
versionSemver of that standard’s event format, e.g. 1.0.0. Lets consumers handle format changes.
eventEvent type, e.g. ft_mint, ft_transfer, ft_burn, nft_mint, nft_transfer, nft_burn.
dataPayload defined by the standard. The token standards use an array, so one log can describe several mints/transfers.

Emitting events in Rust#

Solidity events vs a typed NEP-297 event
Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Dex {
    event Swap(address indexed trader, address tokenIn, address tokenOut,
               uint256 amountIn, uint256 amountOut);

    function swap(/* ... */) external {
        // ...
        emit Swap(msg.sender, address(0), address(0), 0, 0);
    }
}
Rust
use near_sdk::json_types::U128;
use near_sdk::{env, near, AccountId};

#[near(event_json(standard = "mydex"))]
pub enum DexEvent {
    #[event_version("1.0.0")]
    Swap { trader: AccountId, token_in: AccountId, token_out: AccountId, amount_in: U128, amount_out: U128 },
}

#[near(contract_state)]
#[derive(Default)]
pub struct Dex {}

#[near]
impl Dex {
    pub fn swap(&mut self, token_in: AccountId, token_out: AccountId, amount_in: U128) {
        let amount_out = U128(0); // ...do the swap...
        DexEvent::Swap {
            trader: env::predecessor_account_id(),
            token_in,
            token_out,
            amount_in,
            amount_out,
        }
        .emit();
        // logs: EVENT_JSON:{"standard":"mydex","version":"1.0.0","event":"swap","data":{...}}
    }
}
Coming from EVM

emit ↔ .emit(), which serialises the variant and calls env::log_str with the EVENT_JSON: prefix for you.

For token standards use the ready-made types in near-contract-standards (e.g. FtMint, FtTransfer, NftMint), which produce exactly the shapes wallets expect — and the reference FungibleToken / NonFungibleToken already emit transfer events.

Check yourself

3 questions · progress saved in this browser

  1. 1.Which log line is a valid NEP-297 event?
  2. 2.How does an indexer find all NEP-171 mints, given that NEAR has no indexed topics?
  3. 3.Why does the version field matter?