---
title: "EIP-1153 — Transient Storage"
description: "EIP-1153 — Transient Storage mapped to Neo N3."
---
# EIP-1153 — Transient Storage
[Back to Protocol-Level EIPs](/standards-mirror/protocol-eips)
<StandardsMirror>
<StandardEntry
id="eip-1153"
title="EIP-1153 — Transient Storage"
eip="1153"
status="Final"
neoMapping="Native (Storage with transaction-scoped lifetime)"
category="Storage"
parityLabel="Native"
parityClass="sm-pill-native"
>
<template #spec>
## EIP-1153: Transient Storage Opcodes (TSTORE / TLOAD)
EIP-1153 (Cancun, 2024) introduced two new opcodes — `TSTORE` and `TLOAD` — that
read/write storage that lives only for the duration of a single transaction.
Persistent storage (`SSTORE`) costs 20,000 gas; transient storage costs 100 gas.
### Use Cases
- **Re-entrancy guards**: a `bool` flag set during entry, checked on re-entry,
cleared at the end of the call. Without TSTORE this requires writing a regular
storage slot at 20K gas.
- **Inter-call communication**: a contract that needs to pass non-trivial state
through a callback no longer pays for permanent storage.
- **Cached computation**: expensive views computed once per transaction.
### Neo Equivalent
Neo's storage API supports the same pattern via per-transaction storage scopes.
A contract can use a small, manually-managed transient store: write a value, use
it, delete it before the transaction ends. The cost is similar to permanent
storage at write/delete, but the value is gone after the tx — no permanent state
bloat.
For the most common use case (re-entrancy guards), the standard Neo idiom is
to use a contract-local boolean stored in transient memory implemented through
a deliberate `Put` then `Delete` pattern, OR to use the `Runtime.GetTrigger() ==
TriggerType.Application` check combined with `Runtime.ExecutingScriptHash !=
Runtime.CallingScriptHash` to detect external entry. Neo VM's call semantics
prevent a re-entrancy hazard pattern much more strongly than EVM's, so guards
are needed less often.
::: tip Live on Neo TestNet
Both implementations are deployed on Neo N3 TestNet (network magic `894710606`).
| **Solidity** (`neo-solc`) | `0x7e4e48124ed93c56eb4715965bb5b91fd0eb1a66` | (reused — see [`0x7e4e48124ed93c56eb4715965bb5b91fd0eb1a66`](https://dora.coz.io/contract/neo3/testnet/0x7e4e48124ed93c56eb4715965bb5b91fd0eb1a66)) |
| **Neo C#** (`nccs`) | `0xe67e6815ad4d151bf87667af4e9aa9cbc3eaa7bf` | (reused — see [`0xe67e6815ad4d151bf87667af4e9aa9cbc3eaa7bf`](https://dora.coz.io/contract/neo3/testnet/0xe67e6815ad4d151bf87667af4e9aa9cbc3eaa7bf)) |
Checked-in snapshot: Solidity 2 / 3 assertions pass; Neo C# 2 / 3 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-1153/`](https://github.com/r3e-network/neo-devpack-solidity/tree/main/docs/standards-mirror/deployments/eip-1153).
:::
</template>
<template #solidity>
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
abstract contract ReentrancyGuardTransient {
bytes32 private constant LOCKED = keccak256("ReentrancyGuard.LOCKED");
modifier nonReentrant() {
require(_locked() == 0, "reentrant");
_setLocked(1);
_;
_setLocked(0);
}
function _locked() private view returns (uint256 v) {
bytes32 slot = LOCKED;
assembly { v := tload(slot) }
}
function _setLocked(uint256 v) private {
bytes32 slot = LOCKED;
assembly { tstore(slot, v) }
}
}
contract MyVault is ReentrancyGuardTransient {
function withdraw(uint256 amount) external nonReentrant {
// ... safe to do external calls here ...
}
}
```
</template>
<template #csharp>
```csharp
using System;
using System.ComponentModel;
using System.Numerics;
using Neo;
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Attributes;
using Neo.SmartContract.Framework.Native;
using Neo.SmartContract.Framework.Services;
namespace R3E.Examples;
/// <summary>
/// Neo re-entrancy guard. Two patterns:
///
/// (a) Storage-based: same as EIP-1153 conceptually — write a flag, check on
/// re-entry, delete at exit. Storage in Neo is only persistent if you don't
/// delete it; transient effect = put+delete in one tx.
///
/// (b) Caller-based: use Runtime.CallingScriptHash to detect direct vs reentrant
/// calls. Often sufficient because Neo's call model is more restrictive than
/// delegatecall-heavy EVM patterns.
/// </summary>
[DisplayName("ReentrancyGuarded")]
[ContractPermission("*", "*")]
public class ReentrancyGuarded : SmartContract
{
private static readonly byte[] LockKey = { 0x70 };
public static void Withdraw(BigInteger amount)
{
if (Storage.Get(Storage.CurrentContext, LockKey) != null)
throw new Exception("reentrant");
Storage.Put(Storage.CurrentContext, LockKey, 1);
try
{
// ... external calls / asset transfers here ...
}
finally
{
Storage.Delete(Storage.CurrentContext, LockKey);
}
}
}
```
### Cost Comparison
| Set guard | TSTORE — 100 gas | Put — ~3.5 GAS at default fee factor |
| Check guard | TLOAD — 100 gas | Get — ~1 GAS |
| Clear guard | TSTORE 0 — 100 gas | Delete — ~1 GAS |
Neo's storage costs are higher in absolute terms but the gas-to-value-protected
ratio is similar — a guarded function call costs only marginally more than an
unguarded one in either system.
</template>
</StandardEntry>
</StandardsMirror>