---
title: "EIP-2930 — Access Lists"
description: "EIP-2930 — Access Lists mapped to Neo N3."
---
# EIP-2930 — Access Lists
[Back to Protocol-Level EIPs](/standards-mirror/protocol-eips)
<StandardsMirror>
<StandardEntry
id="eip-2930"
title="EIP-2930 — Access Lists"
eip="2930"
status="Final"
neoMapping="Witness scopes (CustomContracts)"
category="Transactions"
parityLabel="Native"
parityClass="sm-pill-native"
>
<template #spec>
## EIP-2930: Access Lists
EIP-2930 adds an optional `accessList` field to transactions: a pre-declared list
of `(address, storageKeys[])` tuples that the transaction will touch. Pre-declared
state access gets a gas discount; non-declared access costs more. The goal: enable
parallel transaction execution by letting nodes know in advance what state each tx
needs.
### Format
```
accessList = [
(address_1, [storageKey_1a, storageKey_1b, ...]),
(address_2, [storageKey_2a, ...]),
...
]
```
### Neo Equivalent: Witness Scopes
Neo's `WitnessScope.CustomContracts` already serves the same role for the
authorisation surface — declaring "this signature authorises calls only into these
contracts". Combined with `Signer.AllowedContracts` and `Signer.AllowedGroups`,
the protocol knows ahead of time which contracts a transaction can touch.
For the parallel-execution use case (knowing storage keys ahead of time), Neo's
storage prefix model makes static analysis tractable — the contract bytecode
declares its storage prefixes via `Storage.Get/Put/Delete` call sites.
::: tip Live on Neo TestNet
Both implementations are deployed on Neo N3 TestNet (network magic `894710606`).
| **Solidity** (`neo-solc`) | `0x02a174b4080c57d8ceb2c66b223799a79d09a2c3` | [`0x17844b4b…d94112`](https://dora.coz.io/transaction/neo3/testnet/0x17844b4b50ebc920cd449de142170ef8b55886d0acde40b20894baa02bd94112) |
| **Neo C#** (`nccs`) | `0x3570e80f52a9329b604d5d18fb94de3133b0a6ad` | [`0xb1bfb7d4…136169`](https://dora.coz.io/transaction/neo3/testnet/0xb1bfb7d4b46715b6e0848bbbf2b6a2a6b163531011d6998c6dc873025b136169) |
Checked-in snapshot: Solidity 2 / 2 assertions pass; Neo C# 2 / 2 assertions pass. This is a validation snapshot, not an all-green parity certification; see [TestNet Results](/standards-mirror/deployments/RESULTS) for failure details.
Source pairs: [`docs/standards-mirror/deployments/eip-2930/`](https://github.com/r3e-network/neo-devpack-solidity/tree/main/docs/standards-mirror/deployments/eip-2930).
:::
</template>
<template #solidity>
```solidity
// EIP-2930 type-1 transaction:
//
// to, value, data,
// accessList, // [(address, storageKeys[])]
// v, r, s
// ])
//
// Application impact: contracts can be written without thinking about access
// lists; they're a transaction-level concern. Protocols that benefit:
// MEV bots that pre-compute the storage they'll touch and pass it as access list
// for a small gas discount and inclusion priority.
```
</template>
<template #csharp>
```csharp
// Neo witness scopes — equivalent of EIP-2930's authorisation surface:
//
// enum WitnessScope : byte {
// None = 0x00, // signer pays fee but grants no contract authority
// CalledByEntry = 0x01, // grant only to top-level invoke and direct callees
// CustomContracts = 0x10, // grant to listed contracts only
// CustomGroups = 0x20, // grant to listed groups (cert-signed)
// WitnessRules = 0x40, // arbitrary pattern matching
// Global = 0x80 // unlimited grant (dangerous; usually rejected)
// }
//
// Building a transaction with access-list-equivalent scoping:
//
// const tx = new TransactionBuilder()
// .invoke(swapRouter, "swap", [tokenIn, tokenOut, amount])
// .signers([{
// account: user,
// scopes: WitnessScope.CustomContracts,
// allowedContracts: [tokenIn, tokenOut, swapRouter]
// }])
// .build();
//
// The protocol enforces: any Contract.Call from inside the transaction that
// would require user's witness must be to one of allowedContracts, or
// CheckWitness(user) returns false. This is a finer-grained authorisation
// surface than EIP-2930's access list, which is purely a gas-pricing hint.
using Neo;
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Attributes;
using Neo.SmartContract.Framework.Native;
using Neo.SmartContract.Framework.Services;
namespace R3E.Examples;
[DisplayName("WitnessIntrospection")]
public class WitnessIntrospection : SmartContract
{
public static UInt160[] CurrentSigners()
{
var signers = Runtime.CurrentSigners;
var output = new UInt160[signers.Length];
for (int i = 0; i < signers.Length; i++) output[i] = signers[i].Account;
return output;
}
}
```
### Why Neo's Version Is Stronger
EIP-2930 access lists are advisory — they're a gas optimisation, not an
authorisation boundary. Witness scopes are **enforced**: a contract that's not in
your allowed list cannot pretend you authorised it.
</template>
</StandardEntry>
</StandardsMirror>