瞬态存储新引入的一种数据位置类型。它提供了一种在单个交易执行期间临时存储数据的机制,这些数据会在交易结束后自动清除。是对现有数据位置的补充。
瞬态存储(Transient Storage)是 Solidity 0.8.24 版本中新引入的一种数据位置类型。它提供了一种在单个交易执行期间临时存储数据的机制,这些数据会在交易结束后自动清除。该特性通过 EIP-1153 提案实现,是对现有数据位置(内存、存储、调用数据)的补充。
瞬态存储的需求主要源于以下几点:
高昂的 Gas 成本:在以太坊中,永久存储(Storage)的 SSTORE 操作成本极高,首次写入至少需要 20,000 gas。这使得处理临时数据变得不经济。
状态膨胀:永久存储的数据会不断累积,导致区块链状态的增长,增加了节点的存储负担。
清理成本:删除不再需要的存储数据需要额外支付 gas,这进一步增加了管理存储的复杂性和成本。
临时性需求:许多应用场景只需要临时存储数据。
瞬态存储填补了以太坊现有存储机制的空缺,为开发者提供了一个经济高效的临时数据存储方案,尤其适合那些数据只需在单个交易内有效且需要频繁读写的场景。
以下是关于瞬态存储(Transient Storage)、存储(Storage)、内存(Memory)和调用数据(Calldata)的比较:
| 特性 | 瞬态存储 | 存储 | 内存 | 调用数据 | 
|---|---|---|---|---|
| 存储位置 | 临时存储 | 永久存储 | 临时存储 | 只读存储 | 
| Gas 成本 | 较低(约 100 gas/操作) | 高昂(首次写入至少 20,000 gas) | 较低 | 较低 | 
| 作用域 | 跨函数调用可用 | 跨交易可用 | 仅限于单个函数调用 | 仅限于函数参数 | 
| 状态保持 | 可以保持状态 | 永久保持状态 | 不可保持状态 | 不可保持状态 | 
| 清理成本 | 无需清理 | 需要额外支付 gas | 无需清理 | 无需清理 | 
| 大小限制 | 无明显限制 | 受链上状态限制 | 有限的内存空间 | 有限的输入参数大小 | 
| 适用场景 | 临时数据和计算 | 永久性数据存储 | 临时数据处理 | 函数参数传递 | 
可以通过以下方式使用瞬态存储:
tstore 和 tload 汇编指令:contract TransientStorageExample {
    function example() public {
        assembly {
            // 存储值
            tstore(0x01, 100)
            // 其它操作
            // 读取值
            let value := tload(0x01)
        }
    }
}
transient 关键字(在 Solidity 0.8.28 及之后的版本中):pragma solidity ^0.8.28;
contract TransientStorageExample {
    uint transient tempValue;
    function example() public {
        tempValue = 100;
        // 其它操作
        return tempValue;
    }
}
pragma solidity ^0.8.28;
contract TReentrant {
    mapping(address => bool) claimed;
    bool transient locked;
    modifier nonReentrant {
        require(!locked, "Reentrancy attempt");
        locked = true;
        _;
        locked = false;
    }
    function claim() nonReentrant public {
        require(!claimed[msg.sender], "Already claimed");
        // 其它操作
        claimed[msg.sender] = true;
    }
}
pragma solidity ^0.8.28;
contract TMultiplier {
    uint public transient multiplier;
    function setMultiplier(uint mul) external {
        multiplier = mul;
    }
    function multiply(uint value) external view returns (uint) {
        return value * multiplier;
    }
}
setMultiplier(100);
multiply(1); // 返回 0,而不是 100
multiply(2); // 返回 0,而不是 200
如果该示例使用内存或存储来存储乘数,它将是完全可组合的。无论是将交易拆分为单独的交易还是以某种方式将它们组合在一起,都没有关系。总是会得到相同的结果:在 multiplier 设置为 100 后,后续调用将分别返回 100 和 200。这使得可以将来自多个交易的调用批量处理在一起以减少 gas 费用。
瞬态存储可能会破坏这样的用例,因为可组合性不再是理所当然的。在这个例子中,如果调用不是在同一交易中执行的,则 multiplier 将被重置,后续对函数 multiply 的调用将始终返回 0。
瞬态存储的数据在当前交易执行期间有效,交易结束后会自动清除,从而确保其临时性。这种存储方式的写入和读取成本显著低于传统存储,能够有效节省交易的 Gas 消耗,特别适合处理临时数据。通过在合适的场景中应用瞬态存储,开发者能够显著提升合约的 Gas 效率,从而为用户节省成本并提高交易的整体性能。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!