Skip to content

Commit 3e75013

Browse files
authored
Merge dcdc6ee into d923fca
2 parents d923fca + dcdc6ee commit 3e75013

File tree

1 file changed

+355
-0
lines changed

1 file changed

+355
-0
lines changed

EIPS/eip-7809.md

Lines changed: 355 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,355 @@
1+
---
2+
eip: 7809
3+
title: Native Tokens
4+
description: Fungible tokens with native-like properties in the EVM
5+
author: Paul Razvan Berg (@PaulRBerg), Iaroslav Mazur (@IaroslavMazur)
6+
discussions-to: https://ethereum-magicians.org/t/eip-7809-native-tokens/21615
7+
status: Draft
8+
type: Standards Track
9+
category: Core
10+
created: 2024-11-07
11+
requires: 2718, 2930
12+
---
13+
14+
## Abstract
15+
16+
This EIP introduces Native Tokens (NTs) as a backward-compatible extension of the EVM, enabling
17+
fungible tokens to function with native-like properties. Unlike ERC-20 tokens, NTs are
18+
integrated into the global VM state, allowing for direct transfers through newly defined opcodes and eliminating the
19+
traditional two-step "approve" and "transfer" pattern. Ether (ETH) is designated as one of the NTs while retaining its
20+
unique role as the exclusive token for gas fee payments. The EIP introduces the new opcodes `MINT`, `BURN`, `BALANCEOF`,
21+
and `CALLVALUES` to manage NT supply and query account balances. Additional opcodes such as `NTCALL`, `NTCALLCODE`,
22+
`NTCREATE`, and `NTCREATE2` facilitate NT transfers and NT-infused contract creation. Existing opcodes and transactions
23+
are adapted to refer to the default NT, which is `ETH`. A new transaction type is introduced in which the `value` field
24+
is replaced with a collection of (`token_id`, `token_amount`) pairs, enabling multi-token transactions. By embedding
25+
tokens natively in the EVM, this proposal aims to improve the user experience of token management and facilitate
26+
advanced innovating use-cases, particularly on L2s.
27+
28+
## Motivation
29+
30+
Implementing Native Tokens in the EVM offers several compelling advantages over traditional ERC-20 smart
31+
contracts.
32+
33+
### Native Support for Financial Instruments
34+
35+
Storing token balances in the VM state unlocks the potential for sophisticated financial instruments to be implemented
36+
at the protocol level. This native integration facilitates features such as recurring payments and on-chain incentives
37+
without the need for complex smart contract interactions. For instance, platforms could natively provide yield to token
38+
holders or execute airdrops natively, similar to how rollups like Blast offer yield for ETH holders. Extending this
39+
capability to any token enhances utility and encourages users to engage more deeply with the network.
40+
41+
### Elimination of Two-Step "Approve" and "Transfer"
42+
43+
By embedding token balances into the VM state, the cumbersome process of approving tokens before transferring them is
44+
eliminated. Token transfers can be seamlessly included into smart contract calls, simplifying transaction flows and
45+
reducing the number of steps users must take. This streamlined process not only enhances the user experience but also
46+
reduces gas costs associated with multiple contract calls, making interactions more efficient and cost-effective.
47+
48+
### Encouraging Experimentation on Layer 2 Solutions
49+
50+
The proposed model aims to encourage innovation on Ethereum L2s by providing a flexible framework for token management.
51+
EVM rollups can experiment with this design to develop new paradigms in decentralized finance (DeFi), gaming, and
52+
beyond. By enabling tokens to have native properties and interactions, developers are empowered to explore features that
53+
could lead to more robust and versatile applications. This experimentation is vital for the evolution of the Ethereum
54+
ecosystem, as it fosters advancements that can benefit the broader community.
55+
56+
## Specification
57+
58+
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT
59+
RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
60+
61+
### State Changes
62+
63+
A global `token_id` -> `token_supply` mapping is introduced to keep track of the existing NTs and their circulating
64+
supply. This mapping is also used to validate the supported NTs. An NT exists if and only if its ID can be found in the
65+
mapping. The supply of an NT increases as a result of executing the `MINT` opcode, and decreases as a result of
66+
executing the `BURN` opcode. The `token_id` of an NT is the Ethereum address of its associated smart contract.
67+
68+
`ETH` becomes the 'Base Token', with its ID and supply initialized to zero. `ETH` is the only NT whose supply is not
69+
tracked explicitly, i.e., its supply is determined just like it currently is.
70+
71+
For increased security and consistency, the token contracts representing the NTs SHOULD NOT use an upgradeability
72+
pattern.
73+
74+
### Stack
75+
76+
Since the EVM stack can support only up to 1024 elements, there is a natural limit to the number of tokens that can be
77+
transferred during the execution of a single opcode. Given that a token pair takes 2 stack slots, while the number of
78+
transferred tokens occupies another one, the maximum number of tokens that can be transferred can be calculated as
79+
follows:
80+
81+
$$
82+
(1024 - 1 - N) / 2
83+
$$
84+
85+
Where $N$ is the number of non-NT-related arguments.
86+
87+
For example, a single `NTCALL` opcode can transfer up to (1024 - 1 - 6) / 2 = 508 tokens.
88+
89+
### New Opcodes
90+
91+
#### `MINT` - `0xb0`
92+
93+
- **Gas**: Constant
94+
- **Stack inputs**:
95+
- `recipient`: the address to which the minted tokens are credited
96+
- `token_amount`
97+
- **Stack outputs**:
98+
- `success`: a Boolean indicating success
99+
100+
#### `BURN` - `0xb1`
101+
102+
- **Gas**: Constant
103+
- **Stack inputs**:
104+
- `burner`: the address from which the tokens are burned
105+
- `token_amount`
106+
- **Stack outputs**:
107+
- `success`: a Boolean indicating success
108+
109+
Note: the burner MUST have an NT balance that is at least equal to `token_amount`.
110+
111+
#### `BALANCEOF` - `0xb2`
112+
113+
- **Gas**: Constant
114+
- **Stack inputs**:
115+
- `token_id`: the ID of the NT to query the balance of
116+
- `address`: the address to query the balance of
117+
- **Stack outputs**:
118+
- `balance`: the NT balance of the given address
119+
120+
#### `CALLVALUES` - `0xb3`
121+
122+
- **Gas**: Dynamic, proportional to the number of NTs transferred by the executing call
123+
- **Stack inputs**: None
124+
- **Stack outputs**:
125+
- `transferred_tokens_length`: the number of transferred tokens
126+
- The list of `transferred_tokens_length` (`token_id`, `token_amount`) pairs
127+
128+
#### `NTCALL` - `0xb4`
129+
130+
- **Gas**: Dynamic, proportional to the number of transferred NTs
131+
- **Stack inputs**:
132+
133+
- `gas`: amount of gas to send to the sub context to execute. The gas that is not used by the sub context is returned
134+
to this one
135+
- `address`: the account which context to execute
136+
- `transferred_tokens_length`: the number of transferred tokens
137+
- The list of `transferred_tokens_length` (`token_id`, `token_amount`) pairs
138+
- `argsOffset`: byte offset in the memory in bytes, the calldata of the sub context
139+
- `argsSize`: byte size to copy (size of the calldata)
140+
- `retOffset`: byte offset in the memory in bytes, where to store the return data of the sub context
141+
- `retSize`: byte size to copy (size of the return data)
142+
143+
- **Stack outputs**:
144+
- `success`: return 0 if the sub context reverted, 1 otherwise
145+
146+
#### `NTCALLCODE` - `0xb5`
147+
148+
- **Gas**: Dynamic, proportional to the number of transferred NTs
149+
- **Stack inputs**:
150+
151+
- `gas`: amount of gas to send to the sub context to execute. The gas that is not used by the sub context is returned
152+
to this one
153+
- `address`: the account which code to execute
154+
- `transferred_tokens_length`: the number of transferred tokens
155+
- The list of `transferred_tokens_length` (`token_id`, `token_amount`) pairs
156+
- `argsOffset`: byte offset in the memory in bytes, the calldata of the sub context
157+
- `argsSize`: byte size to copy (size of the calldata)
158+
- `retOffset`: byte offset in the memory in bytes, where to store the return data of the sub context
159+
- `retSize`: byte size to copy (size of the return data)
160+
161+
- **Stack outputs**:
162+
- `success`: return 0 if the sub context reverted, 1 otherwise
163+
164+
#### `NTCREATE` - `0xb6`
165+
166+
- **Gas**: Dynamic, proportional to the number of transferred NTs
167+
- **Stack inputs**:
168+
169+
- `transferred_tokens_length`: the number of transferred tokens
170+
- The list of `transferred_tokens_length` (`token_id`, `token_amount`) pairs
171+
- `offset`: byte offset in the memory in bytes, the initialization code for the new account
172+
- `size`: byte size to copy (size of the initialization code)
173+
174+
- **Stack outputs**:
175+
- `address`: the address of the deployed contract, 0 if the deployment failed.
176+
177+
#### `NTCREATE2` - `0xb7`
178+
179+
- **Gas**: Dynamic, proportional to the number of transferred NTs
180+
- **Stack inputs**:
181+
182+
- `transferred_tokens_length`: the number of transferred tokens
183+
- The list of `transferred_tokens_length` (`token_id`, `token_amount`) pairs
184+
- `offset`: byte offset in the memory in bytes, the initialization code of the new account
185+
- `size`: byte size to copy (size of the initialization code)
186+
- `salt`: 32-byte value used to create the new account at a deterministic address
187+
188+
- **Stack outputs**:
189+
- `address`: the address of the deployed contract, 0 if the deployment failed
190+
191+
### Existing Opcodes
192+
193+
#### Balance Query
194+
195+
The following opcodes are adapted to query the balance of the default NT, which is `ETH`:
196+
197+
- `BALANCE`
198+
- `SELFBALANCE`
199+
- `CALLVALUE`
200+
201+
#### Contract Creation
202+
203+
The `value` field in the following opcodes will refer to the default NT, which is `ETH`:
204+
205+
- `CREATE`
206+
- `CREATE2`
207+
208+
#### Calling Contracts
209+
210+
The `value` field in the following opcodes will refer to the default NT, which is `ETH`:
211+
212+
- `CALL`
213+
- `CALLCODE`
214+
215+
### Transaction structure
216+
217+
### Parameters
218+
219+
| Parameter | Value |
220+
| ----------------------- | ---------------------------------- |
221+
| `MNT_TX_TYPE` | > 0x03 ([EIP-4844](./eip-4844.md)) |
222+
| `PER_NATIVE_TOKEN_COST` | `2500` |
223+
224+
#### New Transaction
225+
226+
A new [EIP-2718](./eip-2718.md) transaction is introduced with `TransactionType` = `MNT_TX_TYPE`.
227+
228+
The [EIP-2718](./eip-2718.md) `TransactionPayload` for this transaction is:
229+
230+
```
231+
rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, native_tokens_list, data, access_list, signature_y_parity, signature_r, signature_s])
232+
```
233+
234+
The `signatureYParity, signatureR, signatureS` elements of this transaction represent a secp256k1 signature over
235+
`keccak256(0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, native_tokens_list, data, accessList]))`.
236+
237+
The `native_tokens_list` element consists of the `transferred_tokens_length` variable, which specifies the number of
238+
tokens being transferred, followed by the (`token_id`, `token_amount`) pairs.
239+
240+
For the transaction to be valid, `native_tokens_list` must be of type `[{2 bytes}, [{32 bytes},{32 bytes},...]]`.
241+
242+
The [EIP-2718](./eip-2718.md) `ReceiptPayload` for this transaction is
243+
`rlp([status, cumulativeGasUsed, logsBloom, logs])`.
244+
245+
#### Gas Costs
246+
247+
The intrinsic cost of the new transaction follows the model defined in [EIP-2930](./eip-2930.md), specifically
248+
`21000 + 16 * non-zero calldata bytes + 4 * zero calldata bytes + 1900 * access list storage key count + 2400 * access list address count`.
249+
250+
In addition, a cost of `PER_NATIVE_TOKEN_COST` \* `transferred_tokens_length` is charged for each token in
251+
`native_tokens_list`.
252+
253+
#### EVM Transactions
254+
255+
All existing EVM transactions remain valid.
256+
257+
- A zero `value` is equivalent to an empty `transferred_tokens` list.
258+
- A non-zero `value` is equivalent to a list containing a single pair with `ETH`'s `token_id` (which is zero) and the
259+
`value` as `token_amount`.
260+
261+
## Rationale
262+
263+
An alternative to the proposed opcode-based approach was to use precompiles, which would have worked as follows:
264+
265+
- No new opcodes.
266+
- Existing EVM opcodes would remain unchanged.
267+
- As a result, no modifications to smart contract languages would be required.
268+
269+
However, the precompile-based approach also has disadvantages:
270+
271+
- It would require major architectural changes to the EVM implementation, as precompiles are not designed to be
272+
stateful.
273+
- Users would be required to handle low-level data manipulations to encode inputs to precompile functions and decode
274+
their outputs. This would lead to a subpar user experience.
275+
276+
Considering this, the opcode-based approach was chosen for its simplicity and efficiency in handling NTs at the EVM
277+
level.
278+
279+
280+
## Backwards Compatibility
281+
282+
This EIP does not introduce any breaking changes to the existing Ethereum protocol. However, it adds substantial new
283+
functionality that requires consideration across various layers of the ecosystem.
284+
285+
- Front-end Ethereum libraries, such as web3.js and wagmi, will need to adapt to the new transaction structures introduced
286+
by NTs. These libraries must update their interfaces and transaction handling mechanisms to accommodate the inclusion
287+
of token transfers within smart contract calls and the absence of traditional "approve" and "transfer" functions.
288+
- Smart contract languages like Solidity will need to incorporate support for the newly introduced opcodes associated with
289+
NTs. This includes adapting compilers and development environments to recognize and compile contracts that interact
290+
with tokens stored in the VM state.
291+
- Additionally, Ethereum wallets, block explorers, and development tools will require updates to fully support NTs.
292+
Wallets must be capable of managing native token balances, signing new types of transactions, and displaying
293+
token information accurately. Explorers need to parse and present the new transaction formats and token states, while
294+
development tools should facilitate debugging and deployment in this enhanced environment.
295+
296+
To ensure a smooth transition, the authors recommend a gradual deployment process. This phased approach allows
297+
developers, users, and infrastructure providers to adapt incrementally. By introducing NTs in stages, the ecosystem can
298+
adjust to the new functionalities, verify compatibility, and address any issues that arise, ensuring that every
299+
component behaves correctly throughout the integration period.
300+
301+
## Reference Implementation
302+
303+
The authors have begun implementing this EIP in Sablier's [SabVM repository](https://github.com/sablier-labs/sabvm), a
304+
fork of [REVM](https://github.com/bluealloy/revm) that supports NTs. Unlike the proposed EIP, SabVM uses precompiles
305+
instead of opcodes because that was easier to implement at the time.
306+
307+
A particularly relevant resource in SabVM is this
308+
[draft Solidity spec](https://github.com/sablier-labs/sabvm/discussions/87), which details support for NTs in Solidity.
309+
310+
Additionally, the [SRFs repository](https://github.com/sablier-labs/SRFs) (Sablier Requests for Comments) hosts the
311+
SRF-20 standard: an application-level standard designed to replicate the ERC-20 standard specifically for NTs.
312+
313+
| Name | Link | Description |
314+
| ------ | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------- |
315+
| SabVM | [github.com/sablier-labs/sabvm](https://github.com/sablier-labs/sabvm) | Fork of REVM that implements NTs with precompiles |
316+
| SRFs | [github.com/sablier-labs/SRFs](https://github.com/sablier-labs/SRFs) | Sablier Requests for Comments |
317+
| stdlib | [github.com/sablier-labs/stdlib](https://github.com/sablier-labs/stdlib) | Sablier Standard Library, providing precompiles, standards, and testing utilities |
318+
319+
320+
### Prior Art
321+
322+
This EIP has been inspired by FuelVM's
323+
[Native Assets](https://docs.fuel.network/docs/sway/blockchain-development/native_assets/) design, as well as its
324+
[SRC-20: Native Asset](https://docs.fuel.network/docs/sway-standards/src-20-native-asset/) standard.
325+
326+
The key distinction from Fuel's Native Assets is that, in this EIP, each contract is limited to a single native token
327+
(NT). A contract can mint only one NT, and the contract's address itself serves as the NT's ID. Basically, this EIP is
328+
meant to be an alternative to ERC-20.
329+
330+
## Security Considerations
331+
332+
This EIP introduces a few security risks related to malicious tokens and system integrity. Below are the key
333+
considerations and how they are mitigated.
334+
335+
1. **Malicious or Misbehaving Native Tokens**: a token that becomes a Native Token (NT) may later behave maliciously,
336+
causing disruptions in the network.
337+
338+
Mitigation: Users are encouraged to prefer using immutable, non-upgradeable NTs.
339+
340+
2. **Cross-Contract NT Transfers**: inter-contract NTs transfers could lead to lost tokens if contracts are not properly
341+
equipped to handle multiple tokens.
342+
343+
Mitigation: Contracts must validate token transfers correctly, with guidance for developers on standard patterns to
344+
ensure safe cross-contract interactions. Existing EVM contracts should be audited and updated to handle NTs.
345+
346+
3. **Gas Bombs**: Users may become stuck if they hold an excessive number of NTs, causing the gas required for
347+
processing their transactions to exceed the block gas limit.
348+
349+
Mitigation: All introduced opcodes operate with constant-time complexity. The stack limit of 1024 elements effectively
350+
prevents the creation of gas bombs when calling contracts. Although an opcode for querying all NT balances of an account
351+
was initially considered, it was ultimately omitted to eliminate the risk of gas bomb exploits.
352+
353+
## Copyright
354+
355+
Copyright and related rights waived via [CC0](../LICENSE.md).

0 commit comments

Comments
 (0)