0.16.13: repair ~130 client selectors that did not match the deployed contracts (D-512)

Ten clients (RaaS, Sink, Agent and x402 settlement, state channels, compliance,
lending, the compliant pool) carried hand-written function selectors that matched
neither the compiled ABI nor the signature written next to them, so every call
through them reverted. Every selector is now the keccak of a signature in the
compiled ABI. Functions the contracts never had are replaced by the real ones:
AereSanctionsRegistry (latestEpoch, isSanctioned with epoch, isSanctionedLatest,
currentRoot via snapshotStatus), ChainalysisOracleWrapper (upstreamOracle),
AereTravelRuleHashRegistry (status by commitment id), AereInsuranceFund (two-step
proposeMarket + registerMarket). AereRaaSFactoryClient.registerRollup targets the
canonical AereRaaSFactoryV2 (11 parameters, optional defenseGrace).

Proof: the gate aerenew/scripts/deschise/sdk-selectori-corecti.cjs reads 330 of
330 selectors correct and fails on a planted one; src/test/selectors-live.ts reads
18 values from the deployed contracts through the shipped clients, and the old
rollupCount selector still reverts (its negative control).

Also: state-window-live records that both public endpoints now refuse state
outside the 512-block window ("World state unavailable") instead of returning a
false 0x0, and proves the guard against an endpoint that still lies; npm test
runs the offline suite through scripts/run-tests.mjs (61/61), live proofs stay
separate. README network facts brought to 2026-10-15.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aere Network 2026-10-15 15:10:49 +03:00
parent 0e4339632b
commit 6f13c671f1
15 changed files with 384 additions and 208 deletions

View File

