Settlement contract and proof interface
This companion fixes the settlement contract, its storage layout, the amount representation, and the proof guards that SPECIFICATION.md section 16 depends on. The specification takes precedence. This document defines requirements, not an implemented or deployed contract; compiler artifacts, live deployment pins, and executable qualification remain separate gates.
Fixed design requirements:
- Makechain uses a dedicated beneficiary-cumulative settlement contract on Tempo Moderato for devnets and qualification testnets and a separate deployment on Tempo mainnet. The existing deployment and its receipt-proof flow remain intact.
- Every Makechain credit comes from a real deposit and authentic Tempo proof. Makechain has no genesis credit allocation, local faucet credit, discretionary grant, operator mint, or mocked positive verifier. Tokens obtained through a Tempo testnet faucet may be deposited normally; the deposit and proof remain real Tempo state.
- The only launch asset is pathUSD. One pathUSD base unit is one microdollar and one Makechain credit unit. A payer may fund a different beneficiary.
- Consensus verifies supplied evidence without Tempo RPC. RPC is an external acquisition mechanism, never a consensus oracle.
- Each network launches a non-proxy contract with immutable logic. Deposit pausing, administrator rotation and treasury withdrawals remain operational state transitions in that fixed logic and cannot change any beneficiary cumulative.
The selections below are reviewable through the architecture decision process. Deployment addresses, code hashes, compiler output, and trusted checkpoints must come from qualified artifacts, never fabricated placeholders. Evidence-bound values are a provisional devnet profile and require fixture qualification before a deployed chain uses them.
1. Contract boundary
1.1 Code identity
Immutable logic. Deploy a non-proxy contract independently on Moderato and mainnet. Keep operational authority in ordinary storage so deposits can be paused and the administrator can rotate without changing code. A separate two-step treasury-role rotation keeps operational key replacement independent of code replacement. Fixed logic lets the treasury withdraw deposited pathUSD, but pausing, role changes, and withdrawals cannot write a beneficiary cumulative. Only a real deposit of the fixed accepted asset may raise one. Do not include delegatecall, an implementation slot, an upgrade function, an asset setter, a grant/mint path, or a cumulative decrement/reset path.
This makes one authenticated contract account and one pinned runtime code hash sufficient for code identity. It also makes a code defect expensive: a fix requires a new deployment and the separately reviewed cumulative-migration procedure discussed in section 5, preserving every beneficiary cumulative exactly. The operational roles and their storage remain mutable under the fixed code; that mutability is not a code-update path. No administrative operation can write the monetary mapping.
1.2 ABI contract
The interface below is fixed for this design. Constructor arguments are (address initialAdmin, address initialTreasury) in that order. Errors raised by this contract are Unauthorized(),
InvalidAddress(), InvalidAmount(), InvalidState(), TransferMismatch(), and ReentrantCall().
A failed external token call reverts the whole operation; it never produces a successful deposit.
A change to this interface requires an explicit contract-design revision.
interface IMakechainSettlementV2 {
error Unauthorized();
error InvalidAddress();
error InvalidAmount();
error InvalidState();
error TransferMismatch();
error ReentrantCall();
event Deposited(
address indexed payer,
address indexed beneficiary,
uint256 microdollars,
uint256 beneficiaryLifetimeMicrodollars
);
event DepositsPaused(address indexed admin);
event DepositsUnpaused(address indexed admin);
event AdminTransferStarted(address indexed currentAdmin, address indexed pendingAdmin);
event AdminTransferred(address indexed previousAdmin, address indexed newAdmin);
event TreasuryTransferStarted(address indexed currentTreasury, address indexed pendingTreasury);
event TreasuryTransferred(address indexed previousTreasury, address indexed newTreasury);
event Withdrawn(address indexed treasury, uint256 microdollars);
function PATH_USD() external view returns (address);
function cumulativeMicrodollars(address beneficiary) external view returns (uint256);
function depositsPaused() external view returns (bool);
function admin() external view returns (address);
function pendingAdmin() external view returns (address);
function treasury() external view returns (address);
function pendingTreasury() external view returns (address);
function deposit(address beneficiary, uint256 microdollars)
external
returns (uint256 beneficiaryLifetimeMicrodollars);
function pauseDeposits() external;
function unpauseDeposits() external;
function beginAdminTransfer(address nextAdmin) external;
function acceptAdmin() external;
function cancelAdminTransfer() external;
function beginTreasuryTransfer(address nextTreasury) external;
function acceptTreasury() external;
function cancelTreasuryTransfer() external;
function withdraw(uint256 microdollars) external;
}
PATH_USD is a compile-time constant, not constructor-selected or mutable state. The constructor
rejects zero or self initial admin/treasury addresses. It starts with deposits paused, both pending
roles zero, all cumulatives absent/zero, and reentrancy status 1 (not entered). The administrator
unpauses after deployment review. Admin and treasury transfers are two-step and reject zero, self,
and the current role holder as destinations. The current admin alone pauses/unpauses deposits and starts an admin
transfer. The current treasury alone starts a treasury transfer. In each case the nominated address
alone accepts its role. The current treasury alone calls withdraw, and the destination is always
the current treasury address rather than a caller-supplied address. Pause affects only deposit;
proof reads, role recovery, and treasury withdrawal remain available.
The split roles are deliberate. The admin can stop new exposure and rotate operations but cannot
raise a beneficiary cumulative or withdraw funds. The treasury can withdraw pathUSD but cannot
change the admin, pause state, deposit semantics, or cumulatives; it can transfer its own role.
Starting a new transfer replaces that role's prior pending nomination, and acceptance clears it.
A current role holder may cancel its pending transfer through cancelAdminTransfer() or
cancelTreasuryTransfer() respectively; cancellation requires a nonzero pending nomination, clears
it, and emits the corresponding *TransferStarted(current, address(0)) event. Acceptance requires
the caller to equal a nonzero pending nominee. There is no role-renunciation operation. Repeated
pause or unpause calls that would not change state revert InvalidState(). A wrong caller reverts
Unauthorized() before any role mutation. Admin and treasury may be the same address in a
disposable devnet; production custody must explicitly review role separation.
Role changes and withdrawals are operational events, never Makechain credit evidence.
1.3 Deposit semantics
For deposit(beneficiary, requested) with payer = msg.sender:
- Require deposits unpaused,
beneficiary != address(0), andrequested > 0. - Read
old = cumulativeMicrodollars[beneficiary]; requirerequested <= type(uint256).max - old, revertingInvalidAmount()otherwise. Readbefore = pathUSD.balanceOf(this). - Execute
pathUSD.transferFrom(payer, this, requested)and require the token call to succeed. - Read
after = pathUSD.balanceOf(this). Rejectafter < beforeand requireafter - before == requested. The received balance delta, not calldata or an event, is the paid amount. - Write
old + requested, emitDeposited, and return that value. Bothdepositandwithdrawshare the same reentrancy guard, entered before any token call and restored only on success; a revert restores all contract and token effects. The entered status is2. Token calls use the pinned standard safe ERC-20 call handling; balance deltas are still checked explicitly.
The payer and beneficiary are separate facts. Anyone may pay for any nonzero beneficiary; the beneficiary does not sign and may equal the payer. The beneficiary mapping records pathUSD base units, with no price multiplication, unit conversion, expiry, refund, or transferable claim. A misdirected deposit is therefore permanent from Makechain's perspective.
The beneficiary is one raw 20-byte address whether it is a Tempo EOA or a Tempo configurable account. Settlement proof verification neither derives that address from owners nor inspects Tempo keychain/configuration state, account code, or the deposit transaction's authorization. Makechain's separate account-genesis derivation and authority rules decide who may later control the matching Makechain account; they are not part of this monetary proof.
The exact-received check rejects fee-on-transfer, short-transfer, and false-success behavior. A
Deposited log helps operations but is not settlement authority: no receipt or event proof may
substitute for the contract account and mapping-slot proof.
withdraw(amount) requires the treasury caller, positive amount no greater than the current token
balance, and the common reentrancy guard. It captures the current treasury as destination, transfers
pathUSD only there, requires token-call success and an exact contract-balance decrease of amount,
emits Withdrawn, and never reads or writes a cumulative. Withdrawn reports contract outflow;
recipient net receipt relies on the fixed pathUSD semantics and is not an independent credit claim.
Withdrawing the whole token balance is allowed; the mapping is a lifetime paid frontier, not an
escrow balance. A direct pathUSD transfer to the contract is a donation: it raises the withdrawable
balance, gives no beneficiary credit, and does not affect a later deposit because that deposit uses
its own before/after delta. The ABI has no generic arbitrary-token sweep.
Contract depositsPaused and Makechain imports_paused are independent controls. Contract pause
stops new Tempo deposits but does not erase cumulatives or invalidate an already captured state
proof. Makechain pause rejects both credit imports and checkpoint refreshes but cannot stop Tempo
deposits. Operators coordinate them during an incident; consensus never treats one flag as proof of
the other.
1.4 Storage layout
Use no inherited storage and no storage gap. The compiler storage-layout artifact must match:
| Slot | Solidity value | Proof relevance |
|---|---|---|
0 |
mapping(address => uint256) cumulativeMicrodollars |
The sole monetary storage proof |
1 |
address admin |
Operational only |
2 |
address pendingAdmin |
Operational only |
3 |
address treasury |
Operational only |
4, bytes 0..19 |
address pendingTreasury |
Operational only |
4, byte 20 |
bool depositsPaused |
Operational only; Solidity packs it after the address |
5 |
uint256 reentrancy status |
Operational only |
Byte offsets in the table use Solidity storage-layout offsets from the low-order end of the slot, not left-to-right positions in a displayed big-endian hex word.
The cumulative width is the full uint256 word. It wastes no additional mapping storage,
avoids a narrowing conversion, and makes both contract and verifier overflow rules explicit. For
beneficiary address b, the logical EVM storage slot is:
mapping_slot(b) = keccak256(left_pad_32(b) || uint256_be_32(0))
The Ethereum storage-trie lookup key is then keccak256(mapping_slot(b)). These are two distinct
Keccak operations. The value is the canonical RLP integer stored at that trie key, not the 32-byte
Solidity word or the JSON-RPC value field. Zero is represented by absence in Ethereum state; an
absent/zero value can never satisfy a positive strict-advance import.
Only slot 0 participates in the monetary proof, but the complete compiled layout must still match
the reviewed artifact. Slot 0, uint256, the compiler version/settings, linked-library absence,
constructor scheme, exact runtime bytes, and runtime code hash must be frozen together. A getter
returning the expected value does not prove the slot layout.
Makechain uses unsigned 256-bit Amount values for credited, spent, locked, fees, deposits,
and signed charge ceilings. Canonical protocol encoding is exactly 32 big-endian bytes; EVM storage
proofs retain their canonical RLP integer representation. All monetary arithmetic is checked,
including products and aggregated charges, and conservation checks use checked sums. No narrowing
conversion or truncation is permitted. This keeps every contract cumulative representable; a
narrower ledger would otherwise strand a beneficiary after its external cumulative exceeded the
local range. Counters, times, and collection lengths retain their separate schema widths.
2. Settlement source profile
Each Makechain chain binds one complete canonical static profile and keeps the already specified mutable anchor in consensus state. A network name or enum is insufficient. The split is:
SettlementSourceProfile { // static, changed only by a profile release
profile_id,
tempo_chain_id,
certificate_codec_id,
header_codec_id,
settlement_contract,
settlement_runtime_code_hash,
contract_layout_id,
cumulative_mapping_base_slot,
cumulative_value_bits,
accepted_asset { address, currency, decimals },
trusting_period_ms,
max_source_clock_skew_ms,
evidence_limits,
}
SettlementAnchor { // existing mutable consensus-state role
identity { group_key, from_epoch, installed_at_makechain_height },
checkpoint { tempo_height, tempo_timestamp_ms, accepted_at_makechain_timestamp_ms },
imports_paused,
}
The active identity includes a fixed-size from_epoch: u64, the first Tempo epoch whose
certificates are admissible under this key. Every credit or refresh certificate must have epoch >= from_epoch. Bootstrap and anchor replacement install the key and its reviewed source epoch floor
together; refresh never changes either. Genesis bootstrap accepted-at time equals the explicit
Makechain genesis timestamp. Release installer input excludes Makechain installation height and
accepted-at time: execution derives them from the activation block. Its profile ID comes from the
activated effective profile. A simultaneous import-pause delta must match the installer pause value
or the descriptor is invalid. Rule resolution applies these effects before checking any command in
the activation block. This matches Tempo's source identity contract and the reference codebase
verifier's epoch gate. The floor is not the epoch of Makechain installation, is not inferred from a
submitted proof, and does not follow Tempo autonomously.
All settlement times use Unix milliseconds: trusting_period_ms, max_source_clock_skew_ms,
checkpoint timestamps, and Makechain consensus time are unsigned 64-bit millisecond values. Duration
values are explicit profile inputs. All additions and conversions fail on overflow.
tempo_chain_id selects and domain-separates the configured external source; it is not a fact read
from a finalization certificate or Tempo header. Tempo's consensus signing namespace is the literal
TEMPO and is not network-bound. The certificate authenticates its proposal payload under the
pinned group key. Therefore each deployment/anchor-replacement review must establish that the installed group key
is unique to the selected Tempo network throughout the epochs accepted by that anchor. A network
label or from_epoch alone cannot prevent replay if two networks reuse the same group key with
overlapping eligible epochs. A profile that cannot establish that separation is not deployable under
this certificate format; it needs an authenticated network discriminator and a reviewed new profile.
Within this trust assumption, the identity/checkpoint and contract/code/slot pins bind the accepted
source. Add a cross-network authentic-certificate negative vector to every profile qualification. Makechain's own chain_id remains in the Makechain
command envelope; it is also not supplied by the Tempo proof.
The inspected Tempo revision provides these candidate network facts, but it does not provide the settlement deployment:
| Profile | Verified repository fact | Deployment fields still absent |
|---|---|---|
| Moderato | chain ID 42431; pathUSD 0x20C0000000000000000000000000000000000000; current compiled identity begins at epoch 51 |
Settlement contract address, runtime bytes/hash, deployment block/checkpoint, authentic account/storage proof |
Mainnet (PRESTO in the source tree) |
chain ID 4217; the same pathUSD address; current compiled identity begins at epoch 0 |
Settlement contract address, runtime bytes/hash, deployment block/checkpoint, authentic account/storage proof |
All TIP-20 tokens use six decimals at the inspected revision, so one pathUSD base unit is one microdollar. The contract address and code hash rows must remain unset until an actual reviewed deployment supplies them. No address in this document is a proposed settlement deployment address.
The pathUSD address and denomination are fixed, but token behavior remains a trust dependency on Tempo's protocol and the asset issuer; this profile does not separately prove or freeze token implementation storage/code. A Tempo change that alters transfers or that address's asset semantics requires profile requalification and pause where compatibility is uncertain. Pinning the settlement contract's code does not remove that dependency.
An identity may have rotated since the inspected source snapshot. Activation therefore verifies the identity and checkpoint against the reviewed deployment record rather than copying a repository constant blindly.
3. Canonical proof interface
The selected certificate source is Tempo 25b216a661c2cac72807174583371b582b75c286,
crates/consensus/src/finalization_verifier/mod.rs:9-24,89-104, backed by Commonware 2026.9.0:
Finalization<Scheme<commonware_ed25519::PublicKey, MinSig>, TempoBlockDigest>. The last name is a
Makechain external wrapper with the same fixed 32-byte codec as Tempo's consensus::Digest(B256);
it carries Tempo's Keccak header hash, not a Makechain block hash. The namespace is the exact bytes
TEMPO (crates/consensus/src/config.rs:46). These type/profile choices are defined; compile and
authentic deployed-source vectors remain required. Earlier 2026.7.1 fixtures do not qualify the
settlement contract or prove the current deployed network's codec.
The credit variant carries only self-contained proof material plus canonical framing. It does not carry trusted RPC interpretations:
TempoCumulativeProof {
settlement_profile_id,
finalization_certificate_bytes,
tempo_header_rlp,
contract_account_proof_nodes[],
beneficiary_storage_proof_nodes[],
}
The permissionless Makechain system envelope already names the beneficiary; IMPORT_CREDITS has no
account signature. The verifier reconstructs the contract account key and beneficiary slot from
that beneficiary and the active profile. It does not accept a relayer-supplied account address,
storage root, code hash, slot, cumulative, header hash, chain ID, or beneficiary as an authoritative
field. An eth_getProof response may be a capture format, but its JSON fields are not consensus
facts; only the canonical proof nodes verified under the certified state root establish those facts.
3.1 Verification sequence
Verification is pure, deterministic, and performs no RPC:
- Before cryptography, require the exact active profile ID and enforce every byte/count bound. Reject zero, surplus, reordered, duplicate, unconsumed, or noncanonical proof nodes. Decoder selection comes from the profile; there is no in-band version sniffing.
- Evaluate the existing Makechain pause and prior-checkpoint dual trust deadline before accepting
evidence under the identity. Decode the finalization certificate exactly, require
certificate.epoch >= anchor.identity.from_epoch, and BLS-verify it under the active anchor's group key and the static profile's certificate codec. Its authenticated result is the proposal payload/block hash, not a chain ID. - Decode exactly one Tempo header from
tempo_header_rlp, reject trailing bytes, require canonical decode/re-encode equality, and requirekeccak256(tempo_header_rlp)to equal the certified payload. Extract height and EVM state root. Requiretimestamp_millis_part < 1000and computetempo_timestamp_ms = checked(inner.timestamp_seconds * 1000 + timestamp_millis_part). Do not use saturating arithmetic or compare seconds to Makechain milliseconds. Enforce the existing future-skew and checkpoint timing rules on this normalized value. - Against
header.state_root, verify the contract account inclusion path atkeccak256(settlement_contract_bytes20). Reject account exclusion. Decode the canonical Ethereum account leaf[nonce, balance, storageRoot, codeHash], require the provedcodeHashto equal the pinned immutable runtime code hash, and take only its provedstorageRootforward. - Recompute
mapping_slot(beneficiary)from section 1.4 and verify its inclusion against that storage root using trie keykeccak256(mapping_slot). Reject a proof for another beneficiary, base slot, contract, storage root, or leaf. Decode exactly one canonical unsigned RLP integer, require it to fit the profile's 256-bit width, and call itproved_cumulative. - Require
proved_cumulative > AccountLedger.credited. On first import, create the ledger, setcredited = proved_cumulative, charge the normal first-ledger creation fee tospent, and require the resulting ledger invariant. On later imports setcredited = proved_cumulative; never add the full cumulative and never reducecredited,spent, orlocked. - If the proof header is strictly newer than the current Tempo checkpoint and satisfies the normal refresh rules, update the checkpoint in the same successful state batch. An older qualifying credit proof may advance a beneficiary without moving the checkpoint. Any failure leaves both ledger and checkpoint unchanged.
A refresh-only import stops after certified-header verification and the normal strict checkpoint advance. It contains no account or storage proof and has no monetary effect.
At command execution time, compute the existing dual deadline as
min(checked(checkpoint.accepted_at_makechain_timestamp_ms + trusting_period_ms), checked(checkpoint.tempo_timestamp_ms + trusting_period_ms + max_source_clock_skew_ms)) and require
the Makechain block timestamp to be strictly before it before authenticating the new evidence. Also
require the proved tempo_timestamp_ms not to lead the Makechain block timestamp by more than
max_source_clock_skew_ms. These comparisons use one width and one unit.
The reference codebase's Tempo verifier is reusable only for a qualified version of the certificate decode,
BLS verification, and certified-header binding steps. Its receipt-trie inclusion, Rent event
decode, per-log claim identifier, and replay scheme are deliberately absent from this interface. Its
old claim result exposes only inner.timestamp seconds; this interface must instead validate and
combine Tempo's separate millisecond part as above.
The account path preimage is exactly the 20 contract-address bytes. The storage mapping preimage is exactly two 32-byte ABI words, and its secure-trie key hashes the resulting 32-byte mapping slot once more. Fixtures must catch 20-versus-32-byte account hashing, address left-padding, base-slot endian, missing second Keccak, raw-32-byte versus RLP leaf interpretation, and trailing RLP bytes.
3.2 Provisional devnet evidence bounds
Bounds are consensus parameters in the profile and are checked before allocation, RLP traversal, hashing, or BLS verification. The following is the initial devnet configuration candidate, pending qualification against the first authentic settlement fixture and adversarial vectors:
| Item | Devnet candidate maximum |
|---|---|
| Finalization certificate | 512 bytes |
| Tempo header RLP | 4 KiB |
| Account proof | 65 nodes, 1 KiB/node, 64 KiB total |
| Storage proof | 65 nodes, 1 KiB/node, 64 KiB total |
| Complete credit evidence | 136 KiB encoded, including framing |
The 65-node candidates follow the 64-nibble secure-trie keys and one terminal node; the 1 KiB per-node cap is intentionally above an ordinary 17-item branch RLP. These values become fixed only after the actual deployed account/storage proof passes and worst-case canonical paths are generated. Changing a bound or proof codec requires a profile release; a node-local limit is invalid.
4. Rejected launch alternative: proxy
A proxy was considered because retaining its storage would simplify code replacement. It was rejected for launch because an upgrade authority could change deposit and cumulative semantics, and safe verification would require proving and pinning the proxy code, EIP-1967 implementation slot, and implementation account and code identity in addition to the cumulative. Even then, every allowed implementation change would require a Makechain profile release. A proxy is not an active launch fallback.
5. Successors, reset, and migration
Makechain keeps one lifetime credited frontier per beneficiary, not one frontier per settlement
contract. Installing a successor therefore does not create a fresh namespace. At a frozen cutover,
each successor baseline must equal the predecessor cumulative for that beneficiary. A lower/reset
baseline withholds or delays credit for real post-cut deposits until the new cumulative overtakes the
already imported frontier. A higher baseline creates unpaid credit. “At least the predecessor” is
therefore too weak.
The cutover also must ensure that deposits accepted by the predecessor after its snapshot are not lost and that the successor adds only actual post-cut deposits. The launch ABI above has no deprecation, migration, or baseline-write call. An immutable mapping cannot prove global migration completeness in constant work, and a Makechain release installer cannot scan all beneficiaries. A future successor needs a separately reviewed cutover mechanism and qualification evidence showing exact frontier carry-forward and deposit continuity. Possible mechanisms have different trust and complexity costs; none is selected here. Governance attestation alone is the current specification's trust boundary, not an on-chain proof of completeness. Until a released migration design exists, a code defect that requires replacement is an explicit settlement-import blocker, not permission to reset the frontier silently.
A pre-mainnet Makechain reset is different. It creates a new Makechain chain_id and empty local
ledger. An already captured Tempo proof may be imported again only if it independently satisfies the
new chain's active settlement profile and is wrapped in a new-chain command envelope. That is a new
chain deriving credit from the same external lifetime fact; it is not automatic state migration and
does not make pre-reset chain signatures, receipts, or proofs valid.
6. Required contract and verifier vectors
End-to-end positive settlement qualification is never synthetic. Moderato qualification requires an actual call to the deployed settlement contract, a real successful pathUSD transfer, an authentic certified Tempo header, and authentic contract-account and beneficiary-storage proofs captured for that header. The fixture manifest also records the network/profile ID, deployment address, runtime bytes and code hash, transaction hash for provenance, beneficiary, mapping slot, old/new token balances, expected cumulative, header height and timestamp, certificate bytes, all proof nodes, capture tool revision, and capture time. Consensus uses the certificate/header/account/storage evidence, not the transaction hash or RPC assertions.
Generated local MPT/RLP fixtures remain appropriate for decoder, trie-boundary, overflow, and negative unit tests, and contract tests may use controlled token/test state. They do not qualify a live anchor or a funded end-to-end import and must be labelled as generated test vectors rather than captured Tempo evidence.
Required positive and boundary cases include:
- payer equals beneficiary and payer differs from beneficiary;
- first import, later strict advance, multiple deposits before one import, the largest practically constructible in-profile boundary value, donation before deposit, withdrawal before proof capture, and pause/unpause around deposits;
- account and storage proofs at varied trie depths, including inline and hashed child forms; and
- byte-identical decode/re-encode and equal native/wasm results under the pinned profile.
Required rejection cases include:
- wrong profile, group key, epoch floor, certificate bytes, certified payload/header hash, checkpoint height rule, expired trust, or excessive source clock skew;
- malformed, noncanonical, trailing, oversized, surplus, missing, reordered, or duplicated proof material;
- excluded/wrong contract account, wrong code hash, proxy code under an immutable profile, wrong account key preimage, wrong state root, or malformed account leaf;
- wrong beneficiary, mapping base slot, padding/endian, storage root, missing second Keccak, storage exclusion/zero, malformed leaf RLP, value width overflow, equal cumulative, or lower cumulative;
- a valid
Depositedevent/receipt without the account/storage proof; - zero beneficiary/amount, false or short token transfer, fee-on-transfer token in contract-unit tests, arithmetic overflow, unauthorized role transition/withdrawal, and reentrancy; and
- failed credit import attempting to refresh the checkpoint or mutate any ledger field.
Mainnet activation requires its own authentic mainnet fixture and deployment manifest. A Moderato fixture cannot establish a mainnet contract address, code hash, identity, checkpoint, or storage proof.
The evidence behind these selections is recorded in architecture evidence (external site) section 12.