You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: EIPS/eip-4788.md
+4-7Lines changed: 4 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -117,7 +117,7 @@ def get():
117
117
118
118
root_idx = timestamp_idx +HISTORY_BUFFER_LENGTH
119
119
root = storage.get(root_idx)
120
-
120
+
121
121
evm.return(root)
122
122
123
123
defset():
@@ -240,14 +240,11 @@ If this EIP is active in a genesis block, the genesis header's `parent_beacon_bl
240
240
241
241
## Rationale
242
242
243
-
### Gas cost of precompile
244
-
245
-
The suggested gas cost reflects a cold `SLOAD` analogous to the operation(s) performed while executing the precompile's logic.
246
-
247
243
### Why not repurpose `BLOCKHASH`?
248
244
249
245
The `BLOCKHASH` opcode could be repurposed to provide the beacon root instead of some execution block hash.
250
-
To minimize code change, avoid breaking changes to smart contracts, and simplify deployment to mainnet, this EIP suggests leaving `BLOCKHASH` alone and adding a new precompile with the desired semantics.
246
+
To minimize code change, avoid breaking changes to smart contracts, and simplify deployment to mainnet, this EIP suggests leaving `BLOCKHASH` alone and adding new
247
+
functionality with the desired semantics.
251
248
252
249
### Beacon block root instead of state root
253
250
@@ -261,7 +258,7 @@ e.g. with a singleton state root contract that caches the proof per slot).
261
258
### Why two ring buffers?
262
259
263
260
The first ring buffer only tracks `HISTORY_BUFFER_LENGTH` worth of roots and so for all possible timestamp values would consume a constant amount of storage.
264
-
However, this design opens the precompile to an attack where a skipped slot that has the same value modulo the ring buffer length would return an old root value,
261
+
However, this design opens the contract to an attack where a skipped slot that has the same value modulo the ring buffer length would return an old root value,
265
262
rather than the most recent one.
266
263
267
264
To nullify this attack while retaining a fixed memory footprint, this EIP keeps track of the pair of data `(parent_beacon_block_root, timestamp)` for each index into the
See [Discard Payment](#discard-payment-1) in **Security Considerations** for an important security note.
180
+
Please refer to the [Discard Payment](#discard-payment-1) section in the **Security Considerations** for an important security note.
181
+
182
+
### Native Token Tranfer
183
+
184
+
The `UTR` SHOULD have a `receive()` function for user execution logic that requires transferring ETH in. The `msg.value` transferred into the router can be spent in multiple inputs across different actions. While the caller takes full responsibility for the movement of `ETH` in and out of the router, the `exec` function SHOULD refund any remaining `ETH` before the function ends.
185
+
186
+
Please refer to the [Reentrancy](#reentrancy) section in the **Security Considerations** for information on reentrancy risks and mitigation.
Copy file name to clipboardExpand all lines: EIPS/eip-7201.md
+14-15Lines changed: 14 additions & 15 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,36 +12,35 @@ created: 2023-06-20
12
12
13
13
## Abstract
14
14
15
-
We define a formula to derive a location in storage, that is suitable for structs of arbitrary size, from an identifier. The formula is chosen to be safe against collisions with the standard Solidity storage layout. We define a convention to document this location in Solidity source code.
15
+
We define a formula to derive a location in storage, that is suitable for structs of arbitrary size, from an identifier. The formula is chosen to be safe against collisions with the storage layouts used by Solidity and Vyper. We define a convention to document this location in Solidity source code.
16
16
17
17
## Motivation
18
18
19
-
The default Solidity storage layoutis linear: all state variablesare assigned sequential locations starting from storage slot 0. This is sufficient for most contracts. However, various design patterns used in smart contract development can benefit from a non-linear storage layout. One example is a modular design where using `DELEGATECALL` a contract executes code from multiple contracts, all of which share the same storage space, and which have to carefully coordinate on how to use it. Another example is upgradeable contracts, where it can be difficult to add state variables in an upgrade given that they may affect the assigned storage location for the preexisting variables.
19
+
Smart contract languages such as Solidity and Vyper rely on tree-shaped storage layout. This tree starts at slot 0 and is composed of sequential chunks for consecutive variables. Hashes are used to ensure the chunks containing values of mappings and dynamic arrays do not collide. This is sufficient for most contracts. However, it presents a challenge for various design patterns used in smart contract development. One example is a modular design where using `DELEGATECALL` a contract executes code from multiple contracts, all of which share the same storage space, and which have to carefully coordinate on how to use it. Another example is upgradeable contracts, where it can be difficult to add state variables in an upgrade given that they may affect the assigned storage location for the preexisting variables.
20
20
21
-
In a non-linear storage layout, instead of being assigned sequential locations starting from slot 0, state variables are spread out across the storage space, usually at pseudorandom locations obtained by hashing. Each value may be placed in an entirely different location, but more frequently values that are used together are put in a Solidity struct and co-located in storage.
21
+
Rather than using this default storage layout, these patterns can benefit from laying out state variables across the storage space, usually at pseudorandom locations obtained by hashing. Each value may be placed in an entirely different location, but more frequently values that are used together are put in a Solidity struct and co-located in storage. These pseudorandom locations can be the root of new storage trees that follow the same rules as the default one. Provided that this pseudorandom root is constructed so that it is not part of the default tree, this should result in the definition of independent spaces that do not collide with one another or with the default one.
22
22
23
-
These storage usage patterns are invisible to the Solidity compiler because they are not represented as Solidity state variables. Smart contract tools like static analyzers or blockchain explorers often need to know the storage location of contract data. Standardizing the location for non-linear storage layouts will allow these tools to correctly interpret contracts where these design patterns are used.
23
+
These storage usage patterns are invisible to the Solidity and Vyper compilers because they are not represented as Solidity state variables. Smart contract tools like static analyzers or blockchain explorers often need to know the storage location of contract data. Standardizing the location for storage layouts will allow these tools to correctly interpret contracts where these design patterns are used.
24
24
25
25
## Specification
26
26
27
-
A _namespace_ consists of a set of variables that are placed contiguous in the storage layout of a contract. It should be implemented as a struct.
27
+
A _namespace_ consists of a set of ordered variables, some of which may be dynamic arrays or mappings, with its values laid out following the same rules as the default storage layout but rooted in some location that is not necessarily slot 0. It should be implemented as a struct.
28
28
29
29
A _namespace id_ is a string that uniquely identifies a namespace in a contract. It should not contain any whitespace characters.
30
30
31
-
The storage location for a namespace is defined as `ns_loc(id: string) = keccak256(abi.encode(uint256(keccak256(id)) - 1))`.
31
+
The storage location for a namespace is defined as `ns_loc(id: string) = keccak256(keccak256(id) - 1) & ~0xff`.
32
32
33
-
A Solidity contract using namespaced storage can annotate a struct with the NatSpec tag `@custom:storage-location erc7201:<NAMESPACE_ID>` to identify it as a namespace with id `<NAMESPACE_ID>`. _(Note: The Solidity compiler includes this annotation in the AST since v0.8.20, so this is recommended as the minimum compiler version when using this pattern.)_
33
+
A Solidity contract using namespaced storage can annotate a struct with the NatSpec tag `@custom:storage-location erc7201:<NAMESPACE_ID>` to identify it as a namespace with id `<NAMESPACE_ID>`. _(Note: The Solidity compiler includes this annotation in the AST since v0.8.20, so this is recommended as the minimum compiler version when using this pattern.)_ Structs with this annotation found outside of contracts are not considered to be namespaces for any contract in the source code.
34
34
35
35
## Rationale
36
36
37
-
A requirement for the location is that it shouldn't overlap with any storage location that would be used by the standard Solidity layout. This is in case namespaced storage is used alongside standard linear storage, either deliberately or accidentally.
37
+
The tree-shaped storage layout used by Solidity and Vyper follows the following grammar (with root=0):
38
38
39
-
First, note that a namespace may be larger than a single storage slot, so a variable in a namespace will be placed in a slot `ns_loc(id) + k`. If we assume collision resistance of Keccak-256, the chosen `ns_loc` function has the desired property with very high probability, because the cases in which a Solidity variable receives a location of the form `keccak256(x)` are:
1. If the array is at the top level and is assigned to storage slot `n`, the location of the `k`th item in the array will be `keccak256(n) + k`, but `n` will be a number much smaller than `uint256(keccak256(id)) - 1`.
43
-
2. If the array is within another array or mapping, it will be in some location `keccak256(x) + j`, and the `k`th item will be at `keccak256(keccak256(x) + j) + k`. For this to equal `ns_loc(x) + k` we would need `j = -1`, but `j` will always be a non-negative number in standard Solidity layout.
44
-
2. Mappings: The value for key `q` in a mapping will be at location `keccak256(h(q) . x)` where `x` is the location of the mapping itself, and `h` is as defined in the Solidity documentation (section "Layout of State Variables in Storage"). Note that `h(q)` can be any number of bytes. If it is a non-zero number of bytes, it is distinct from any `ns_loc(id) + k`. If it is zero bytes, it can be that `ns_loc(id) = keccak256(x)` if `x = keccak256(id) - 1`, but we know that `x` is the location of the mapping and (as mentioned for arrays above) a variable will be at `keccak256(y) + j` for a non-negative number `j`.
41
+
A requirement for the root is that it shouldn't overlap with any storage location that would be part of the standard storage tree used by Solidity and Vyper (root = 0), nor should it be part of the storage tree derived from any other namespace (another root). This is so that multiple namespaces may be used alongside each other and alongside the standard storage layout, either deliberately or accidentally, without colliding. The term `keccak256(id) - 1` in the formula is chosen as a location that is unused by Solidity, but this is not used as the final location because namespaces can be larger than 1 slot and would extend into `keccak256(id) + n`, which is potentially used by Solidity. A second hash is added to prevent this and guarantee that namespaces are completely disjoint from standard storage, assuming keccak256 collision resistance and that arrays are not unreasonably large.
42
+
43
+
Additionally, namespace locations are aligned to 256 as a potential optimization, in anticipation of gas schedule changes after the Verkle state tree migration, which may cause groups of 256 storage slots to become warm all at once.
function oracleId() view external returns (bytes32 oracleId);
42
-
function fulfillOracleData(bytes oracleQuery, bytes signedOffchainData) payable external;
42
+
function fulfillOracleQuery(bytes oracleQuery, bytes signedOffchainData) payable external;
43
43
}
44
44
```
45
45
@@ -48,12 +48,12 @@ interface IERC7412 {
48
48
The contract implementing the `IOracleContract` interface MUST revert with the following error message if it requires payment to fulfill the oracle data query:
49
49
50
50
```solidity
51
-
error OracleFeeRequired(uint feeAmount)
51
+
error FeeRequired(uint amount)
52
52
```
53
53
54
-
`feeAmount` specifies the amount of native gas tokens required to execute the `fulfillOracleData` function, denominated in wei. This error MUST be resolved if the caller provides sufficient `msg.value` such that the fee amount can be collected by the oracle contract. The contract MAY NOT return gas tokens if they are provided in excess of the `feeAmount`. In practice, we would expect the fee amount to remain relatively stable, if not constant.
54
+
`amount` specifies the amount of native gas tokens required to execute the `fulfillOracleQuery` function, denominated in wei. This error MUST be resolved if the caller provides sufficient `msg.value` such that the fee amount can be collected by the oracle contract. The contract MAY NOT return gas tokens if they are provided in excess of the `amount`. In practice, we would expect the fee amount to remain relatively stable, if not constant.
55
55
56
-
It is the responsibility of the client to decide how to construct the multicall, where necessary the `fulfillOracleData` functions are being called before the intended function call in an atomic transaction. Wallets that support account abstraction (per [ERC-4337](./eip-4337.md)) should already have the ability to generate atomic multi-operations. For EOA support, protocols could implement [ERC-2771](./eip-2771.md). A standard multicall contract can only be used to construct multicalls including functions which do not reference `msg.sender` or `msg.data`.
56
+
It is the responsibility of the client to decide how to construct the multicall, where necessary the `fulfillOracleQuery` functions are being called before the intended function call in an atomic transaction. Wallets that support account abstraction (per [ERC-4337](./eip-4337.md)) should already have the ability to generate atomic multi-operations. For EOA support, protocols could implement [ERC-2771](./eip-2771.md). A standard multicall contract can only be used to construct multicalls including functions which do not reference `msg.sender` or `msg.data`.
57
57
58
58
To prevent data becoming too stale for a request between the simulation and a call's execution, ideally a contract could also emit the following event: `event OracleDataUsed(address oracleContract, bytes oracleQuery, uint expirationTime)` Here, `expirationTime` is the time after which the `OracleDataRequired` error would be thrown by the contract. (This would typically be a calculation involving a staleness tolerance and `block.timestamp`). Client applications that implement this standard would be able to recognize this event during simulation and estimate if an additional update will still be necessary, taking into account the speed of the chain. For example, the oracle query may request the latest quote available for a particular price feed and the expiration time may signal that the price cannot be older than three seconds prior to the current timestamp recognized by the blockchain. This has been omitted from the standard because there isn't a practical way to retrieve event data during transaction simulations on most JSON-RPC APIs at this time.
59
59
@@ -111,7 +111,7 @@ contract OracleContract is IERC7412 {
111
111
return bytes32(abi.encodePacked("MY_ORACLE_ID"));
112
112
}
113
113
114
-
function fulfillOracleData(bytes calldata oracleQuery, bytes calldata signedOffchainData) payable external {
114
+
function fulfillOracleQuery(bytes calldata oracleQuery, bytes calldata signedOffchainData) payable external {
This repository serves as a reference implementation for **EIP-5725 Transferrable Vesting NFT Standard**. A Non-Fungible Token (NFT) standard used to vest ERC-20 tokens over a vesting release curve.
3
4
4
5
## Contents
6
+
5
7
-[EIP-5725 Specification](./contracts/IERC5725.sol): Interface and definitions for the EIP-5725 specification.
6
-
-[ERC-5725 Implementation (abstract)](./contracts/ERC5725.sol): ERC-5725 contract which can be extended to implement the specification.
8
+
-[ERC-5725 Implementation (abstract)](./contracts/ERC5725.sol): ERC-5725 contract which can be extended to implement the specification.
7
9
-[VestingNFT Implementation](./contracts/reference/LinearVestingNFT.sol): Full ERC-5725 implementation using cliff vesting curve.
8
-
-[LinearVestingNFT Implementation](./contracts/reference/VestingNFT.sol): Full ERC-5725 implementation using linear vesting curve.
10
+
-[LinearVestingNFT Implementation](./contracts/reference/VestingNFT.sol): Full ERC-5725 implementation using linear vesting curve.
0 commit comments