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 account-abstraction and post-quantum contract surface, the seedless hybrid PQC
wallet, and AERE's native NIST post-quantum precompiles. wallet, and AERE's native NIST post-quantum precompiles.
**SDK version:** 0.16.12 **SDK version:** 0.16.13
**Last updated:** 2026-08-19 (consensus and network facts; the rest is as of 2026-07-16) **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 **Network state at this release:** 35+ typed contract clients over a canonical
address book of 130+ live contracts on chain 2800. 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 name | AERE Network |
| Chain ID | 2800 (`0xAF0`) | | Chain ID | 2800 (`0xAF0`) |
| Consensus | Hyperledger Besu QBFT, classical secp256k1 ECDSA | | 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 | 7, Byzantine-fault tolerance f=2, commit quorum 5 of 7 | | Validators | 10 since 2026-09-11, Byzantine-fault tolerance f=3, commit quorum 7 of 10 |
| Operator / client diversity | Single operator (Foundation), single client (Besu) today | | 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 (500 ms) target, sub-second deterministic finality | | 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) | | Native token | AERE (18 decimals) |
| Max supply | 2,800,000,000 AERE (capped at genesis-v2, 2026-05-07) | | 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) | | 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
AERE chain — https://rpc.aere.network AERE chain — https://rpc.aere.network
Chain ID 2800 (0xaf0) Chain ID 2800 (0xaf0)
Block height 9520927 Block height 22945693
Base fee 1.0 gwei Base fee 1.0 gwei (1000000000 wei)
Gas price 1.0 gwei Gas price 1.0 gwei (1000000000 wei)
Validators 7 Validators 10
Peers 7 - 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) ## State window on the public RPC (read before pinning a historical block)
Both public endpoints are **pruning full nodes**, not archive nodes. Both public endpoints are **pruning full nodes**, not archive nodes.
- Blocks, transactions, receipts, logs and `qbft_getValidatorsByBlockNumber` go back to **block 0**. - 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 - **World state is kept for only about the most recent 512 blocks**, roughly 5 minutes 17 seconds
at the measured 517 ms block interval. That bounds `eth_getBalance`, `eth_getCode`, 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`. `eth_getStorageAt`, `eth_call`, `eth_getProof` and `eth_getTransactionCount`.
Outside the window most methods say so: `eth_getBalance` / `eth_getCode` / `eth_getStorageAt` Outside the window both public endpoints now say so for every state method: measured on 2026-10-15,
return `null`, and `eth_getProof` returns `-32000 World state unavailable`. `eth_getTransactionCount`, `eth_getBalance` and `eth_getProof` all return `-32000 World state unavailable`.
**`eth_getTransactionCount` does not.** It returns `0x0`, which is well formed and Until October 2026 they did not: `eth_getBalance` / `eth_getCode` / `eth_getStorageAt` returned `null`, and
indistinguishable from an account that has never transacted. Reproduce it yourself: block **`eth_getTransactionCount` returned `0x0`**, well formed and indistinguishable from an account that has
8,236,382 contains a transaction from `0xbeb33d20dfbbd49ec7ac1f617667f1f02dfd6465` with nonce never transacted (block 8,236,382 holds a transaction from `0xbeb33d20dfbbd49ec7ac1f617667f1f02dfd6465` with
123,063, and the endpoint still answers `0x0` for that address at that block. 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. `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. 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 - **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.). IBANs / cards / SEPA come from a BaaS partner (Striga, Baanx, Kulipa, etc.).
- **Decentralization.** 7 QBFT validators give f=2 (the chain keeps producing - **Decentralization.** 10 QBFT validators give f=3 (the chain keeps producing
blocks while up to 2 validators are down), commit quorum 5 of 7. All 7 are blocks while up to 3 validators are down), commit quorum 7 of 10. All 10 are
operated by the Foundation on a single client (Besu), so this is operated by the Foundation; nine run the Besu-derived client and one, since
fault-tolerance, not yet operator or client diversity. Broader validator 2026-09-11, the Nethermind-derived client. That is fault tolerance and a second
operators and a second client are roadmap. code base, not yet operator diversity: independent validator operators are roadmap.
- **Foundation key, not a multisig.** `0x0243A4f47D44b40b65D33f20329dE20D00c6f3C3` - **Foundation key, not a multisig.** `0x0243A4f47D44b40b65D33f20329dE20D00c6f3C3`
is a single-key EOA (zero bytecode on-chain), described here as a is a single-key EOA (zero bytecode on-chain), described here as a
Foundation-controlled account. A real Safe-style threshold multisig is roadmap, Foundation-controlled account. A real Safe-style threshold multisig is roadmap,