@ -5,8 +5,18 @@ live AERE L1 (chain ID 2800): the DeFi, agentic-settlement, compliance,
account-abstraction and post-quantum contract surface, the seedless hybrid PQC
wallet, and AERE's native NIST post-quantum precompiles.
**SDK version:** 0.16.12
**Last updated:** 2026-08-19 (consensus and network facts; the rest is as of 2026-07-16)
**SDK version:** 0.16.13
**Last updated:** 2026-10-15 (network facts, state window, client selectors; the rest is as of 2026-07-16)
**0.16.13, 2026-10-15: client selectors repaired.** Until this release about 130 hand-written function selectors in ten
clients (RaaS, Sink, Agent and x402 settlement, state channels, compliance, lending, the compliant pool) did not match
the deployed contracts, so calls through them reverted. Every selector is now the keccak of a signature in the compiled
ABI, checked by a gate on each change, and `src/test/selectors-live.ts` reads the deployed contracts through the shipped
clients. API changes: `AereSanctionsRegistryClient` gains `latestEpoch()`, `isSanctioned(epoch, ...)` and
`isSanctionedLatest(...)`, and `currentRoot(epoch?)` now reads the root of an epoch; `ChainalysisOracleWrapperClient`
gains `upstreamOracle()`; `AereTravelRuleHashRegistryClient.status` takes a commitment id; `AereInsuranceFundClient`
registers a market in two steps, `proposeMarket` then `registerMarket`; `AereRaaSFactoryClient.registerRollup` targets
the canonical `AereRaaSFactoryV2` and accepts an optional `defenseGrace`.
**Network state at this release:** 35+ typed contract clients over a canonical
address book of 130+ live contracts on chain 2800.
@ -18,10 +28,10 @@ address book of 130+ live contracts on chain 2800.
|---|---|
| Chain name | AERE Network |
| Chain ID | 2800 (`0xAF0`) |
| Consensus | Hyperledger Besu QBFT, classical secp256k1 ECDSA |
| Validators | 7, Byzantine-fault tolerance f=2, commit quorum 5 of 7 |
| Operator / client diversity | Single operator (Foundation), single client (Besu) today |
| Block period | 0.5 s (500 ms) target, sub-second deterministic finality |
| Consensus | QBFT; per-block finality on classical secp256k1 ECDSA commit seals, with post-quantum validator seals on consensus messages and a post-quantum certificate under every 32nd block's hash (details on aere.network) |
| Validators | 10 since 2026-09-11, Byzantine-fault tolerance f=3, commit quorum 7 of 10 |
| Operator / client diversity | Single operator (the Foundation); two client implementations, Besu-derived and Nethermind-derived (one validator, since 2026-09-11) |
| Block period | 0.5 s target; measured 0.62 s (median of five 20,000-block windows, 2026-10-15); deterministic finality |
| Native token | AERE (18 decimals) |
| Max supply | 2,800,000,000 AERE (capped at genesis-v2, 2026-05-07) |
| EVM ruleset | Pectra + Fusaka opcode and precompile set (Besu), plus five AERE post-quantum precompiles; not full parity with Ethereum mainnet (the beacon-chain system contracts, e.g. EIP-4788 beacon roots, have no code on chain 2800) |
@ -245,28 +255,31 @@ Options:
$ aere chain
AERE chain — https://rpc.aere.network
Chain ID 2800 (0xaf0)
Block height 9520927
Base fee 1.0 gwei
Gas price 1.0 gwei
Validators 7
Peers 7
Block height 22945693
Base fee 1.0 gwei (1000000000 wei)
Gas price 1.0 gwei (1000000000 wei)
Validators 10
- 0x38fb1510d4d11624564a74e6ebfd752e666849c3
...
```
(Output of 2026-10-15. Since block 20,836,228 the validator list holds post-quantum validator ids, not ECDSA addresses.)
## State window on the public RPC (read before pinning a historical block)
Both public endpoints are **pruning full nodes**, not archive nodes.
- Blocks, transactions, receipts, logs and `qbft_getValidatorsByBlockNumber` go back to **block 0**.
- **World state is kept for only about the most recent 512 blocks**, roughly 4 minutes 25 seconds
at the measured 517 ms block interval. That bounds `eth_getBalance`, `eth_getCode`,
- **World state is kept for only about the most recent 512 blocks**, roughly 5 minutes 17 seconds
at the 0.62 s block interval measured on 2026-10-15. That bounds `eth_getBalance`, `eth_getCode`,
`eth_getStorageAt`, `eth_call`, `eth_getProof` and `eth_getTransactionCount`.
Outside the window most methods say so: `eth_getBalance` / `eth_getCode` / `eth_getStorageAt`
return `null`, and `eth_getProof` returns `-32000 World state unavailable`.
**`eth_getTransactionCount` does not.** It returns `0x0`, which is well formed and
indistinguishable from an account that has never transacted. Reproduce it yourself: block
8,236,382 contains a transaction from `0xbeb33d20dfbbd49ec7ac1f617667f1f02dfd6465` with nonce
123,063, and the endpoint still answers `0x0` for that address at that block.
Outside the window both public endpoints now say so for every state method: measured on 2026-10-15,
`eth_getTransactionCount`, `eth_getBalance` and `eth_getProof` all return `-32000 World state unavailable`.
Until October 2026 they did not: `eth_getBalance` / `eth_getCode` / `eth_getStorageAt` returned `null`, and
**`eth_getTransactionCount` returned `0x0`**, well formed and indistinguishable from an account that has
never transacted (block 8,236,382 holds a transaction from `0xbeb33d20dfbbd49ec7ac1f617667f1f02dfd6465` with
nonce 123,063, and the endpoint answered `0x0` for that address at that block). Other endpoints and other
node versions may still answer that way, which is why the reader below keeps its guards.
`StateWindowReader` returns a value or throws. It never returns a placeholder.
@ -301,11 +314,11 @@ pinning a historical block; and never test a nonce for zero at a historical bloc
pass that signer into the SDK, or use the seedless PQC wallet above.
- **Fiat rails are out of scope.** AERE is the on-chain settlement layer; fiat
IBANs / cards / SEPA come from a BaaS partner (Striga, Baanx, Kulipa, etc.).
- **Decentralization.** 7 QBFT validators give f=2 (the chain keeps producing
blocks while up to 2 validators are down), commit quorum 5 of 7. All 7 are
operated by the Foundation on a single client (Besu), so this is
fault-tolerance, not yet operator or client diversity. Broader validator
operators and a second client are roadmap.
- **Decentralization.** 10 QBFT validators give f=3 (the chain keeps producing
blocks while up to 3 validators are down), commit quorum 7 of 10. All 10 are
operated by the Foundation; nine run the Besu-derived client and one, since
2026-09-11, the Nethermind-derived client. That is fault tolerance and a second
code base, not yet operator diversity: independent validator operators are roadmap.
- **Foundation key, not a multisig.** `0x0243A4f47D44b40b65D33f20329dE20D00c6f3C3`
is a single-key EOA (zero bytecode on-chain), described here as a
Foundation-controlled account. A real Safe-style threshold multisig is roadmap,

View File

@ -1,6 +1,6 @@
{
"name": "@aere/sdk",
"version": "0.16.12",
"version": "0.16.13",
"description": "Official AERE Network SDK: typed ethers v6 clients for AERE L1 (chain ID 2800). 35+ typed clients over a 130+ contract address book, covering core DeFi, agentic x402 settlement, compliance, ERC-4337 account abstraction, t-of-n post-quantum custody, the 5 live NIST PQC precompiles (Falcon-512/1024, ML-DSA-44, SLH-DSA-128s, SHAKE256), and a seedless hybrid PQC wallet.",
"type": "module",
"main": "./dist/index.js",
@ -24,7 +24,7 @@
],
"scripts": {
"build": "tsc",
"test": "node --test dist/test/*.js",
"test": "node scripts/run-tests.mjs",
"test:live": "node dist/test/state-window-live.js"
},
"keywords": [

14
scripts/run-tests.mjs Normal file
View File

@ -0,0 +1,14 @@
// Runs the offline test suite: every dist/test/*.js except the *-live.js proofs, which need the network
// (run those explicitly, e.g. `npm run test:live`). Portable: no shell globbing, works the same under cmd and sh.
import { readdirSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
const files = readdirSync('dist/test')
.filter((f) => f.endsWith('.js') && !/-live(\.test)?\.js$/.test(f))
.map((f) => `dist/test/${f}`);
if (files.length === 0) {
console.error('no compiled tests in dist/test; run `npm run build` first');
process.exit(2);
}
const r = spawnSync(process.execPath, ['--test', ...files], { stdio: 'inherit' });
process.exit(r.status ?? 1);

View File

@ -8,14 +8,14 @@ export interface Eip1193Provider {
// 4-byte selectors (precomputed; never call eth_sign just to derive them).
const SEL = {
registerAgent: '0xa30b3acc', // registerAgent(address,bytes32,bytes32,uint128,uint128,uint128,string)
setSigner: '0x6c19e783', // setSigner(uint256,address)
setRateLimits: '0x44dec0e2', // setRateLimits(uint256,uint128,uint128,uint128)
setPaused: '0x8456db15', // setPaused(uint256,bool)
topUp: '0xc35a8e8b', // topUp(uint256,address,uint256)
withdraw: '0x069a89ec', // withdraw(uint256,address,uint256,address)
signerOf: '0x9f9eb5b1', // signerOf(uint256) view returns (address)
balances: '0xa7263000', // balances(uint256,address) view returns (uint256)
registerAgent: '0x8d471e06', // registerAgent(address,bytes32,bytes32,uint128,uint128,uint128,string)
setSigner: '0x85267907', // setSigner(uint256,address)
setRateLimits: '0x70c4e4df', // setRateLimits(uint256,uint128,uint128,uint128)
setPaused: '0xdaff97b5', // setPaused(uint256,bool)
topUp: '0x16645824', // topUp(uint256,address,uint256)
withdraw: '0x5f0f48bd', // withdraw(uint256,address,uint256,address)
signerOf: '0x5161fdf5', // signerOf(uint256) view returns (address)
balances: '0x1f320331', // balances(uint256,address) view returns (uint256)
} as const;
function pad32Hex(hex: string): string {
@ -175,7 +175,7 @@ export function buildAuthTypedData(opts: {
};
}
const SEL_SETTLE = '0x0c0a40c3'; // settle(uint256,address,address,uint256,bytes32,uint256,uint256,bytes)
const SEL_SETTLE = '0xdf3beb47'; // settle(uint256,address,address,uint256,bytes32,uint256,uint256,bytes)
export class AERE402SettlementClient {
constructor(public readonly provider: Eip1193Provider, public readonly facilitator: string) {}

View File

@ -135,10 +135,10 @@ export class AereAIReputationClient {
const INFER_SEL = {
modelEpochCount: '0xf407005b',
modelOf: '0x1548a0e9',
epochOf: '0xc1a5ae12',
epochOf: '0x033ee480', // epochOf(address,bytes32,uint64)
registerModel: '0x703722ac',
commitEpoch: '0x7f11fb10',
verifyInference: '0xa2a98a64',
verifyInference: '0x46a121a5', // verifyInference(address,bytes32,uint64,bytes32,bytes32,uint64,bytes32,bytes32[])
} as const;
export class AereInferNetClient {

View File

@ -51,15 +51,15 @@ function encDynBytes(hex: string): { offset: bigint; encoded: string } {
}
const SEL = {
CHALLENGE_WINDOW: '0xe9333e75',
channels: '0xb29a8140', // channels(bytes32)
open: '0x73c0d3a9', // open(address,address,address,uint256,uint256)
cooperativeClose: '0xa9fbe4b6', // cooperativeClose(ChannelState,bytes,bytes)
startChallenge: '0xd1e26000', // startChallenge(ChannelState,bytes,bytes)
overrideChallenge: '0x44b04beb', // overrideChallenge(ChannelState,bytes,bytes)
finalize: '0x6e3e98bf', // finalize(bytes32)
settleHtlc: '0xa84f3c0e', // settleHtlc(bytes32,bytes)
withdraw: '0x51cff8d9', // withdraw(bytes32)
CHALLENGE_WINDOW: '0xd62aad29', // CHALLENGE_WINDOW()
channels: '0x7a7ebd7b', // channels(bytes32)
open: '0xf9bff880', // open(address,address,address,uint256,uint256)
cooperativeClose: '0xeb8edd09', // cooperativeClose((bytes32,uint256,uint256,uint256,uint256,uint256,bytes32,uint256),bytes,bytes)
startChallenge: '0x1b6a88c1', // startChallenge((bytes32,uint256,uint256,uint256,uint256,uint256,bytes32,uint256),bytes,bytes)
overrideChallenge: '0x70c84312', // overrideChallenge((bytes32,uint256,uint256,uint256,uint256,uint256,bytes32,uint256),bytes,bytes)
finalize: '0x92584d80', // finalize(bytes32)
settleHtlc: '0x9be71c4d', // settleHtlc(bytes32,bytes)
withdraw: '0x8e19899e', // withdraw(bytes32)
} as const;
export class AereStateChannelsClient {

View File

@ -125,23 +125,23 @@ const POOL_SEL = {
publishAssociationRoot: '0x9f92a5b1',
withdraw: '0xb7433691',
// R5/R6 additions
proposeAssociationRoot: '0x73fc3b6a', // proposeAssociationRoot(bytes32)
proposeAssociationRoot: '0xcca5d27b', // proposeAssociationRoot(bytes32)
challengeAssociationRoot: '0x45cb9085', // challengeAssociationRoot(bytes32,string) — bond required
dismissAssociationRootChallenge: '0x793819b9',
associationRootProposedAt: '0x68c46b72',
associationRootChallenged: '0x8a76c7c8',
associationRootDismissCount: '0x6c7560c8',
associationRootChallenger: '0xbb84a4c8',
associationRootProposedAt: '0xaff8a8bd', // associationRootProposedAt(bytes32)
associationRootChallenged: '0x7cb2674e', // associationRootChallenged(bytes32)
associationRootDismissCount: '0x5b2aa748', // associationRootDismissCount(bytes32)
associationRootChallenger: '0x957d8c14', // associationRootChallenger(bytes32)
CHALLENGE_BOND: '0x9790ce71',
MAX_DISMISSALS_PER_ROOT: '0x9c9eea4f',
CHALLENGE_BOND_BURN: '0x9f3098ff',
BOND_TOKEN: '0x10ee36c5',
isKnownRoot: '0x2b7ac3f3',
getLastRoot: '0x4cf088d9',
ROOT_HISTORY_SIZE: '0xb0d3e9b4',
TREE_DEPTH: '0x7d9f6c6f',
nextLeafIndex: '0x3b15aabf',
ASSOCIATION_ROOT_CHALLENGE_WINDOW: '0x37dd9e6c',
MAX_DISMISSALS_PER_ROOT: '0xde2ccfa5', // MAX_DISMISSALS_PER_ROOT()
CHALLENGE_BOND_BURN: '0xf10dca34', // CHALLENGE_BOND_BURN()
BOND_TOKEN: '0xf21b52e5', // BOND_TOKEN()
isKnownRoot: '0x6d9833e3', // isKnownRoot(bytes32)
getLastRoot: '0xba70f757', // getLastRoot()
ROOT_HISTORY_SIZE: '0xcd87a3b4', // ROOT_HISTORY_SIZE()
TREE_DEPTH: '0xf1650536', // TREE_DEPTH()
nextLeafIndex: '0x0be4f422', // nextLeafIndex()
ASSOCIATION_ROOT_CHALLENGE_WINDOW: '0x03cf6363', // ASSOCIATION_ROOT_CHALLENGE_WINDOW()
} as const;
export class AereCompliancePoolClient {

View File

@ -35,27 +35,56 @@ function encodeDynamicBytes(hex: string): { offset: string; body: string } {
}
/* ===================== AereSanctionsRegistry ====================== */
/* 2026-10-15 (D-512): this client called currentRoot() and an isSanctioned(address,uint16,bytes32[]) that the deployed
* contract does not have. The registry keeps one Merkle root per epoch: read latestEpoch(), the root and state of an epoch
* through snapshotStatus(uint256), and check an address with a Merkle proof against a given or the latest attested epoch. */
const SANCT_SEL = {
currentRoot: '0x6f29ed46', // currentRoot()
isSanctioned: '0x7ad8e02b', // isSanctioned(address,uint16,bytes32[])
commitRoot: '0x9b30b9ae', // commitRoot(bytes32,uint16,string) — placeholder
latestEpoch: '0x9cb118bf', // latestEpoch()
snapshotStatus: '0x3d1146fa', // snapshotStatus(uint256)
isSanctioned: '0x7acc3cf1', // isSanctioned(uint256,address,uint16,bytes32[])
isSanctionedLatest: '0xf6035837', // isSanctionedLatest(address,uint16,bytes32[])
} as const;
/** list ids as defined by the contract (LIST_OFAC_SDN = 1, ...). */
export const SANCTIONS_LIST = { OFAC_SDN: 1, OFAC_SSI: 2, EU_CONSOLIDATED: 3, UK_HMT: 4, UN_CONSOLIDATED: 5 } as const;
const encProof = (proof: string[]) => encUint(BigInt(proof.length)) + proof.map(encBytes32).join('');
export class AereSanctionsRegistryClient {
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
async currentRoot(): Promise<string> {
return await ethCall(this.provider, this.address, SANCT_SEL.currentRoot);
async latestEpoch(): Promise<bigint> {
return BigInt(await ethCall(this.provider, this.address, SANCT_SEL.latestEpoch));
}
/** Root committed for `epoch` (default: the latest epoch). The registry holds one snapshot per epoch. */
async currentRoot(epoch?: bigint): Promise<string> {
const e = epoch ?? await this.latestEpoch();
const res = await ethCall(this.provider, this.address, SANCT_SEL.snapshotStatus + encUint(e));
return '0x' + res.replace(/^0x/, '').slice(0, 64);
}
/** True iff the proof shows `addr` on `listId` in snapshot `epoch` AND that snapshot is attested. */
async isSanctioned(epoch: bigint, addr: string, listId: number, proof: string[]): Promise<boolean> {
const data = SANCT_SEL.isSanctioned + encUint(epoch) + encAddr(addr) + pad32(listId.toString(16)) + encUint(4n * 32n) + encProof(proof);
return BigInt(await ethCall(this.provider, this.address, data)) === 1n;
}
/** Same check against the latest attested epoch; returns the verdict and that epoch (0 if none is attested). */
async isSanctionedLatest(addr: string, listId: number, proof: string[]): Promise<{ sanctioned: boolean; attestedEpoch: bigint }> {
const data = SANCT_SEL.isSanctionedLatest + encAddr(addr) + pad32(listId.toString(16)) + encUint(3n * 32n) + encProof(proof);
const res = (await ethCall(this.provider, this.address, data)).replace(/^0x/, '');
return { sanctioned: BigInt('0x' + res.slice(0, 64)) === 1n, attestedEpoch: BigInt('0x' + res.slice(64, 128)) };
}
}
/* ===================== ChainalysisOracleWrapper =================== */
const CHAIN_SEL = {
isSanctioned: '0xdf592f7d', // isSanctioned(address)
upstream: '0x6c0360eb', // upstream()
locked: '0xcf30901', // locked()
isSanctioned: '0xdf592f7d', // isSanctioned(address)
upstreamOracle: '0xff132a15', // upstreamOracle()
locked: '0xcf309012', // locked()
} as const;
export class ChainalysisOracleWrapperClient {
@ -70,32 +99,44 @@ export class ChainalysisOracleWrapperClient {
return true; // fails closed
}
}
/** The upstream oracle the wrapper forwards to; the zero address means none is configured and every call fails closed. */
async upstreamOracle(): Promise<string> {
return '0x' + (await ethCall(this.provider, this.address, CHAIN_SEL.upstreamOracle)).slice(-40);
}
}
/* ===================== AereTravelRuleHashRegistry ================= */
/* 2026-10-15 (D-512): this client read committedHashes(bytes32) and status(bytes32), which the contract does not have.
* Commitments are numbered: read one by id through commitments(uint256). */
const TRR_SEL = {
committedHashes: '0x6c5e6e95', // committedHashes(bytes32)
status: '0x200d2ed2', // status(bytes32)
commitments: '0x49ce8997', // commitments(uint256)
nextCommitmentId: '0x5ac48e6d', // nextCommitmentId()
} as const;
export class AereTravelRuleHashRegistryClient {
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
/** State enum: 0=Unknown, 1=Committed, 2=Acknowledged, 3=Disputed. */
async status(hash: string): Promise<number> {
const res = await ethCall(this.provider, this.address, TRR_SEL.status + encBytes32(hash));
return Number(BigInt(res));
async nextCommitmentId(): Promise<bigint> {
return BigInt(await ethCall(this.provider, this.address, TRR_SEL.nextCommitmentId));
}
/** State of commitment `id`: 0=Committed, 1=Acknowledged, 2=Disputed (the contract's State enum). */
async status(id: bigint): Promise<number> {
const res = (await ethCall(this.provider, this.address, TRR_SEL.commitments + encUint(id))).replace(/^0x/, '');
// words: payloadHash, originatorVasp, beneficiaryVasp, threshold, committedAt, statusUpdatedAt, state, offset(disputeReason)
return Number(BigInt('0x' + res.slice(6 * 64, 7 * 64)));
}
}
/* ===================== AereForensicEventRegistry ================== */
const FORENSIC_SEL = {
subjectAlertCount: '0x7c1d3d0a', // subjectAlertCount(address)
recentConfirmedAlertCount: '0x44e3a39d', // recentConfirmedAlertCount(address,uint8,uint256)
nextBotId: '0xf4cae09e', // nextBotId()
nextAlertId: '0xa0d49d3a', // nextAlertId()
subjectAlertCount: '0x3fa664d6', // subjectAlertCount(address)
recentConfirmedAlertCount: '0xd9f77c99', // recentConfirmedAlertCount(address,uint8,uint256)
nextBotId: '0x6fc0c79d', // nextBotId()
nextAlertId: '0x260be2a2', // nextAlertId()
} as const;
export class AereForensicEventRegistryClient {
@ -116,9 +157,9 @@ export class AereForensicEventRegistryClient {
/* ===================== AereZKScreen =============================== */
const ZK_SEL = {
isCleared: '0xb6a5d7de', // isCleared(address,bytes32,uint256)
clearedAt: '0xc5f4dec0', // clearedAt(address,bytes32)
submitProof: '0x77a91da7', // submitProof(bytes32,bytes,bytes)
isCleared: '0x011f0451', // isCleared(address,bytes32,uint256)
clearedAt: '0xedddc71f', // clearedAt(address,bytes32)
submitProof: '0xeb90e2cb', // submitProof(bytes32,bytes,bytes)
} as const;
export class AereZKScreenClient {
@ -163,10 +204,10 @@ export class AereZKScreenClient {
* proof of inference see AereZKMLVerifier. */
const AI_SEL = {
models: '0x88e98b22', // models(bytes32)
attestationCount: '0x8b2c3a99', // attestationCount(bytes32)
registerModel: '0xa9b5a9eb', // registerModel(bytes32,address,string)
anchor: '0x5b9babe0', // anchor(bytes32,bytes32,bytes32,bytes32,uint256,uint256,bytes)
models: '0xe236f46b', // models(bytes32)
attestationCount: '0xd65ddd52', // attestationCount(bytes32)
registerModel: '0x31c0953b', // registerModel(bytes32,address,string)
anchor: '0x10912ffc', // anchor(bytes32,bytes32,bytes32,bytes32,uint256,uint256,bytes)
} as const;
export class AereAIProofClient {

View File

@ -21,15 +21,16 @@ async function ethSend(p: RpcProvider, from: string, to: string, data: string):
}
const SEL = {
FOUNDATION: '0x4d09f93e', // FOUNDATION()
COOLDOWN_SECONDS: '0xb47d5cf5', // COOLDOWN_SECONDS()
markets: '0x2c43cab8', // markets(address)
FOUNDATION: '0x65df2e51', // FOUNDATION()
COOLDOWN_SECONDS: '0x72b49d63', // COOLDOWN_SECONDS()
markets: '0x8e8f294b', // markets(address)
balances: '0x27e235e3', // balances(address)
coverageRemaining: '0x1d29df0e', // coverageRemaining(address)
cooldownReady: '0x3a09a73e', // cooldownReady(address)
registerMarket: '0xc6da18d4', // registerMarket(address,uint256)
donate: '0xf14fcbc8', // donate(address,uint256)
coverBadDebt: '0xa90c1e1c', // coverBadDebt(address,address,uint256)
coverageRemaining: '0xb8046933', // coverageRemaining(address)
cooldownReady: '0xd881ceec', // cooldownReady(address)
proposeMarket: '0xe993ea12', // proposeMarket(address,uint256)
registerMarket: '0xa79b9ec9', // registerMarket(address)
donate: '0xe69d849d', // donate(address,uint256)
coverBadDebt: '0x71c8886f', // coverBadDebt(address,address,uint256)
} as const;
export class AereInsuranceFundClient {
@ -59,9 +60,18 @@ export class AereInsuranceFundClient {
return ethSend(this.provider, from, this.address, SEL.donate + encAddr(token) + encUint(amount));
}
/** Foundation-only: register a market with a lifetime coverage cap. */
async registerMarket(from: string, market: string, lifetimeCap: bigint): Promise<string> {
return ethSend(this.provider, from, this.address, SEL.registerMarket + encAddr(market) + encUint(lifetimeCap));
/**
* Foundation-only, step 1 of 2: propose a market with a lifetime coverage cap. The contract enforces
* REGISTRATION_DELAY before step 2. (2026-10-15: this client used to send a one-step
* registerMarket(address,uint256) that the contract does not have.)
*/
async proposeMarket(from: string, market: string, lifetimeCap: bigint): Promise<string> {
return ethSend(this.provider, from, this.address, SEL.proposeMarket + encAddr(market) + encUint(lifetimeCap));
}
/** Foundation-only, step 2 of 2: register a proposed market once REGISTRATION_DELAY has passed. */
async registerMarket(from: string, market: string): Promise<string> {
return ethSend(this.provider, from, this.address, SEL.registerMarket + encAddr(market));
}
/** Foundation-only: pay down `amount` of `borrower`'s debt on `market`. */

View File

@ -21,47 +21,47 @@ export interface LendingMarketConfig {
const SEL = {
// immutable views
COLLATERAL: '0xfb4cd02f', // COLLATERAL()
DEBT_TOKEN: '0xea5b5ddf', // DEBT_TOKEN()
SINK: '0xf4f3b200', // SINK()
ORACLE: '0x7dc0d1d0', // ORACLE()
LTV_BPS: '0xcbc4cdc7', // LTV_BPS()
LIQ_THRESHOLD_BPS: '0xc0c5294d', // LIQ_THRESHOLD_BPS()
LIQ_BONUS_BPS: '0x3a86d9b3', // LIQ_BONUS_BPS()
BORROW_FEE_BPS: '0x47bf8b54', // BORROW_FEE_BPS()
MARKET_DEBT_CAP: '0xcca5e09e', // MARKET_DEBT_CAP()
COLLATERAL_DECIMALS: '0x99e9f4ad', // COLLATERAL_DECIMALS()
DEBT_DECIMALS: '0x4d9e7826', // DEBT_DECIMALS()
COLLATERAL: '0x24bbab8b', // COLLATERAL()
DEBT_TOKEN: '0x854db43c', // DEBT_TOKEN()
SINK: '0xae9fc205', // SINK()
ORACLE: '0x38013f02', // ORACLE()
LTV_BPS: '0xfae3706f', // LTV_BPS()
LIQ_THRESHOLD_BPS: '0xe3cd32d6', // LIQ_THRESHOLD_BPS()
LIQ_BONUS_BPS: '0xe3513531', // LIQ_BONUS_BPS()
BORROW_FEE_BPS: '0x185691da', // BORROW_FEE_BPS()
MARKET_DEBT_CAP: '0xfaac9ee3', // MARKET_DEBT_CAP()
COLLATERAL_DECIMALS: '0x4ddde78d', // COLLATERAL_DECIMALS()
DEBT_DECIMALS: '0xbad8a798', // DEBT_DECIMALS()
// state views
collateralOf: '0x8aef7ddf', // collateralOf(address)
debtOf: '0xcc4d2f49', // debtOf(address)
supplierScaled: '0x9d7d6f0d', // supplierScaled(address)
supplierBalance: '0x9d9f3ff6', // supplierBalance(address)
debtBalance: '0x33ed81e4', // debtBalance(address)
collateralOf: '0x1aefb107', // collateralOf(address)
debtOf: '0xd283e75f', // debtOf(address)
supplierScaled: '0x68fdada6', // supplierScaled(address)
supplierBalance: '0x1de233e8', // supplierBalance(address)
debtBalance: '0x11005b07', // debtBalance(address)
borrowIndex: '0xaa5af0fd', // borrowIndex()
supplyIndex: '0xb02f7d5e', // supplyIndex()
totalBorrowsScaled: '0xc0c44d6b', // totalBorrowsScaled()
totalSuppliedScaled: '0x7b0b3da7', // totalSuppliedScaled()
totalBorrowsCurrent: '0xc26c9116', // totalBorrowsCurrent()
totalSuppliedCurrent: '0x57e3c7fc', // totalSuppliedCurrent()
pendingSinkFee: '0x99c50fcb', // pendingSinkFee()
borrowRatePerSecond: '0x9e6dadce', // borrowRatePerSecond()
borrowPaused: '0x42b6e3bf', // borrowPaused()
depositPaused: '0x6a5db0fa', // depositPaused()
healthFactor: '0xe8b9c2dd', // healthFactor(address)
supplyIndex: '0x98f1bc12', // supplyIndex()
totalBorrowsScaled: '0x03647a88', // totalBorrowsScaled()
totalSuppliedScaled: '0x2785ce6d', // totalSuppliedScaled()
totalBorrowsCurrent: '0x73acee98', // totalBorrowsCurrent()
totalSuppliedCurrent: '0x9a1509b4', // totalSuppliedCurrent()
pendingSinkFee: '0x20a0e28c', // pendingSinkFee()
borrowRatePerSecond: '0x52609750', // borrowRatePerSecond()
borrowPaused: '0xbcb4bbea', // borrowPaused()
depositPaused: '0x02befd24', // depositPaused()
healthFactor: '0x6ad9f9df', // healthFactor(address)
// write
accrueInterest: '0x59601e8a', // accrueInterest()
flushSinkFee: '0x71a5ba73', // flushSinkFee()
supply: '0xf2b9fdb8', // supply(uint256)
accrueInterest: '0xa6afed95', // accrueInterest()
flushSinkFee: '0x599fb8ac', // flushSinkFee()
supply: '0x35403023', // supply(uint256)
redeem: '0xdb006a75', // redeem(uint256)
depositCollateral: '0x80aebec5', // depositCollateral(uint256)
withdrawCollateral: '0xeb0aaa4b', // withdrawCollateral(uint256)
depositCollateral: '0xbad4a01f', // depositCollateral(uint256)
withdrawCollateral: '0x6112fe2e', // withdrawCollateral(uint256)
borrow: '0xc5ebeaec', // borrow(uint256)
repay: '0x0e752702', // repay(uint256)
repayOnBehalfOf: '0x8f1a3bf0', // repayOnBehalfOf(address,uint256)
liquidate: '0xf5e3c462', // liquidate(address,uint256)
repay: '0x371fd8e6', // repay(uint256)
repayOnBehalfOf: '0xede66a45', // repayOnBehalfOf(address,uint256)
liquidate: '0xbcbaf487', // liquidate(address,uint256)
} as const;
/* ----------------------------- helpers ---------------------------------- */

View File

@ -19,13 +19,13 @@ async function ethSend(p: RpcProvider, from: string, to: string, data: string):
}
const SEL = {
PAYOUT_TOKEN: '0x935ca8e6', // PAYOUT_TOKEN()
ORACLE: '0x7dc0d1d0', // ORACLE()
FOUNDATION: '0x4d09f93e', // FOUNDATION()
configs: '0x1d12be96', // configs(address)
states: '0xb95da3ec', // states(address)
periodRemaining: '0x3aff0d34', // periodRemaining(address)
payoutLiquidity: '0x8b8a55be', // payoutLiquidity()
PAYOUT_TOKEN: '0xe2f6f511', // PAYOUT_TOKEN()
ORACLE: '0x38013f02', // ORACLE()
FOUNDATION: '0x65df2e51', // FOUNDATION()
configs: '0xfce89878', // configs(address)
states: '0x02a22337', // states(address)
periodRemaining: '0x0a41950f', // periodRemaining(address)
payoutLiquidity: '0x875d90e6', // payoutLiquidity()
redeem: '0x1e9a6950', // redeem(address,uint256)
} as const;

View File

@ -27,19 +27,19 @@ async function ethSend(p: RpcProvider, from: string, to: string, data: string, v
const FAC_SEL = {
// immutable views
BOND_TOKEN: '0x18b13f55', // BOND_TOKEN()
SINK: '0xf4f3b200', // SINK()
REGISTRATION_BOND: '0x4b094c4b', // REGISTRATION_BOND()
MIN_SINK_BPS: '0x9dabd040', // MIN_SINK_BPS()
DEFAULT_CHALLENGE_WINDOW: '0x8e8a6f3a', // DEFAULT_CHALLENGE_WINDOW()
FOUNDATION: '0x4d09f93e', // FOUNDATION()
BOND_TOKEN: '0xf21b52e5', // BOND_TOKEN()
SINK: '0xae9fc205', // SINK()
REGISTRATION_BOND: '0xc89d37de', // REGISTRATION_BOND()
MIN_SINK_BPS: '0x354ec275', // MIN_SINK_BPS()
DEFAULT_CHALLENGE_WINDOW: '0xda493740', // DEFAULT_CHALLENGE_WINDOW()
FOUNDATION: '0x65df2e51', // FOUNDATION()
// state views
rollups: '0xdcecb8e3', // rollups(bytes32)
rollupCount: '0xff19a3a4', // rollupCount()
rollups: '0x59a181dc', // rollups(bytes32)
rollupCount: '0xf4e92675', // rollupCount()
// writes
registerRollup: '0xbb4ab86c', // registerRollup(bytes32,address,address,address,uint16,uint16,uint16,uint256,uint64,string)
updateMetadata: '0xa1ae9b0e', // updateMetadata(bytes32,string)
exitRollup: '0x73db2bba', // exitRollup(bytes32,address,uint64,bytes)
registerRollup: '0x3878aaf1', // registerRollup(bytes32,address,address,address,uint16,uint16,uint16,uint256,uint64,uint64,string) (AereRaaSFactoryV2)
updateMetadata: '0xa375765e', // updateMetadata(bytes32,string)
exitRollup: '0x72fbb1bb', // exitRollup(bytes32,address,uint64,bytes)
} as const;
export class AereRaaSFactoryClient {
@ -73,7 +73,8 @@ export class AereRaaSFactoryClient {
rollupBps: number;
sequencerBps: number;
minChallengeBond: bigint;
challengeWindow: number;
challengeWindow: number; // seconds; 0 = the factory's DEFAULT_CHALLENGE_WINDOW
defenseGrace?: number; // seconds; 0 or omitted = the factory's DEFAULT_DEFENSE_GRACE (AereRaaSFactoryV2)
metadataUri: string;
}
): Promise<string> {
@ -88,7 +89,8 @@ export class AereRaaSFactoryClient {
encUint(BigInt(opts.sequencerBps)) +
encUint(opts.minChallengeBond) +
encUint(BigInt(opts.challengeWindow)) +
encUint(0x140n); // 10 head slots * 32 = 320 = 0x140
encUint(BigInt(opts.defenseGrace ?? 0)) +
encUint(0x160n); // 11 head slots * 32 = 352 = 0x160
const padded = Buffer.alloc(Math.ceil(meta.length / 32) * 32);
meta.copy(padded);
const dyn = encUint(BigInt(meta.length)) + padded.toString('hex');
@ -100,28 +102,28 @@ export class AereRaaSFactoryClient {
/* ============================ Settlement ============================= */
const SET_SEL = {
ROLLUP_ID: '0x7b0a4eb1', // ROLLUP_ID()
SINK: '0xf4f3b200', // SINK()
DEBT_TOKEN: '0xea5b5ddf', // DEBT_TOKEN()
CHALLENGE_WINDOW: '0x7a6a4abe', // CHALLENGE_WINDOW()
SINK_BPS: '0xb3f54bdd', // SINK_BPS()
ROLLUP_BPS: '0xc70aa727', // ROLLUP_BPS()
SEQUENCER_BPS: '0xe35d8b85', // SEQUENCER_BPS()
rollupOwner: '0x0e8d63c0', // rollupOwner()
sequencer: '0x42d1de27', // sequencer()
latestEpoch: '0x6f6db00f', // latestEpoch()
latestFinalisedEpoch:'0x3aac4670', // latestFinalisedEpoch()
isFinalised: '0x3a8a4a3e', // isFinalised(uint256)
totalRevenueSettled: '0xf3b6db95', // totalRevenueSettled()
ROLLUP_ID: '0xb5ed5559', // ROLLUP_ID()
SINK: '0xae9fc205', // SINK()
DEBT_TOKEN: '0x854db43c', // DEBT_TOKEN()
CHALLENGE_WINDOW: '0xd62aad29', // CHALLENGE_WINDOW()
SINK_BPS: '0x10dbc17d', // SINK_BPS()
ROLLUP_BPS: '0x7c103fdc', // ROLLUP_BPS()
SEQUENCER_BPS: '0x9d09bcd5', // SEQUENCER_BPS()
rollupOwner: '0x7c3a72a0', // rollupOwner()
sequencer: '0x5c1bba38', // sequencer()
latestEpoch: '0x9cb118bf', // latestEpoch()
latestFinalisedEpoch:'0x24b5e709', // latestFinalisedEpoch()
isFinalised: '0xc7a9b0ed', // isFinalised(uint256)
totalRevenueSettled: '0x335d152b', // totalRevenueSettled()
proposeStateRoot: '0x9e706b08', // proposeStateRoot(uint256,bytes32,bytes32)
advanceFinalisation: '0xb78f2ebd', // advanceFinalisation()
challenge: '0x8da548fa', // challenge(uint256,uint256,bytes32)
resolveChallenge: '0x437f2ba0', // resolveChallenge(uint256,bool)
resolveChallengeExpired: '0xb1cc5a3d', // resolveChallengeExpired(uint256)
settleRevenue: '0xc7c1aa8c', // settleRevenue(uint256)
setSequencer: '0xa6f9dae1', // setSequencer(address)
transferOwner: '0xc6dad082', // transferOwner(address)
proposeStateRoot: '0xe871f194', // proposeStateRoot(uint256,bytes32,bytes32)
advanceFinalisation: '0x51f09e3b', // advanceFinalisation()
challenge: '0xe0f7c5ae', // challenge(uint256,uint256,bytes32)
resolveChallenge: '0x94a754e0', // resolveChallenge(uint256,bool)
resolveChallengeExpired: '0x5454e09a', // resolveChallengeExpired(uint256)
settleRevenue: '0xe3c22c42', // settleRevenue(uint256)
setSequencer: '0x2547fa3e', // setSequencer(address)
transferOwner: '0x4fb2e45d', // transferOwner(address)
} as const;
export class AereRollupSettlementClient {

View File

@ -33,20 +33,20 @@ export interface AereSinkConfig {
// 4-byte function selectors. Computed once at module load and held constant.
// (Verified via cast sig-of <name>().)
const SEL = {
AERE: '0x1cc59c1b', // AERE()
BURN_VAULT: '0x4e7eee76', // BURN_VAULT()
SAERE_VAULT: '0xc46aedd7', // SAERE_VAULT()
DEX_ROUTER: '0x3b1664c1', // DEX_ROUTER()
BURN_BPS: '0x9748d6c0', // BURN_BPS()
BUYBACK_BPS: '0xb56fcecf', // BUYBACK_BPS()
STAKER_YIELD_BPS: '0xc77b3402', // STAKER_YIELD_BPS()
MAX_SLIPPAGE_BPS: '0xfb6fb1f9', // MAX_SLIPPAGE_BPS()
AERE: '0xe3b11fda', // AERE()
BURN_VAULT: '0x25bad93c', // BURN_VAULT()
SAERE_VAULT: '0x4f0a5e94', // SAERE_VAULT()
DEX_ROUTER: '0x7af11ac0', // DEX_ROUTER()
BURN_BPS: '0xa37a9fc0', // BURN_BPS()
BUYBACK_BPS: '0x1a17db67', // BUYBACK_BPS()
STAKER_YIELD_BPS: '0x7a03922d', // STAKER_YIELD_BPS()
MAX_SLIPPAGE_BPS: '0xe229cd76', // MAX_SLIPPAGE_BPS()
} as const;
// flush(address,uint256) — for write
const FLUSH_SELECTOR = '0x6cd5c39b';
const FLUSH_SELECTOR = '0x96fd1c42';
// sweepDust(address) — for write
const SWEEP_DUST_SELECTOR = '0xeb1a0a96';
const SWEEP_DUST_SELECTOR = '0xac7ad478';
/* ----------------------------- helper functions ---------------------------- */

View File

@ -0,0 +1,93 @@
/**
* selectors-live.ts — prove the repaired SDK clients against the REAL contracts on chain 2800 (D-512).
*
* On 2026-10-15 a site page showed "0 rollups" because the SDK's RaaS client called rollupCount() with a selector that
* no contract has; the call reverted and the caller printed a zero. The same day ~130 selectors across ten clients were
* found wrong, none of them even matching the signature written next to it. The offline gate
* (aerenew/scripts/deschise/sdk-selectori-corecti.cjs) now checks every selector against keccak and the compiled ABI;
* this file checks the other half: that the clients, as shipped, get an ANSWER from the deployed contracts.
*
* Every read below must return without a revert. Values are printed, not asserted, except where the chain fixes them
* (RaaS bond, sink floor, list of sanctions epochs). Negative control: the same RaaS read with the pre-repair selector
* must revert, otherwise this file could not tell a broken client from a working one.
*
* node dist/test/selectors-live.js [rpc] (network; not part of `npm test`)
* Read-only. No keys, no transactions.
*/
import { AereRaaSFactoryClient } from '../raas/index.js';
import { AereSinkClient } from '../sink/AereSinkClient.js';
import { LendingMarketClient, AereInsuranceFundClient } from '../lending/index.js';
import { AereSanctionsRegistryClient, ChainalysisOracleWrapperClient, AereTravelRuleHashRegistryClient, AereForensicEventRegistryClient } from '../compliance/index.js';
import { AereCompliancePoolClient } from '../compliance/NewComplianceClients.js';
const RPC = process.argv[2] ?? 'https://rpc.aere.network';
const provider = {
async request({ method, params }: { method: string; params: unknown[] }): Promise<unknown> {
const r = await fetch(RPC, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }) });
const j = (await r.json()) as { result?: unknown; error?: { message: string } };
if (j.error) throw new Error(j.error.message);
return j.result;
},
};
let failures = 0;
async function check(label: string, f: () => Promise<unknown>, expect?: (v: unknown) => boolean): Promise<void> {
try {
const v = await f();
const shown = typeof v === 'bigint' ? v.toString() : JSON.stringify(v, (_k, x) => (typeof x === 'bigint' ? x.toString() : x));
if (expect && !expect(v)) { failures++; console.log(` FAIL ${label}: unexpected ${shown}`); }
else console.log(` PASS ${label}: ${shown}`);
} catch (e) {
failures++;
console.log(` FAIL ${label}: ${(e as Error).message}`);
}
}
async function main(): Promise<void> {
console.log(`selector live proof against ${RPC}\n`);
const raas = new AereRaaSFactoryClient(provider, '0xB7F8c754AC3155197d76f01857172bBd5a5F39Ab');
await check('RaaS V2 rollupCount()', () => raas.rollupCount());
await check('RaaS V2 REGISTRATION_BOND() = 1000 AERE', () => raas.getRegistrationBond(), (v) => v === 1000n * 10n ** 18n);
await check('RaaS V2 MIN_SINK_BPS() = 1000', () => raas.getMinSinkBps(), (v) => v === 1000);
await check('RaaS V2 SINK()', () => raas.getSink(), (v) => typeof v === 'string' && /^0x[0-9a-f]{40}$/i.test(v));
// negative control: the pre-repair rollupCount selector must revert, or this file proves nothing
await provider.request({ method: 'eth_call', params: [{ to: '0xB7F8c754AC3155197d76f01857172bBd5a5F39Ab', data: '0xff19a3a4' }, 'latest'] }).then(
() => { failures++; console.log(' FAIL negative control: the old selector 0xff19a3a4 answered; this proof cannot tell broken from working'); },
() => console.log(' PASS negative control: the old selector 0xff19a3a4 reverts, as it did on the site'),
);
const sink = new AereSinkClient({ provider: provider as never, address: '0x69581B86A48161b067Ff4E01544780625B231676' });
await check('AereSink readConfig()', () => sink.readConfig());
const market = new LendingMarketClient({ provider: provider as never, address: '0x2C2d39dB711C0A33De04Dc74b1E22f4760FD4bb0' });
await check('AereLendingMarket getLtvBps()', () => market.getLtvBps());
await check('AereLendingMarket totalSuppliedCurrent()', () => market.totalSuppliedCurrent());
await check('AereLendingMarket borrowPaused()', () => market.borrowPaused());
const fund = new AereInsuranceFundClient(provider as never, '0x5Ab95C549c2A2b07913Df7edD4a16fd108B7CAAC');
await check('AereInsuranceFund FOUNDATION()', () => fund.getFoundation());
const sanctions = new AereSanctionsRegistryClient(provider, '0xb7d235718D99560F6EA4Fc5eAea2F8a306A3Cacf');
await check('AereSanctionsRegistry latestEpoch()', () => sanctions.latestEpoch(), (v) => typeof v === 'bigint' && v > 0n);
await check('AereSanctionsRegistry currentRoot()', () => sanctions.currentRoot(), (v) => typeof v === 'string' && /^0x[0-9a-f]{64}$/i.test(v));
const chainalysis = new ChainalysisOracleWrapperClient(provider, '0x1B7Be82C80f368f75Cb3807B1bc05E86A498f85c');
await check('ChainalysisOracleWrapper upstreamOracle()', () => chainalysis.upstreamOracle());
const travel = new AereTravelRuleHashRegistryClient(provider, '0xcF0E2e010E6e4506672019b1e570874AEeBC4c84');
await check('AereTravelRuleHashRegistry nextCommitmentId()', () => travel.nextCommitmentId());
const forensic = new AereForensicEventRegistryClient(provider, '0x4a7526A068e5DDE9788f6571E4A99095b14C6fff');
await check('AereForensicEventRegistry subjectAlertCount()', () => forensic.subjectAlertCount('0x0000000000000000000000000000000000000001'));
const pool = new AereCompliancePoolClient(provider as never, '0xB144c923572E5Ac1B6B961C4ccfec36917173465');
await check('AereCompliancePoolV2 depositCount()', () => pool.depositCount());
await check('AereCompliancePoolV2 nextLeafIndex()', () => pool.nextLeafIndex());
await check('AereCompliancePoolV2 getLastRoot()', () => pool.getLastRoot());
console.log(`\n${failures === 0 ? 'RESULT: PASS' : `RESULT: FAIL, ${failures} check(s)`}`);
process.exit(failures === 0 ? 0 : 1);
}
main().catch((e) => { console.error('live proof aborted:', e); process.exit(2); });

View File

@ -6,8 +6,8 @@
* still behaves the way the author measured. This file closes that gap by
* pointing the guard at production and asserting on what comes back.
*
* It is deliberately NOT part of `npm test`: it needs the network, and a test
* suite that fails when the internet is down teaches people to ignore red.
* It is deliberately NOT part of `npm test` (the test script skips *-live.js): it needs the network, and a
* test suite that fails when the internet is down teaches people to ignore red.
*
* node dist/test/state-window-live.js # rpc.aere.network
* node dist/test/state-window-live.js https://rpc2.aere.network
@ -18,7 +18,7 @@
* Structure, in this order, because a guard that has never been shown to fail
* cannot be trusted:
*
* STEP 1 PLANTED FAILURE. The raw call really does return a false zero.
* STEP 1 THE ENDPOINT. What the raw call returns today (a false zero until 2026-10, a refusal since).
* STEP 2 NEGATIVE CONTROL. With the guards disabled, the reader hands that
* false zero straight to the caller.
* STEP 3 THE GUARD. With the guards on, the reader refuses.
@ -74,35 +74,38 @@ async function main(): Promise<void> {
console.log('');
// -------------------------------------------------------------------------
console.log('STEP 1 PLANTED FAILURE: the raw call must still lie');
console.log('STEP 1 WHAT THE ENDPOINT DOES TODAY outside the state window');
// -------------------------------------------------------------------------
const rawNonce = await send('eth_getTransactionCount', [SIGNER, tag]);
if (rawNonce === '0x0') {
ok('eth_getTransactionCount returns a false zero',
`result "0x0" at block ${deepBlock}, where the block body proves ${trueNonce}`);
} else {
bad('eth_getTransactionCount returns a false zero',
`expected "0x0", got ${JSON.stringify(rawNonce)}. If this endpoint now answers honestly, ` +
`the published documentation must be updated, not this assertion.`);
}
const rawBalance = await send('eth_getBalance', [SIGNER, tag]);
if (rawBalance === null) {
ok('eth_getBalance is honest at the same block', 'result null, which is a refusal and not a value');
} else {
bad('eth_getBalance is honest at the same block', `expected null, got ${JSON.stringify(rawBalance)}`);
}
await send('eth_getProof', [SIGNER, [], tag]).then(
() => bad('eth_getProof is honest at the same block', 'it answered; the state window may have changed'),
(e: Error) => ok('eth_getProof is honest at the same block', `error ${e.message}`),
// Until 2026-10 this endpoint returned a false "0x0" here. Measured on 2026-10-15 it refuses with
// "-32000 World state unavailable", which is honest. Either answer is recorded; a non-zero number would be a new hazard.
let endpointLies = false;
await send('eth_getTransactionCount', [SIGNER, tag]).then(
(raw) => {
if (raw === '0x0') { endpointLies = true; ok('the endpoint still returns a false zero (hazard real)', `"0x0" at block ${deepBlock}, the body proves ${trueNonce}`); }
else bad('eth_getTransactionCount answers outside the window', `got ${JSON.stringify(raw)}; neither a refusal nor the known false zero`);
},
(e: Error) => ok('the endpoint refuses honestly outside the window', `error ${e.message}`),
);
console.log('');
// A lying endpoint, reproducing exactly the behaviour measured until 2026-10: state calls outside the window answer
// "0x0" (nonce) or null (balance) instead of an error. The guard must hold against THAT, whatever this endpoint does now.
const lying = async (method: string, params: unknown[]): Promise<unknown> => {
try { return await send(method, params); } catch (e) {
if (/World state unavailable/.test(String(e))) {
if (method === 'eth_getTransactionCount') return '0x0';
if (method === 'eth_getBalance' || method === 'eth_getCode' || method === 'eth_getStorageAt') return null;
}
throw e;
}
};
if (!endpointLies) console.log(' the steps below run against a wrapper that reproduces the false zero, since this endpoint no longer produces it');
if (!endpointLies) console.log('');
// -------------------------------------------------------------------------
console.log('STEP 2 NEGATIVE CONTROL: with the guards off, the lie gets through');
// -------------------------------------------------------------------------
const unguarded = new StateWindowReader(send, {
const unguarded = new StateWindowReader(endpointLies ? send : lying, {
windowBlocks: 100_000_000, safetyMarginBlocks: 0, corroborateNonce: false,
});
const leaked = await unguarded.getTransactionCount(SIGNER, deepBlock);
@ -116,7 +119,7 @@ async function main(): Promise<void> {
// -------------------------------------------------------------------------
console.log('STEP 3 THE GUARD: it refuses instead of guessing');
// -------------------------------------------------------------------------
const reader = new StateWindowReader(send);
const reader = new StateWindowReader(endpointLies ? send : lying);
await reader.getTransactionCount(SIGNER, deepBlock).then(
(v) => bad('pre-flight refuses the deep nonce', `expected a throw, got ${v}`),
@ -127,7 +130,7 @@ async function main(): Promise<void> {
);
// Corroboration is the guard that survives a caller who races past pre-flight.
const racedPast = new StateWindowReader(send, { windowBlocks: 100_000_000, safetyMarginBlocks: 0 });
const racedPast = new StateWindowReader(endpointLies ? send : lying, { windowBlocks: 100_000_000, safetyMarginBlocks: 0 });
await racedPast.getTransactionCount(SIGNER, deepBlock).then(
(v) => bad('corroboration catches what pre-flight missed', `expected a throw, got ${v}`),
(e: unknown) =>