// SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity 0.8.30; /// @title The delta discipline, as a Solidity contract sees it. /// /// The five opcodes have no Solidity spelling. `core/stdlib/src/ZycordDelta.yul` /// places their bytes through Yul's `verbatim`, which solc rejects inside /// inline assembly and accepts only in code compiled as pure Yul, so the /// wrapper is a separate object and this interface is how a Solidity contract /// names its entry points. /// /// **Every call to it is a `delegatecall`, and that is not a style choice.** /// The opcodes name `Slot{ctx, word(k)}`, where `ctx` is the *executing /// frame's* storage-context address. A plain `call` enters the library's own /// frame, so the guard and the delta would land on the library's storage and /// not on the caller's -- a live wrong answer on a cell the author never named, /// with no halt anywhere. `delegatecall` keeps the caller's context, which is /// what makes the wrapper usable at all. /// /// `Delta` below is the helper every contract in this directory calls; the /// interface exists so that the selectors are computed by the compiler rather /// than typed. interface IZycordDelta { /// The cell is at least `x`. Checked by the fold, never here. function sassertGE(uint256 slot, uint256 x) external; /// The cell is at most `x`. function sassertLE(uint256 slot, uint256 x) external; /// The cell equals `x`, without the value entering the computation. function sassertEQ(uint256 slot, uint256 x) external; /// Appends `+v` to the slot's running delta. function sdeltaAdd(uint256 slot, uint256 v) external; /// Appends `-v`. function sdeltaSub(uint256 slot, uint256 v) external; } /// @notice The call shape, written once so that no contract in this directory /// gets the `delegatecall` wrong. /// /// **A guard is not a `require`, and the difference is a different bill.** /// `require(balance >= x)` loads, compares and reverts: the frame halts, the /// certificate applies, and the caller pays for a failed call. `sassertGE` /// records a claim the *fold* checks: nothing halts, the whole certificate is /// discarded, the skip fee is burned against the underwriter, and no write of /// that certificate lands. They are different failure modes with different /// bills and they are not interchangeable. /// /// **A guard returns nothing.** No branch anywhere in an execution can depend /// on whether one holds, which is what keeps a guarded value out of the /// computation. A contract that needs to *decide* on a value has to read it /// exactly, and pays the staleness that comes with that. library Delta { /// The library's deployed address: the delta wrapper at library(5). /// /// **This was the zero address, and what that compiled to is the reason /// the value is spelled out below rather than left to a build step.** A /// call into an address holding no code SUCCEEDS: it pushes one, returns /// empty, and lets the caller carry on. So `_call`'s `require(ok)` held, /// every entry point of every contract in this directory completed /// normally, and every guard and every delta the source asked for was /// simply absent from the certificate. A `withdraw` paid out native value /// with no debit on the caller's token balance; a `transfer` credited /// nobody and debited nobody. Nothing halted and nothing refused. That was /// measured rather than argued, and the measurement is kept in /// `sim/contract/stdlib_wrapper_test.go` rather than retired with the /// defect, because it is what a reader needs to see the value below is /// load-bearing. /// /// **Where the value comes from, so that nobody re-derives it and nobody /// treats it as an implementer's choice.** Section 11.2 of /// docs/spec/cevm.md gives the wrapper the next free index in the /// pre-deployed table and gives the derivation for the address at an /// index. The index is 5, the derivation is /// `library(i) = 0x05 || blake3(TagAddr || 0x05 || 0x05 || u64le(i))[:31]`, /// and the release's own table computes it: nothing here is written down /// twice. `core/stdlib/table_test.go` carries the thirty-two-byte ledger /// form as a golden value an independent implementation must reproduce /// from the formula alone. /// /// **Why the constant is twenty bytes and the ledger address is /// thirty-two.** Section 1.11 keeps the machine's address width at /// Ethereum's, so what a contract sees is the address's NAME -- its low /// twenty bytes -- and the adapter owns the mapping back. Solidity's /// `address` is 160 bits and `solc` masks on every cast, so a widened /// machine would truncate inside contract code where no rule of this /// protocol could see it. The constant below is therefore the name of /// library(5) and not its ledger address, and that is what makes a stock /// toolchain able to hold it at all. /// /// **An address is not by itself enough, because a second value fails the /// same way.** The wrapper must be pre-deployed as its RUNTIME image. Its /// creation image is the runtime image behind a prologue that copies the /// runtime out and returns it, so a caller entering that prologue gets a /// successful call, an empty write set, and no way to tell it apart from /// the zero address this constant used to hold. Rule L-5 settles which /// one is shipped -- a pre-deployed contract runs no constructor -- and /// the shipped image under `spec/stdlib` is the runtime one. address internal constant WRAPPER = address(0xD26FF78938902BfE993E0683b7572b9A9e1d035f); function assertGE(uint256 slot, uint256 x) internal { _call(abi.encodeCall(IZycordDelta.sassertGE, (slot, x))); } function assertLE(uint256 slot, uint256 x) internal { _call(abi.encodeCall(IZycordDelta.sassertLE, (slot, x))); } function assertEQ(uint256 slot, uint256 x) internal { _call(abi.encodeCall(IZycordDelta.sassertEQ, (slot, x))); } function add(uint256 slot, uint256 v) internal { _call(abi.encodeCall(IZycordDelta.sdeltaAdd, (slot, v))); } /// A debit takes its guard first. The machine cannot synthesise a debit's /// bound -- that is the point of the delta discipline -- so a net-negative /// slot carrying no guard is a derivation error and the certificate never /// reaches a fold at all. `subGuarded` is the pair, and no contract here /// calls `sub` without one. function subGuarded(uint256 slot, uint256 v) internal { assertGE(slot, v); _call(abi.encodeCall(IZycordDelta.sdeltaSub, (slot, v))); } function _call(bytes memory payload) private { (bool ok,) = WRAPPER.delegatecall(payload); require(ok, "delta"); } }