View File

@ -1,6 +1,6 @@
{ {
"name": "@aere/sdk", "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.", "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", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
@ -24,7 +24,7 @@
], ],
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"test": "node --test dist/test/*.js", "test": "node scripts/run-tests.mjs",
"test:live": "node dist/test/state-window-live.js" "test:live": "node dist/test/state-window-live.js"
}, },
"keywords": [ "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). // 4-byte selectors (precomputed; never call eth_sign just to derive them).
const SEL = { const SEL = {
registerAgent: '0xa30b3acc', // registerAgent(address,bytes32,bytes32,uint128,uint128,uint128,string) registerAgent: '0x8d471e06', // registerAgent(address,bytes32,bytes32,uint128,uint128,uint128,string)
setSigner: '0x6c19e783', // setSigner(uint256,address) setSigner: '0x85267907', // setSigner(uint256,address)
setRateLimits: '0x44dec0e2', // setRateLimits(uint256,uint128,uint128,uint128) setRateLimits: '0x70c4e4df', // setRateLimits(uint256,uint128,uint128,uint128)
setPaused: '0x8456db15', // setPaused(uint256,bool) setPaused: '0xdaff97b5', // setPaused(uint256,bool)
topUp: '0xc35a8e8b', // topUp(uint256,address,uint256) topUp: '0x16645824', // topUp(uint256,address,uint256)
withdraw: '0x069a89ec', // withdraw(uint256,address,uint256,address) withdraw: '0x5f0f48bd', // withdraw(uint256,address,uint256,address)
signerOf: '0x9f9eb5b1', // signerOf(uint256) view returns (address) signerOf: '0x5161fdf5', // signerOf(uint256) view returns (address)
balances: '0xa7263000', // balances(uint256,address) view returns (uint256) balances: '0x1f320331', // balances(uint256,address) view returns (uint256)
} as const; } as const;
function pad32Hex(hex: string): string { 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 { export class AERE402SettlementClient {
constructor(public readonly provider: Eip1193Provider, public readonly facilitator: string) {} constructor(public readonly provider: Eip1193Provider, public readonly facilitator: string) {}

View File

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

View File

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

View File

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

View File

@ -35,27 +35,56 @@ function encodeDynamicBytes(hex: string): { offset: string; body: string } {
} }
/* ===================== AereSanctionsRegistry ====================== */ /* ===================== 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 = { const SANCT_SEL = {
currentRoot: '0x6f29ed46', // currentRoot() latestEpoch: '0x9cb118bf', // latestEpoch()
isSanctioned: '0x7ad8e02b', // isSanctioned(address,uint16,bytes32[]) snapshotStatus: '0x3d1146fa', // snapshotStatus(uint256)
commitRoot: '0x9b30b9ae', // commitRoot(bytes32,uint16,string) — placeholder isSanctioned: '0x7acc3cf1', // isSanctioned(uint256,address,uint16,bytes32[])
isSanctionedLatest: '0xf6035837', // isSanctionedLatest(address,uint16,bytes32[])
} as const; } 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 { export class AereSanctionsRegistryClient {
constructor(public readonly provider: RpcProvider, public readonly address: string) {} constructor(public readonly provider: RpcProvider, public readonly address: string) {}
async currentRoot(): Promise<string> { async latestEpoch(): Promise<bigint> {
return await ethCall(this.provider, this.address, SANCT_SEL.currentRoot); 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 =================== */ /* ===================== ChainalysisOracleWrapper =================== */
const CHAIN_SEL = { const CHAIN_SEL = {
isSanctioned: '0xdf592f7d', // isSanctioned(address) isSanctioned: '0xdf592f7d', // isSanctioned(address)
upstream: '0x6c0360eb', // upstream() upstreamOracle: '0xff132a15', // upstreamOracle()
locked: '0xcf30901', // locked() locked: '0xcf309012', // locked()
} as const; } as const;
export class ChainalysisOracleWrapperClient { export class ChainalysisOracleWrapperClient {
@ -70,32 +99,44 @@ export class ChainalysisOracleWrapperClient {
return true; // fails closed 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 ================= */ /* ===================== 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 = { const TRR_SEL = {
committedHashes: '0x6c5e6e95', // committedHashes(bytes32) commitments: '0x49ce8997', // commitments(uint256)
status: '0x200d2ed2', // status(bytes32) nextCommitmentId: '0x5ac48e6d', // nextCommitmentId()
} as const; } as const;
export class AereTravelRuleHashRegistryClient { export class AereTravelRuleHashRegistryClient {
constructor(public readonly provider: RpcProvider, public readonly address: string) {} constructor(public readonly provider: RpcProvider, public readonly address: string) {}
/** State enum: 0=Unknown, 1=Committed, 2=Acknowledged, 3=Disputed. */ async nextCommitmentId(): Promise<bigint> {
async status(hash: string): Promise<number> { return BigInt(await ethCall(this.provider, this.address, TRR_SEL.nextCommitmentId));
const res = await ethCall(this.provider, this.address, TRR_SEL.status + encBytes32(hash)); }
return Number(BigInt(res));
/** 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 ================== */ /* ===================== AereForensicEventRegistry ================== */
const FORENSIC_SEL = { const FORENSIC_SEL = {
subjectAlertCount: '0x7c1d3d0a', // subjectAlertCount(address) subjectAlertCount: '0x3fa664d6', // subjectAlertCount(address)
recentConfirmedAlertCount: '0x44e3a39d', // recentConfirmedAlertCount(address,uint8,uint256) recentConfirmedAlertCount: '0xd9f77c99', // recentConfirmedAlertCount(address,uint8,uint256)
nextBotId: '0xf4cae09e', // nextBotId() nextBotId: '0x6fc0c79d', // nextBotId()
nextAlertId: '0xa0d49d3a', // nextAlertId() nextAlertId: '0x260be2a2', // nextAlertId()
} as const; } as const;
export class AereForensicEventRegistryClient { export class AereForensicEventRegistryClient {
@ -116,9 +157,9 @@ export class AereForensicEventRegistryClient {
/* ===================== AereZKScreen =============================== */ /* ===================== AereZKScreen =============================== */
const ZK_SEL = { const ZK_SEL = {
isCleared: '0xb6a5d7de', // isCleared(address,bytes32,uint256) isCleared: '0x011f0451', // isCleared(address,bytes32,uint256)
clearedAt: '0xc5f4dec0', // clearedAt(address,bytes32) clearedAt: '0xedddc71f', // clearedAt(address,bytes32)
submitProof: '0x77a91da7', // submitProof(bytes32,bytes,bytes) submitProof: '0xeb90e2cb', // submitProof(bytes32,bytes,bytes)
} as const; } as const;
export class AereZKScreenClient { export class AereZKScreenClient {
@ -163,10 +204,10 @@ export class AereZKScreenClient {
* proof of inference see AereZKMLVerifier. */ * proof of inference see AereZKMLVerifier. */
const AI_SEL = { const AI_SEL = {
models: '0x88e98b22', // models(bytes32) models: '0xe236f46b', // models(bytes32)
attestationCount: '0x8b2c3a99', // attestationCount(bytes32) attestationCount: '0xd65ddd52', // attestationCount(bytes32)
registerModel: '0xa9b5a9eb', // registerModel(bytes32,address,string) registerModel: '0x31c0953b', // registerModel(bytes32,address,string)
anchor: '0x5b9babe0', // anchor(bytes32,bytes32,bytes32,bytes32,uint256,uint256,bytes) anchor: '0x10912ffc', // anchor(bytes32,bytes32,bytes32,bytes32,uint256,uint256,bytes)
} as const; } as const;
export class AereAIProofClient { export class AereAIProofClient {

View File

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

View File

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

View File

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

View File

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

View File

@ -33,20 +33,20 @@ export interface AereSinkConfig {
// 4-byte function selectors. Computed once at module load and held constant. // 4-byte function selectors. Computed once at module load and held constant.
// (Verified via cast sig-of <name>().) // (Verified via cast sig-of <name>().)
const SEL = { const SEL = {
AERE: '0x1cc59c1b', // AERE() AERE: '0xe3b11fda', // AERE()
BURN_VAULT: '0x4e7eee76', // BURN_VAULT() BURN_VAULT: '0x25bad93c', // BURN_VAULT()
SAERE_VAULT: '0xc46aedd7', // SAERE_VAULT() SAERE_VAULT: '0x4f0a5e94', // SAERE_VAULT()
DEX_ROUTER: '0x3b1664c1', // DEX_ROUTER() DEX_ROUTER: '0x7af11ac0', // DEX_ROUTER()
BURN_BPS: '0x9748d6c0', // BURN_BPS() BURN_BPS: '0xa37a9fc0', // BURN_BPS()
BUYBACK_BPS: '0xb56fcecf', // BUYBACK_BPS() BUYBACK_BPS: '0x1a17db67', // BUYBACK_BPS()
STAKER_YIELD_BPS: '0xc77b3402', // STAKER_YIELD_BPS() STAKER_YIELD_BPS: '0x7a03922d', // STAKER_YIELD_BPS()
MAX_SLIPPAGE_BPS: '0xfb6fb1f9', // MAX_SLIPPAGE_BPS() MAX_SLIPPAGE_BPS: '0xe229cd76', // MAX_SLIPPAGE_BPS()
} as const; } as const;
// flush(address,uint256) — for write // flush(address,uint256) — for write
const FLUSH_SELECTOR = '0x6cd5c39b'; const FLUSH_SELECTOR = '0x96fd1c42';
// sweepDust(address) — for write // sweepDust(address) — for write
const SWEEP_DUST_SELECTOR = '0xeb1a0a96'; const SWEEP_DUST_SELECTOR = '0xac7ad478';
/* ----------------------------- helper functions ---------------------------- */ /* ----------------------------- 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 * still behaves the way the author measured. This file closes that gap by
* pointing the guard at production and asserting on what comes back. * 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 * It is deliberately NOT part of `npm test` (the test script skips *-live.js): it needs the network, and a
* suite that fails when the internet is down teaches people to ignore red. * 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 # rpc.aere.network
* node dist/test/state-window-live.js https://rpc2.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 * Structure, in this order, because a guard that has never been shown to fail
* cannot be trusted: * 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 * STEP 2 NEGATIVE CONTROL. With the guards disabled, the reader hands that
* false zero straight to the caller. * false zero straight to the caller.
* STEP 3 THE GUARD. With the guards on, the reader refuses. * STEP 3 THE GUARD. With the guards on, the reader refuses.
@ -74,35 +74,38 @@ async function main(): Promise<void> {
console.log(''); 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]); // Until 2026-10 this endpoint returned a false "0x0" here. Measured on 2026-10-15 it refuses with
if (rawNonce === '0x0') { // "-32000 World state unavailable", which is honest. Either answer is recorded; a non-zero number would be a new hazard.
ok('eth_getTransactionCount returns a false zero', let endpointLies = false;
`result "0x0" at block ${deepBlock}, where the block body proves ${trueNonce}`); await send('eth_getTransactionCount', [SIGNER, tag]).then(
} else { (raw) => {
bad('eth_getTransactionCount returns a false zero', if (raw === '0x0') { endpointLies = true; ok('the endpoint still returns a false zero (hazard real)', `"0x0" at block ${deepBlock}, the body proves ${trueNonce}`); }
`expected "0x0", got ${JSON.stringify(rawNonce)}. If this endpoint now answers honestly, ` + else bad('eth_getTransactionCount answers outside the window', `got ${JSON.stringify(raw)}; neither a refusal nor the known false zero`);
`the published documentation must be updated, not this assertion.`); },
} (e: Error) => ok('the endpoint refuses honestly outside the window', `error ${e.message}`),
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}`),
); );
console.log(''); 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'); 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, windowBlocks: 100_000_000, safetyMarginBlocks: 0, corroborateNonce: false,
}); });
const leaked = await unguarded.getTransactionCount(SIGNER, deepBlock); 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'); 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( await reader.getTransactionCount(SIGNER, deepBlock).then(
(v) => bad('pre-flight refuses the deep nonce', `expected a throw, got ${v}`), (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. // 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( await racedPast.getTransactionCount(SIGNER, deepBlock).then(
(v) => bad('corroboration catches what pre-flight missed', `expected a throw, got ${v}`), (v) => bad('corroboration catches what pre-flight missed', `expected a throw, got ${v}`),
(e: unknown) => (e: unknown) =>