Skip to content
Open
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
132 changes: 132 additions & 0 deletions neps/nep-0638.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
---
NEP: 638
Title: `chain_id()` host function
Authors: Arseny Mitin <[email protected]>
Status: Draft
Type: Protocol
Version: 1.0.0
Created: 2026-02-02
LastUpdated: 2026-02-02
---

## Summary

This NEP proposes adding a new `chain_id` host function, so that smart-contracts
can retrieve the identifier of Near chain they are being executed on.

## Motivation

With [NEP-616](./nep-0616.md) it's now possible to have account-abstracted
[wallet-contracts](./nep-0616.md#wallet-extentions) deployed at [deterministic
AccountIds](./nep-0616.md#deterministic-accountids). These wallet-contracts are
responsible themselves for signature verification, nonce tracking and etc.

While it enables for true account abstraction, smart-contracts on Near currently
still lack knowledge of `chain_id` that they are being executed on. As a result,
wallet-contracts do not have a native protection against replaying messages
between mainnet, testnet and other networks. The existing [workarounds](#alternatives)
come at a cost of poor UX.

This NEP removes this limitation by introduing a new `chain_id` host function, enabling
for account abstraction without UX tradeoffs.

## Specification

The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).

### Host Function

```rust
extern "C" {
/// Get the current chain_id.
///
/// Writes the current chain_id as UTF-8 encoded string into register
/// `register_id`.
fn chain_id(register_id: u64);
}
```

### Gas cost

The gas cost MUST be computed as:

`base + write_register_base + write_register_byte * num_bytes`

## Reference Implementation

Having `chain_id` [field](https://github.com/near/nearcore/blob/b8ab0f5d578617eb287030e77ab17be512a5f8ac/core/chain-configs/src/genesis_config.rs#L120-L122)
already present in validator config, it's possible to make this value accessible
by smart-contracts in the runtime.

## Security Implications

As already [stated](https://github.com/near/nearcore/blob/b8ab0f5d578617eb287030e77ab17be512a5f8ac/core/chain-configs/src/genesis_config.rs#L120-L122)
in nearcore implementation, `chain_id` MUST be unique for every blockchain.
Otherwise, messages can be replayed between networks with same chain_ids.

## Alternatives

An alternative would be to encode "virtual" `chain_id` as a part of
initializaton state for wallet-contract. For instance, TON's [wallet-v5](https://github.com/ton-blockchain/wallet-contract-v5/blob/main/README.md#known-security-issues) follows such approach, where clients [derive](https://github.com/ton-org/ton/blob/5deac43432fa5dfcd441f2f0100dc3f89f55bead/src/wallets/v5r1/WalletV5R1WalletId.ts#L11-L15)
corresponding `wallet_id` using "virtual" `chain_id`.

However, this would make users to have their wallet-contracts deployed at
different [deterministic AccountIds](./nep-0616.md#deterministic-accountids)
on different Near chains even if they use the same public key. As a result, this
worsens the UX for end users.

Having `chain_id` encoded in the signed payload enables EVM-like UX, where all
wallet-contracts are deployed on the same AccountIds, regardless of a chain_id
being used.


## Future possibilities

Currently, [transactions](https://github.com/near/nearcore/blob/66a3dc3c2e79adb3bbe5bfd39e41ef7dcd723a95/core/primitives/src/transaction.rs#L82-L98)
on Near do not include `chain_id`. So, replay protection is achieved solely by
relying on the unlikelihood of recent [block_hash](https://github.com/near/nearcore/blob/66a3dc3c2e79adb3bbe5bfd39e41ef7dcd723a95/core/primitives/src/transaction.rs#L94-L95)
collisions. It might make sense to add `chain_id` field in the next version of
[Transaction](https://github.com/near/nearcore/blob/66a3dc3c2e79adb3bbe5bfd39e41ef7dcd723a95/core/primitives/src/transaction.rs#L109-L112)
object.

## Consequences

### Positive

* Wallet-contracts (and other smart-contracts) can have native protection
against replaying messages between different Near chains without UX tradeoffs.

### Neutral

\-

### Negative

\-

### Backwards Compatibility

This proposal is **backwards-compatible**: it only adds a new host function,
so that new contracts can opt-in using it, while existing ones can be
upgraded to start using it if necessary.


## Changelog

### 1.0.0 - Initial Version

#### Benefits

* Native protection against replaying messages between different Near chains.
* Better UX for end users: wallet-contracts are deployed at the same AccountIds
when using same public key.

#### Concerns

| # | Concern | Resolution | Status |
| -: | :------------------------------------------------------------- | :--- | ---: |
| 1 | Does consensus ensure all validators have the same `chain_id`? | Yes ([reasoning](https://github.com/near/NEPs/pull/638#issuecomment-3842705589)) | New |

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).