feat(sdk): SDK clients for the 2026-06-08 ship batch
Six new clients (dependency-free EIP-1193 wrappers, precomputed
4-byte selectors, ABI tuple/string encoders inline):
- AereAgentBondClient — bondOf, isBonded, postBond, requestWithdraw,
withdraw, slash; oracle-only on slash
- AereAIReputationClient — scoreOf, statsOf, attest (delta -1/0/+1)
- AereInferNetClient — modelEpochCount, registerModel, commitEpoch;
EU AI Act Article 12 provider surface
- AereAgentMemoryVaultClient — grant, revoke, write, canReadNow,
permissionOf; user-owned namespace
- AereAttestationGatewayClient — publishSchema, setAttestor, attest,
revoke, isValidNow, subjectCurrentlyAttested
- AereCompliancePoolClient — deposit, withdraw, publishAssociationRoot,
setComplianceProvider; Privacy Pools surface
All exported from the package barrel. Zero TS errors.
This commit is contained in:
parent
1917a4cd20
commit
99abfff98a
85
examples/agent-402-client.js
Normal file
85
examples/agent-402-client.js
Normal file
@ -0,0 +1,85 @@
|
||||
// agent-402-client.js
|
||||
//
|
||||
// Drop-in example: agent-side AERE402 client. Calls a paid endpoint,
|
||||
// receives 402, signs the quote via the agent's signing key, retries.
|
||||
//
|
||||
// Run:
|
||||
// yarn add ethers
|
||||
// AGENT_PRIVATE_KEY=0x... AGENT_ID=1234 ENDPOINT=http://localhost:3000/v1/inference node agent-402-client.js
|
||||
//
|
||||
// Prereq: the agent's signing key (AGENT_PRIVATE_KEY) must already be
|
||||
// registered on AereAgent with sufficient prepaid balance + un-exceeded
|
||||
// rate limits.
|
||||
|
||||
const { ethers } = require('ethers');
|
||||
|
||||
const AGENT_PK = process.env.AGENT_PRIVATE_KEY;
|
||||
const AGENT_ID = process.env.AGENT_ID;
|
||||
const ENDPOINT = process.env.ENDPOINT ?? 'http://localhost:3000/v1/inference';
|
||||
|
||||
if (!AGENT_PK || !AGENT_ID) {
|
||||
console.error('AGENT_PRIVATE_KEY + AGENT_ID env required');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const signer = new ethers.Wallet(AGENT_PK);
|
||||
|
||||
(async () => {
|
||||
console.log(`agent ${AGENT_ID} calling ${ENDPOINT}...`);
|
||||
|
||||
// 1. Unpaid request.
|
||||
const r1 = await fetch(ENDPOINT);
|
||||
if (r1.status !== 402) {
|
||||
console.log(`unexpected status ${r1.status} on first call`);
|
||||
console.log(await r1.text());
|
||||
return;
|
||||
}
|
||||
const quoteJson = r1.headers.get('x-aere402-quote');
|
||||
if (!quoteJson) throw new Error('server did not return X-AERE402-Quote');
|
||||
const quote = JSON.parse(quoteJson);
|
||||
console.log(' received quote:', quote);
|
||||
|
||||
// 2. Build EIP-712 domain + types.
|
||||
const domain = {
|
||||
name: 'AERE402Facilitator',
|
||||
version: '1',
|
||||
chainId: quote.chainId,
|
||||
verifyingContract: quote.facilitator,
|
||||
};
|
||||
const types = {
|
||||
PaymentAuth: [
|
||||
{ name: 'agentId', type: 'uint256' },
|
||||
{ name: 'token', type: 'address' },
|
||||
{ name: 'payee', type: 'address' },
|
||||
{ name: 'amount', type: 'uint256' },
|
||||
{ name: 'resourceId', type: 'bytes32' },
|
||||
{ name: 'nonce', type: 'uint256' },
|
||||
{ name: 'deadline', type: 'uint256' },
|
||||
],
|
||||
};
|
||||
const value = {
|
||||
agentId: BigInt(AGENT_ID),
|
||||
token: quote.token,
|
||||
payee: quote.payee,
|
||||
amount: BigInt(quote.amount),
|
||||
resourceId: quote.resourceId,
|
||||
nonce: BigInt(quote.nonce),
|
||||
deadline: BigInt(quote.deadline),
|
||||
};
|
||||
|
||||
// 3. Sign.
|
||||
const signature = await signer.signTypedData(domain, types, value);
|
||||
console.log(` signed: ${signature.slice(0, 18)}...`);
|
||||
|
||||
// 4. Retry with the signature.
|
||||
const r2 = await fetch(ENDPOINT, {
|
||||
headers: {
|
||||
'X-AERE402-Sig': signature,
|
||||
'X-AERE402-AgentId': AGENT_ID,
|
||||
'X-AERE402-Quote': quoteJson,
|
||||
},
|
||||
});
|
||||
console.log(` retry status: ${r2.status}`);
|
||||
console.log(` tx hash: ${r2.headers.get('x-aere402-txhash')}`);
|
||||
console.log(' body:', await r2.json());
|
||||
})();
|
||||
84
examples/express-402-paid-endpoint.js
Normal file
84
examples/express-402-paid-endpoint.js
Normal file
@ -0,0 +1,84 @@
|
||||
// express-402-paid-endpoint.js
|
||||
//
|
||||
// Drop-in example: a paid `/v1/inference` endpoint that charges $0.0125
|
||||
// in USDC.e per call via AERE402 settlement on Chain 2800.
|
||||
//
|
||||
// Run:
|
||||
// yarn add express @aere/sdk-js ethers
|
||||
// FACILITATOR=0x... PAYEE=0x... PROVIDER_PRIVATE_KEY=0x... node express-402-paid-endpoint.js
|
||||
//
|
||||
// Then the agent-side does:
|
||||
// 1. curl http://localhost:3000/v1/inference
|
||||
// → 402 with X-AERE402-Quote JSON header
|
||||
// 2. agent EIP-712-signs the quote using its registered signing key
|
||||
// 3. curl -H "X-AERE402-Sig: 0x.." -H "X-AERE402-AgentId: 1234" \
|
||||
// -H "X-AERE402-Quote: <original-quote-json>" \
|
||||
// http://localhost:3000/v1/inference
|
||||
// → 200 with the actual inference output, X-AERE402-TxHash header set
|
||||
|
||||
const express = require('express');
|
||||
const { ethers } = require('ethers');
|
||||
const { createAere402Middleware } = require('@aere/sdk-js');
|
||||
|
||||
const FACILITATOR = process.env.FACILITATOR;
|
||||
const USDC_E = process.env.USDC_E ?? '0x0000000000000000000000000000000000000000';
|
||||
const PAYEE = process.env.PAYEE;
|
||||
const PROVIDER_PK = process.env.PROVIDER_PRIVATE_KEY;
|
||||
const RPC_URL = process.env.RPC_URL ?? 'https://rpc.aere.network';
|
||||
const CHAIN_ID = parseInt(process.env.CHAIN_ID ?? '2800', 10);
|
||||
|
||||
if (!FACILITATOR || !PAYEE || !PROVIDER_PK) {
|
||||
console.error('FACILITATOR, PAYEE, and PROVIDER_PRIVATE_KEY env vars required');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const provider = new ethers.JsonRpcProvider(RPC_URL);
|
||||
const providerWallet = new ethers.Wallet(PROVIDER_PK, provider);
|
||||
|
||||
// Real `settle()` ABI from AERE402Facilitator. Pulled minimal — just what we call.
|
||||
const FAC_ABI = [
|
||||
'function settle(uint256 agentId, address token, address payee, uint256 amount, bytes32 resourceId, uint256 nonce, uint256 deadline, bytes signature) external',
|
||||
];
|
||||
const facContract = new ethers.Contract(FACILITATOR, FAC_ABI, providerWallet);
|
||||
|
||||
const charge = createAere402Middleware({
|
||||
facilitator: FACILITATOR,
|
||||
chainId: CHAIN_ID,
|
||||
token: USDC_E,
|
||||
payee: PAYEE,
|
||||
pricer: () => 12500n, // $0.0125 in USDC.e (6 decimals)
|
||||
submit: async (auth, signature) => {
|
||||
const tx = await facContract.settle(
|
||||
BigInt(auth.agentId),
|
||||
auth.token,
|
||||
auth.payee,
|
||||
BigInt(auth.amount),
|
||||
auth.resourceId,
|
||||
BigInt(auth.nonce),
|
||||
BigInt(auth.deadline),
|
||||
signature,
|
||||
);
|
||||
const rcpt = await tx.wait();
|
||||
return rcpt.hash;
|
||||
},
|
||||
});
|
||||
|
||||
const app = express();
|
||||
|
||||
app.get('/v1/inference', charge, (req, res) => {
|
||||
// The middleware paid us — we can read req.aere402 for receipt info.
|
||||
const { txHash, auth } = req.aere402;
|
||||
console.log(`paid call: agent=${auth.agentId} amount=${auth.amount} tx=${txHash}`);
|
||||
res.json({
|
||||
output: 'Pretend this is an LLM completion.',
|
||||
aere402: { txHash, amountCharged: auth.amount, token: auth.token },
|
||||
});
|
||||
});
|
||||
|
||||
const PORT = parseInt(process.env.PORT ?? '3000', 10);
|
||||
app.listen(PORT, () => {
|
||||
console.log(`paid endpoint running at http://localhost:${PORT}/v1/inference`);
|
||||
console.log(`facilitator: ${FACILITATOR}`);
|
||||
console.log(`payee: ${PAYEE}`);
|
||||
console.log(`provider wallet: ${providerWallet.address}`);
|
||||
});
|
||||
203
src/agentic/AERE402Middleware.ts
Normal file
203
src/agentic/AERE402Middleware.ts
Normal file
@ -0,0 +1,203 @@
|
||||
/**
|
||||
* AERE402Middleware — Express/Connect-compatible middleware for charging
|
||||
* AI agents via the AERE402 HTTP 402 protocol.
|
||||
*
|
||||
* USAGE:
|
||||
*
|
||||
* import express from 'express';
|
||||
* import { createAere402Middleware } from '@aere/sdk-js';
|
||||
*
|
||||
* const app = express();
|
||||
*
|
||||
* const charge = createAere402Middleware({
|
||||
* facilitator: '0xFacilitator...',
|
||||
* chainId: 2800,
|
||||
* token: '0xUSDC.e...',
|
||||
* payee: '0xMyProviderWallet...',
|
||||
* pricer: (req) => 12500n, // 12500 USDC.e ($0.0125) per call
|
||||
* submit: async (auth, sig) => {
|
||||
* // YOUR provider's wallet signs + submits facilitator.settle(...)
|
||||
* // Returns the txHash. See examples/express-402-paid-endpoint.js
|
||||
* return submitSettleViaMyWallet(auth, sig);
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* app.get('/v1/inference', charge, (req, res) => {
|
||||
* // By the time this handler runs, the agent has paid.
|
||||
* res.json({ output: runInference(req.body) });
|
||||
* });
|
||||
*
|
||||
* FLOW handled by this middleware:
|
||||
* 1. No X-AERE402-Sig header → respond 402 with quote (X-AERE402-Quote)
|
||||
* 2. X-AERE402-Sig present → parse auth + signature, verify EIP-712
|
||||
* signature recovers to a known signer, call user-supplied submit()
|
||||
* to land facilitator.settle on-chain, attach result to req, call next()
|
||||
* 3. On any verification failure → 401/402 with descriptive reason
|
||||
*
|
||||
* AGENT-SIDE (for reference): client must read the 402 response,
|
||||
* EIP-712-sign the auth via the agent's signing key, then retry with
|
||||
* X-AERE402-Sig set.
|
||||
*/
|
||||
|
||||
export interface PaymentAuth {
|
||||
agentId: string; // uint256 as decimal string
|
||||
token: string;
|
||||
payee: string;
|
||||
amount: string; // uint256 as decimal string
|
||||
resourceId: string; // bytes32 hex
|
||||
nonce: string; // uint256 as decimal string
|
||||
deadline: string; // uint256 as decimal string
|
||||
}
|
||||
|
||||
export interface Aere402MiddlewareConfig {
|
||||
/** AERE402Facilitator contract address. */
|
||||
facilitator: string;
|
||||
/** Chain ID (2800 for AERE mainnet). */
|
||||
chainId: number;
|
||||
/** Payment token (e.g. USDC.e). */
|
||||
token: string;
|
||||
/** Provider's receiving address. */
|
||||
payee: string;
|
||||
/** Function to compute the price per request. Receives the Express req. */
|
||||
pricer: (req: any) => bigint;
|
||||
/**
|
||||
* User-supplied function to submit the auth + signature to the facilitator.
|
||||
* Receives the auth + signature; should sign + send the tx via the
|
||||
* provider's wallet. Returns the on-chain tx hash.
|
||||
*/
|
||||
submit: (auth: PaymentAuth, signature: string) => Promise<string>;
|
||||
/**
|
||||
* Function to derive a resourceId (bytes32) for the request. Defaults to
|
||||
* keccak256-equivalent of `${req.method} ${req.path}`. Override if you
|
||||
* want finer-grained resource pricing.
|
||||
*/
|
||||
resourceIdFor?: (req: any) => string;
|
||||
/** Quote validity in seconds. Default 300 (5 min). */
|
||||
deadlineSeconds?: number;
|
||||
/** Nonce generator. Default: monotonic per-process counter starting at Date now / 1000. */
|
||||
nextNonce?: () => string;
|
||||
}
|
||||
|
||||
const DEFAULT_DEADLINE = 300;
|
||||
|
||||
let _nonceCounter = BigInt(Math.floor(Date.now() / 1000)) * 1000n;
|
||||
function defaultNextNonce(): string {
|
||||
_nonceCounter += 1n;
|
||||
return _nonceCounter.toString();
|
||||
}
|
||||
|
||||
function bytes32ResourceFromPath(method: string, path: string): string {
|
||||
// Tiny stable hash without pulling keccak. Hex of ASCII path padded/truncated to 64.
|
||||
// For production parity use keccak256(`${method} ${path}`), but this works as a
|
||||
// unique-per-resource identifier without dependencies.
|
||||
const raw = `${method} ${path}`;
|
||||
const buf = Buffer.from(raw, 'utf8').toString('hex').slice(0, 64).padEnd(64, '0');
|
||||
return '0x' + buf;
|
||||
}
|
||||
|
||||
export function createAere402Middleware(cfg: Aere402MiddlewareConfig) {
|
||||
const deadlineSec = cfg.deadlineSeconds ?? DEFAULT_DEADLINE;
|
||||
const nextNonce = cfg.nextNonce ?? defaultNextNonce;
|
||||
const resourceIdFor = cfg.resourceIdFor ?? ((req: any) => bytes32ResourceFromPath(req.method ?? 'GET', req.path ?? ''));
|
||||
|
||||
return async function aere402(req: any, res: any, next: (err?: any) => void) {
|
||||
try {
|
||||
const sigHeader = req.headers?.['x-aere402-sig'];
|
||||
const agentIdHeader = req.headers?.['x-aere402-agentid'];
|
||||
|
||||
if (!sigHeader || !agentIdHeader) {
|
||||
// Step 1: emit 402 with quote.
|
||||
const amount = cfg.pricer(req);
|
||||
const auth: PaymentAuth = {
|
||||
agentId: '0', // agent fills its own id when signing; quote leaves it blank
|
||||
token: cfg.token,
|
||||
payee: cfg.payee,
|
||||
amount: amount.toString(),
|
||||
resourceId: resourceIdFor(req),
|
||||
nonce: nextNonce(),
|
||||
deadline: (Math.floor(Date.now() / 1000) + deadlineSec).toString(),
|
||||
};
|
||||
res.statusCode = 402;
|
||||
res.setHeader('X-AERE402-Quote', JSON.stringify({
|
||||
facilitator: cfg.facilitator,
|
||||
chainId: cfg.chainId,
|
||||
...auth,
|
||||
}));
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({
|
||||
error: 'Payment Required',
|
||||
quote: { facilitator: cfg.facilitator, chainId: cfg.chainId, ...auth },
|
||||
message: 'Sign the quote via EIP-712 and retry with X-AERE402-Sig + X-AERE402-AgentId headers.',
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: parse + reconstruct the auth and submit.
|
||||
const quoteHeader = req.headers?.['x-aere402-quote'];
|
||||
if (!quoteHeader) {
|
||||
res.statusCode = 400;
|
||||
res.end('Missing X-AERE402-Quote header on retry.');
|
||||
return;
|
||||
}
|
||||
let quote: PaymentAuth;
|
||||
try { quote = JSON.parse(quoteHeader as string); }
|
||||
catch { res.statusCode = 400; res.end('Invalid X-AERE402-Quote JSON.'); return; }
|
||||
|
||||
// Override agentId from header (the signer's identity).
|
||||
const auth: PaymentAuth = { ...quote, agentId: String(agentIdHeader) };
|
||||
// Deadline guard server-side too.
|
||||
if (Number(auth.deadline) < Math.floor(Date.now() / 1000)) {
|
||||
res.statusCode = 408;
|
||||
res.end('Quote expired. Request a fresh 402.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 3: submit on-chain via user-supplied callback.
|
||||
let txHash: string;
|
||||
try {
|
||||
txHash = await cfg.submit(auth, String(sigHeader));
|
||||
} catch (e: any) {
|
||||
res.statusCode = 402;
|
||||
res.end(`Settlement failed: ${e?.message ?? 'unknown'}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Attach settlement info to the request for the downstream handler.
|
||||
(req as any).aere402 = { auth, signature: sigHeader, txHash };
|
||||
res.setHeader('X-AERE402-TxHash', txHash);
|
||||
next();
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Standalone helper: build the EIP-712 typed-data object the AGENT-SIDE
|
||||
* wallet must sign. Same shape as buildAuthTypedData in AereAgentClient.ts.
|
||||
* Re-exported here so providers can validate before charging.
|
||||
*/
|
||||
export function buildAere402TypedData(facilitator: string, chainId: number, auth: PaymentAuth): Record<string, unknown> {
|
||||
return {
|
||||
types: {
|
||||
EIP712Domain: [
|
||||
{ name: 'name', type: 'string' },
|
||||
{ name: 'version', type: 'string' },
|
||||
{ name: 'chainId', type: 'uint256' },
|
||||
{ name: 'verifyingContract', type: 'address' },
|
||||
],
|
||||
PaymentAuth: [
|
||||
{ name: 'agentId', type: 'uint256' },
|
||||
{ name: 'token', type: 'address' },
|
||||
{ name: 'payee', type: 'address' },
|
||||
{ name: 'amount', type: 'uint256' },
|
||||
{ name: 'resourceId', type: 'bytes32' },
|
||||
{ name: 'nonce', type: 'uint256' },
|
||||
{ name: 'deadline', type: 'uint256' },
|
||||
],
|
||||
},
|
||||
primaryType: 'PaymentAuth',
|
||||
domain: { name: 'AERE402Facilitator', version: '1', chainId, verifyingContract: facilitator },
|
||||
message: auth,
|
||||
};
|
||||
}
|
||||
205
src/agentic/AereAgentClient.ts
Normal file
205
src/agentic/AereAgentClient.ts
Normal file
@ -0,0 +1,205 @@
|
||||
// AereAgentClient — dependency-light EIP-1193 wrapper for the AereAgent
|
||||
// machine-account registry. Used by operators (humans / orgs) running an
|
||||
// AI agent on Chain 2800.
|
||||
|
||||
export interface Eip1193Provider {
|
||||
request(args: { method: string; params?: unknown[] }): Promise<any>;
|
||||
}
|
||||
|
||||
// 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)
|
||||
} as const;
|
||||
|
||||
function pad32Hex(hex: string): string {
|
||||
const h = hex.toLowerCase().replace(/^0x/, '');
|
||||
return h.padStart(64, '0');
|
||||
}
|
||||
|
||||
function encUint(n: bigint): string {
|
||||
return pad32Hex(n.toString(16));
|
||||
}
|
||||
|
||||
function encAddr(addr: string): string {
|
||||
return pad32Hex(addr.replace(/^0x/, ''));
|
||||
}
|
||||
|
||||
function encBytes32(b: string): string {
|
||||
return pad32Hex(b.replace(/^0x/, ''));
|
||||
}
|
||||
|
||||
export class AereAgentClient {
|
||||
constructor(
|
||||
public readonly provider: Eip1193Provider,
|
||||
public readonly agentRegistry: string,
|
||||
public readonly facilitator: string,
|
||||
) {}
|
||||
|
||||
/** Returns calldata for registerAgent(...). */
|
||||
encodeRegisterAgent(opts: {
|
||||
signer: string;
|
||||
passkeyPubX?: string;
|
||||
passkeyPubY?: string;
|
||||
perCallCap: bigint;
|
||||
perHourCap: bigint;
|
||||
perDayCap: bigint;
|
||||
metadataUri: string;
|
||||
}): string {
|
||||
const px = opts.passkeyPubX ?? '0x' + '0'.repeat(64);
|
||||
const py = opts.passkeyPubY ?? '0x' + '0'.repeat(64);
|
||||
const meta = Buffer.from(opts.metadataUri, 'utf8');
|
||||
// 7-arg encoding; metadataUri is dynamic, lives at offset 0x100 (= 7 * 32 - 32 from selector).
|
||||
// Layout: signer (32) | px (32) | py (32) | perCallCap (32) | perHourCap (32) | perDayCap (32)
|
||||
// | metadataUri offset (32) | metadataUri length (32) | metadataUri bytes (padded)
|
||||
const head =
|
||||
encAddr(opts.signer) +
|
||||
encBytes32(px) +
|
||||
encBytes32(py) +
|
||||
encUint(opts.perCallCap) +
|
||||
encUint(opts.perHourCap) +
|
||||
encUint(opts.perDayCap) +
|
||||
encUint(0xe0n); // offset to dynamic slot from start of args
|
||||
const padded = Buffer.alloc(Math.ceil(meta.length / 32) * 32);
|
||||
meta.copy(padded);
|
||||
const dyn = encUint(BigInt(meta.length)) + padded.toString('hex');
|
||||
return SEL.registerAgent + head + dyn;
|
||||
}
|
||||
|
||||
async sendRegisterAgent(from: string, opts: Parameters<AereAgentClient['encodeRegisterAgent']>[0]): Promise<string> {
|
||||
const data = this.encodeRegisterAgent(opts);
|
||||
return this.provider.request({
|
||||
method: 'eth_sendTransaction',
|
||||
params: [{ from, to: this.agentRegistry, data }],
|
||||
});
|
||||
}
|
||||
|
||||
async sendTopUp(from: string, agentId: bigint, token: string, amount: bigint): Promise<string> {
|
||||
const data = SEL.topUp + encUint(agentId) + encAddr(token) + encUint(amount);
|
||||
return this.provider.request({
|
||||
method: 'eth_sendTransaction',
|
||||
params: [{ from, to: this.agentRegistry, data }],
|
||||
});
|
||||
}
|
||||
|
||||
async sendSetPaused(from: string, agentId: bigint, paused: boolean): Promise<string> {
|
||||
const data = SEL.setPaused + encUint(agentId) + encUint(paused ? 1n : 0n);
|
||||
return this.provider.request({
|
||||
method: 'eth_sendTransaction',
|
||||
params: [{ from, to: this.agentRegistry, data }],
|
||||
});
|
||||
}
|
||||
|
||||
async getSigner(agentId: bigint): Promise<string> {
|
||||
const data = SEL.signerOf + encUint(agentId);
|
||||
const res: string = await this.provider.request({
|
||||
method: 'eth_call',
|
||||
params: [{ to: this.agentRegistry, data }, 'latest'],
|
||||
});
|
||||
return '0x' + res.slice(-40);
|
||||
}
|
||||
|
||||
async getBalance(agentId: bigint, token: string): Promise<bigint> {
|
||||
const data = SEL.balances + encUint(agentId) + encAddr(token);
|
||||
const res: string = await this.provider.request({
|
||||
method: 'eth_call',
|
||||
params: [{ to: this.agentRegistry, data }, 'latest'],
|
||||
});
|
||||
return BigInt(res);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- *
|
||||
* AERE402 payment authorisation helpers (EIP-712 typed data).
|
||||
*
|
||||
* The agent's signing wallet (registered via setSigner) signs this object
|
||||
* and ships the signature inside the HTTP 402 retry headers. The provider
|
||||
* POSTs (auth, signature) to AERE402Facilitator.settle().
|
||||
* ---------------------------------------------------------------------- */
|
||||
|
||||
export interface PaymentAuth {
|
||||
agentId: bigint;
|
||||
token: string;
|
||||
payee: string;
|
||||
amount: bigint;
|
||||
resourceId: string; // bytes32 hex; e.g. keccak256("GET /v1/inference")
|
||||
nonce: bigint;
|
||||
deadline: bigint; // unix seconds
|
||||
}
|
||||
|
||||
export function buildAuthTypedData(opts: {
|
||||
chainId: number;
|
||||
facilitator: string;
|
||||
auth: PaymentAuth;
|
||||
}): Record<string, unknown> {
|
||||
return {
|
||||
types: {
|
||||
EIP712Domain: [
|
||||
{ name: 'name', type: 'string' },
|
||||
{ name: 'version', type: 'string' },
|
||||
{ name: 'chainId', type: 'uint256' },
|
||||
{ name: 'verifyingContract', type: 'address' },
|
||||
],
|
||||
PaymentAuth: [
|
||||
{ name: 'agentId', type: 'uint256' },
|
||||
{ name: 'token', type: 'address' },
|
||||
{ name: 'payee', type: 'address' },
|
||||
{ name: 'amount', type: 'uint256' },
|
||||
{ name: 'resourceId', type: 'bytes32' },
|
||||
{ name: 'nonce', type: 'uint256' },
|
||||
{ name: 'deadline', type: 'uint256' },
|
||||
],
|
||||
},
|
||||
primaryType: 'PaymentAuth',
|
||||
domain: {
|
||||
name: 'AERE402Facilitator',
|
||||
version: '1',
|
||||
chainId: opts.chainId,
|
||||
verifyingContract: opts.facilitator,
|
||||
},
|
||||
message: {
|
||||
agentId: opts.auth.agentId.toString(),
|
||||
token: opts.auth.token,
|
||||
payee: opts.auth.payee,
|
||||
amount: opts.auth.amount.toString(),
|
||||
resourceId: opts.auth.resourceId,
|
||||
nonce: opts.auth.nonce.toString(),
|
||||
deadline: opts.auth.deadline.toString(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const SEL_SETTLE = '0x0c0a40c3'; // settle(uint256,address,address,uint256,bytes32,uint256,uint256,bytes)
|
||||
|
||||
export class AERE402SettlementClient {
|
||||
constructor(public readonly provider: Eip1193Provider, public readonly facilitator: string) {}
|
||||
|
||||
/** Encode and submit settle(...) from the provider's address. */
|
||||
async sendSettle(from: string, auth: PaymentAuth, signature: string): Promise<string> {
|
||||
const sig = signature.replace(/^0x/, '');
|
||||
const sigPadded = Buffer.alloc(Math.ceil(sig.length / 2 / 32) * 32);
|
||||
Buffer.from(sig, 'hex').copy(sigPadded);
|
||||
// 7 head slots + signature offset slot (8 head total) = 0x100
|
||||
const head =
|
||||
encUint(auth.agentId) +
|
||||
encAddr(auth.token) +
|
||||
encAddr(auth.payee) +
|
||||
encUint(auth.amount) +
|
||||
encBytes32(auth.resourceId) +
|
||||
encUint(auth.nonce) +
|
||||
encUint(auth.deadline) +
|
||||
encUint(0x100n); // offset to signature dynamic
|
||||
const dyn = encUint(BigInt(sig.length / 2)) + sigPadded.toString('hex');
|
||||
const data = SEL_SETTLE + head + dyn;
|
||||
return this.provider.request({
|
||||
method: 'eth_sendTransaction',
|
||||
params: [{ from, to: this.facilitator, data }],
|
||||
});
|
||||
}
|
||||
}
|
||||
227
src/agentic/NewAgenticClients.ts
Normal file
227
src/agentic/NewAgenticClients.ts
Normal file
@ -0,0 +1,227 @@
|
||||
/**
|
||||
* NewAgenticClients — SDK clients for the 2026-06-08 ship batch.
|
||||
* Covers: AereAgentBond, AereAIReputation, AereInferNet, AereAgentMemoryVault.
|
||||
*
|
||||
* Pattern matches sibling clients in @aere/sdk-js — dependency-free EIP-1193
|
||||
* wrapper, precomputed 4-byte selectors. Callers supply provider + signer.
|
||||
*/
|
||||
|
||||
export interface RpcProvider {
|
||||
request(args: { method: string; params: unknown[] }): Promise<unknown>;
|
||||
}
|
||||
|
||||
const pad32 = (h: string) => h.toLowerCase().replace(/^0x/, '').padStart(64, '0');
|
||||
const encU = (n: bigint) => pad32(n.toString(16));
|
||||
const encA = (a: string) => pad32(a.replace(/^0x/, ''));
|
||||
const encB = (b: string) => pad32(b.replace(/^0x/, ''));
|
||||
|
||||
async function call(p: RpcProvider, to: string, data: string): Promise<string> {
|
||||
return (await p.request({ method: 'eth_call', params: [{ to, data }, 'latest'] })) as string;
|
||||
}
|
||||
async function send(p: RpcProvider, from: string, to: string, data: string): Promise<string> {
|
||||
return (await p.request({ method: 'eth_sendTransaction', params: [{ from, to, data, value: '0x0' }] })) as string;
|
||||
}
|
||||
|
||||
function encString(s: string): { offset: bigint; body: string } {
|
||||
const b = Buffer.from(s, 'utf8');
|
||||
const padded = Buffer.alloc(Math.ceil(b.length / 32) * 32);
|
||||
b.copy(padded);
|
||||
return { offset: BigInt(b.length), body: encU(BigInt(b.length)) + padded.toString('hex') };
|
||||
}
|
||||
|
||||
/* ============================== AereAgentBond ============================== */
|
||||
|
||||
const BOND_SEL = {
|
||||
AERE: '0xe3b11fda',
|
||||
SINK: '0xae9fc205',
|
||||
WITHDRAWAL_COOLDOWN: '0xfeedab00',
|
||||
bondOf: '0xb416623b',
|
||||
isBonded: '0x52bd7947',
|
||||
isOracle: '0xa97e5c93',
|
||||
oracles: '0x2857373a',
|
||||
totalSlashedToSink: '0x8ca95389',
|
||||
postBond: '0x18b348af',
|
||||
requestWithdraw: '0x1fad8338',
|
||||
withdraw: '0x40e5e9aa',
|
||||
slash: '0x6d68fdd7',
|
||||
} as const;
|
||||
|
||||
export interface BondInfo {
|
||||
amount: bigint; requestedAt: bigint; lifetimeSlashed: bigint; exists: boolean;
|
||||
}
|
||||
|
||||
export class AereAgentBondClient {
|
||||
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
|
||||
|
||||
async bondOf(operator: string, agentId: string): Promise<BondInfo> {
|
||||
const res = await call(this.provider, this.address, BOND_SEL.bondOf + encA(operator) + encB(agentId));
|
||||
const r = res.replace(/^0x/, '');
|
||||
return {
|
||||
amount: BigInt('0x' + r.slice(0, 64)),
|
||||
requestedAt: BigInt('0x' + r.slice(64, 128)),
|
||||
lifetimeSlashed: BigInt('0x' + r.slice(128, 192)),
|
||||
exists: BigInt('0x' + r.slice(192, 256)) === 1n,
|
||||
};
|
||||
}
|
||||
|
||||
async isBonded(operator: string, agentId: string, minBond: bigint): Promise<boolean> {
|
||||
const data = BOND_SEL.isBonded + encA(operator) + encB(agentId) + encU(minBond);
|
||||
return BigInt(await call(this.provider, this.address, data)) === 1n;
|
||||
}
|
||||
|
||||
async totalSlashedToSink(): Promise<bigint> {
|
||||
return BigInt(await call(this.provider, this.address, BOND_SEL.totalSlashedToSink));
|
||||
}
|
||||
|
||||
postBond(from: string, agentId: string, amount: bigint): Promise<string> {
|
||||
return send(this.provider, from, this.address, BOND_SEL.postBond + encB(agentId) + encU(amount));
|
||||
}
|
||||
requestWithdraw(from: string, agentId: string): Promise<string> {
|
||||
return send(this.provider, from, this.address, BOND_SEL.requestWithdraw + encB(agentId));
|
||||
}
|
||||
withdraw(from: string, operator: string, agentId: string): Promise<string> {
|
||||
return send(this.provider, from, this.address, BOND_SEL.withdraw + encA(operator) + encB(agentId));
|
||||
}
|
||||
/** Oracle-only. reasonUri is a free-form ipfs:// or https:// link. */
|
||||
slash(from: string, operator: string, agentId: string, amount: bigint, reasonHash: string, reasonUri: string): Promise<string> {
|
||||
const head = encA(operator) + encB(agentId) + encU(amount) + encB(reasonHash) + encU(0xa0n);
|
||||
const s = encString(reasonUri);
|
||||
return send(this.provider, from, this.address, BOND_SEL.slash + head + s.body);
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================ AereAIReputation ============================ */
|
||||
|
||||
const REP_SEL = {
|
||||
BOND: '0xc1c1d218',
|
||||
MAX_SCORE: '0x27ff6223',
|
||||
SLASH_UNIT: '0xd50a3e76',
|
||||
statsOf: '0xcf212c5f',
|
||||
scoreOf: '0x7377fa36',
|
||||
attest: '0x37637f60',
|
||||
isAttestor: '0x2e2f4e24',
|
||||
attestors: '0xe7eb466f',
|
||||
} as const;
|
||||
|
||||
export interface AgentStats { positiveCount: bigint; disputeCount: bigint; lifetimeSlashed: bigint; }
|
||||
|
||||
export class AereAIReputationClient {
|
||||
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
|
||||
|
||||
async scoreOf(operator: string, agentId: string): Promise<bigint> {
|
||||
return BigInt(await call(this.provider, this.address, REP_SEL.scoreOf + encA(operator) + encB(agentId)));
|
||||
}
|
||||
async statsOf(operator: string, agentId: string): Promise<AgentStats> {
|
||||
const res = (await call(this.provider, this.address, REP_SEL.statsOf + encA(operator) + encB(agentId))).replace(/^0x/, '');
|
||||
return {
|
||||
positiveCount: BigInt('0x' + res.slice(0, 64)),
|
||||
disputeCount: BigInt('0x' + res.slice(64, 128)),
|
||||
lifetimeSlashed: BigInt('0x' + res.slice(128, 192)),
|
||||
};
|
||||
}
|
||||
|
||||
/** delta ∈ {-1, 0, 1}. Attestor-only. */
|
||||
attest(from: string, operator: string, agentId: string, delta: number, evidenceHash: string, evidenceUri: string): Promise<string> {
|
||||
// int8 is sign-extended to 32 bytes; negatives become 0xFF...FF
|
||||
const deltaWord = delta < 0 ? 'f'.repeat(64 - 2) + (256 + delta).toString(16).padStart(2, '0') : pad32(delta.toString(16));
|
||||
const head = encA(operator) + encB(agentId) + deltaWord + encB(evidenceHash) + encU(0xa0n);
|
||||
const s = encString(evidenceUri);
|
||||
return send(this.provider, from, this.address, REP_SEL.attest + head + s.body);
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================== AereInferNet ============================== */
|
||||
|
||||
const INFER_SEL = {
|
||||
modelEpochCount: '0xf407005b',
|
||||
modelOf: '0x1548a0e9',
|
||||
epochOf: '0xc1a5ae12',
|
||||
registerModel: '0x703722ac',
|
||||
commitEpoch: '0x7f11fb10',
|
||||
verifyInference: '0xa2a98a64',
|
||||
} as const;
|
||||
|
||||
export class AereInferNetClient {
|
||||
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
|
||||
|
||||
async modelEpochCount(provider_: string, modelId: string): Promise<bigint> {
|
||||
return BigInt(await call(this.provider, this.address, INFER_SEL.modelEpochCount + encA(provider_) + encB(modelId)));
|
||||
}
|
||||
|
||||
/** RiskTier: 0=Minimal, 1=Limited, 2=High, 3=Unacceptable. */
|
||||
registerModel(
|
||||
from: string, modelId: string, modelDigest: string, attestationDigest: string,
|
||||
risk: number, modelName: string, attestationUri: string
|
||||
): Promise<string> {
|
||||
// head = modelId(32) + modelDigest(32) + attestationDigest(32) + risk(32) + offset_name(32) + offset_attUri(32)
|
||||
// tail = nameLen+nameBody, attUriLen+attUriBody
|
||||
const name = encString(modelName);
|
||||
const att = encString(attestationUri);
|
||||
const offsetName = 6n * 32n;
|
||||
const nameBodyLen = name.body.length / 2; // including length word + padded
|
||||
const offsetAtt = offsetName + BigInt(nameBodyLen);
|
||||
const head =
|
||||
encB(modelId) + encB(modelDigest) + encB(attestationDigest) +
|
||||
pad32(risk.toString(16)) + encU(offsetName) + encU(offsetAtt);
|
||||
return send(this.provider, from, this.address, INFER_SEL.registerModel + head + name.body + att.body);
|
||||
}
|
||||
|
||||
commitEpoch(
|
||||
from: string, modelId: string, epochId: bigint, root: string,
|
||||
inferenceCount: bigint, metadataUri: string
|
||||
): Promise<string> {
|
||||
const m = encString(metadataUri);
|
||||
const head = encB(modelId) + encU(epochId) + encB(root) + encU(inferenceCount) + encU(0xa0n);
|
||||
return send(this.provider, from, this.address, INFER_SEL.commitEpoch + head + m.body);
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================== AereAgentMemoryVault ========================== */
|
||||
|
||||
const MEM_SEL = {
|
||||
permissionOf: '0xef928267',
|
||||
canReadNow: '0x80b4e9ba',
|
||||
headOf: '0xcc92a9ed',
|
||||
nextVersion: '0xba7badeb',
|
||||
grant: '0xbb9f078e',
|
||||
revoke: '0x88e62721',
|
||||
write: '0x2a7ae64c',
|
||||
} as const;
|
||||
|
||||
export interface Permission { canRead: boolean; canWrite: boolean; validUntil: bigint; exists: boolean; }
|
||||
export interface MemEntry { contentHash: string; blobUri: string; writer: string; version: bigint; writtenAt: bigint; }
|
||||
|
||||
export class AereAgentMemoryVaultClient {
|
||||
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
|
||||
|
||||
async canReadNow(user: string, agent: string, slot: string): Promise<boolean> {
|
||||
const data = MEM_SEL.canReadNow + encA(user) + encA(agent) + encB(slot);
|
||||
return BigInt(await call(this.provider, this.address, data)) === 1n;
|
||||
}
|
||||
async permissionOf(user: string, agent: string, slot: string): Promise<Permission> {
|
||||
const data = MEM_SEL.permissionOf + encA(user) + encA(agent) + encB(slot);
|
||||
const r = (await call(this.provider, this.address, data)).replace(/^0x/, '');
|
||||
return {
|
||||
canRead: BigInt('0x' + r.slice(0, 64)) === 1n,
|
||||
canWrite: BigInt('0x' + r.slice(64, 128)) === 1n,
|
||||
validUntil: BigInt('0x' + r.slice(128, 192)),
|
||||
exists: BigInt('0x' + r.slice(192, 256)) === 1n,
|
||||
};
|
||||
}
|
||||
|
||||
/** User grants per-slot permission. validUntil=0n means no expiry. */
|
||||
grant(from: string, agent: string, slot: string, canRead: boolean, canWrite: boolean, validUntil: bigint): Promise<string> {
|
||||
const data = MEM_SEL.grant + encA(agent) + encB(slot) +
|
||||
pad32(canRead ? '1' : '0') + pad32(canWrite ? '1' : '0') + encU(validUntil);
|
||||
return send(this.provider, from, this.address, data);
|
||||
}
|
||||
revoke(from: string, agent: string, slot: string): Promise<string> {
|
||||
return send(this.provider, from, this.address, MEM_SEL.revoke + encA(agent) + encB(slot));
|
||||
}
|
||||
/** Agent writes memory entry. contentHash = SHA256(encrypted blob). */
|
||||
write(from: string, user: string, slot: string, contentHash: string, blobUri: string): Promise<string> {
|
||||
const u = encString(blobUri);
|
||||
const head = encA(user) + encB(slot) + encB(contentHash) + encU(0x80n);
|
||||
return send(this.provider, from, this.address, MEM_SEL.write + head + u.body);
|
||||
}
|
||||
}
|
||||
18
src/agentic/index.ts
Normal file
18
src/agentic/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export { AereAgentClient, AERE402SettlementClient, buildAuthTypedData } from './AereAgentClient.js';
|
||||
export type { Eip1193Provider, PaymentAuth as AgentPaymentAuth } from './AereAgentClient.js';
|
||||
export { createAere402Middleware, buildAere402TypedData } from './AERE402Middleware.js';
|
||||
export type { Aere402MiddlewareConfig, PaymentAuth } from './AERE402Middleware.js';
|
||||
|
||||
// 2026-06-08 ship batch — accountability + AI Act + portable memory.
|
||||
export {
|
||||
AereAgentBondClient,
|
||||
AereAIReputationClient,
|
||||
AereInferNetClient,
|
||||
AereAgentMemoryVaultClient,
|
||||
} from './NewAgenticClients.js';
|
||||
export type {
|
||||
BondInfo,
|
||||
AgentStats,
|
||||
Permission as MemoryVaultPermission,
|
||||
MemEntry,
|
||||
} from './NewAgenticClients.js';
|
||||
113
src/channels/index.ts
Normal file
113
src/channels/index.ts
Normal file
@ -0,0 +1,113 @@
|
||||
/**
|
||||
* State Channels SDK — AereStateChannels.
|
||||
*
|
||||
* Contract: aerenew/contracts/contracts/channels/AereStateChannels.sol
|
||||
*
|
||||
* Off-chain transactions: both parties hold a current `ChannelState` and
|
||||
* exchange new ones (signed by both) on every payment. On dispute, EITHER
|
||||
* party submits the latest state to start a 24h challenge window; the other
|
||||
* can override with a higher-nonce state.
|
||||
*/
|
||||
|
||||
export interface RpcProvider {
|
||||
request(args: { method: string; params: unknown[] }): Promise<unknown>;
|
||||
}
|
||||
|
||||
export interface ChannelState {
|
||||
channelId: string; // bytes32
|
||||
nonce: bigint;
|
||||
balanceA: bigint;
|
||||
balanceB: bigint;
|
||||
htlcLockedFromA: bigint;
|
||||
htlcLockedFromB: bigint;
|
||||
lockHash: string; // bytes32 ('0x000...' if no HTLC active)
|
||||
lockTimeout: bigint; // unix seconds
|
||||
}
|
||||
|
||||
const pad32 = (h: string) => h.toLowerCase().replace(/^0x/, '').padStart(64, '0');
|
||||
const encUint = (n: bigint) => pad32(n.toString(16));
|
||||
const encAddr = (a: string) => pad32(a.replace(/^0x/, ''));
|
||||
const encBytes32 = (b: string) => pad32(b.replace(/^0x/, ''));
|
||||
|
||||
async function ethCall(p: RpcProvider, to: string, data: string): Promise<string> {
|
||||
return await p.request({ method: 'eth_call', params: [{ to, data }, 'latest'] }) as string;
|
||||
}
|
||||
async function ethSend(p: RpcProvider, from: string, to: string, data: string): Promise<string> {
|
||||
return await p.request({ method: 'eth_sendTransaction', params: [{ from, to, data, value: '0x0' }] }) as string;
|
||||
}
|
||||
|
||||
function encChannelState(s: ChannelState): string {
|
||||
return encBytes32(s.channelId) + encUint(s.nonce) + encUint(s.balanceA) + encUint(s.balanceB) +
|
||||
encUint(s.htlcLockedFromA) + encUint(s.htlcLockedFromB) +
|
||||
encBytes32(s.lockHash) + encUint(s.lockTimeout);
|
||||
}
|
||||
|
||||
function encDynBytes(hex: string): { offset: bigint; encoded: string } {
|
||||
const clean = hex.replace(/^0x/, '');
|
||||
const bytes = Buffer.from(clean, 'hex');
|
||||
const padded = Buffer.alloc(Math.ceil(bytes.length / 32) * 32);
|
||||
bytes.copy(padded);
|
||||
return { offset: 0n, encoded: encUint(BigInt(bytes.length)) + padded.toString('hex') };
|
||||
}
|
||||
|
||||
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)
|
||||
} as const;
|
||||
|
||||
export class AereStateChannelsClient {
|
||||
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
|
||||
|
||||
async open(from: string, partyA: string, partyB: string, token: string, depositA: bigint, depositB: bigint): Promise<string> {
|
||||
const data = SEL.open + encAddr(partyA) + encAddr(partyB) + encAddr(token) + encUint(depositA) + encUint(depositB);
|
||||
return ethSend(this.provider, from, this.address, data);
|
||||
}
|
||||
|
||||
async finalize(from: string, channelId: string): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SEL.finalize + encBytes32(channelId));
|
||||
}
|
||||
|
||||
async settleHtlc(from: string, channelId: string, preimage: string): Promise<string> {
|
||||
const dyn = encDynBytes(preimage);
|
||||
const data = SEL.settleHtlc + encBytes32(channelId) + encUint(64n) + dyn.encoded;
|
||||
return ethSend(this.provider, from, this.address, data);
|
||||
}
|
||||
|
||||
async withdraw(from: string, channelId: string): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SEL.withdraw + encBytes32(channelId));
|
||||
}
|
||||
|
||||
/** Helper: encode the EIP-712 typed-data object for off-chain signing. */
|
||||
buildStateTypedData(chainId: number, state: ChannelState): Record<string, unknown> {
|
||||
return {
|
||||
types: {
|
||||
EIP712Domain: [
|
||||
{ name: 'name', type: 'string' },
|
||||
{ name: 'version', type: 'string' },
|
||||
{ name: 'chainId', type: 'uint256' },
|
||||
{ name: 'verifyingContract', type: 'address' },
|
||||
],
|
||||
ChannelState: [
|
||||
{ name: 'channelId', type: 'bytes32' },
|
||||
{ name: 'nonce', type: 'uint256' },
|
||||
{ name: 'balanceA', type: 'uint256' },
|
||||
{ name: 'balanceB', type: 'uint256' },
|
||||
{ name: 'htlcLockedFromA', type: 'uint256' },
|
||||
{ name: 'htlcLockedFromB', type: 'uint256' },
|
||||
{ name: 'lockHash', type: 'bytes32' },
|
||||
{ name: 'lockTimeout', type: 'uint256' },
|
||||
],
|
||||
},
|
||||
primaryType: 'ChannelState',
|
||||
domain: { name: 'AereStateChannels', version: '1', chainId, verifyingContract: this.address },
|
||||
message: state,
|
||||
};
|
||||
}
|
||||
}
|
||||
188
src/compliance/NewComplianceClients.ts
Normal file
188
src/compliance/NewComplianceClients.ts
Normal file
@ -0,0 +1,188 @@
|
||||
/**
|
||||
* NewComplianceClients — SDK for the 2026-06-08 ship batch.
|
||||
* Covers: AereAttestationGateway, AereCompliancePool.
|
||||
*
|
||||
* Pattern matches sibling clients in @aere/sdk-js — dependency-free
|
||||
* EIP-1193 wrapper, precomputed selectors.
|
||||
*/
|
||||
|
||||
export interface RpcProvider {
|
||||
request(args: { method: string; params: unknown[] }): Promise<unknown>;
|
||||
}
|
||||
|
||||
const pad32 = (h: string) => h.toLowerCase().replace(/^0x/, '').padStart(64, '0');
|
||||
const encU = (n: bigint) => pad32(n.toString(16));
|
||||
const encA = (a: string) => pad32(a.replace(/^0x/, ''));
|
||||
const encB = (b: string) => pad32(b.replace(/^0x/, ''));
|
||||
|
||||
async function call(p: RpcProvider, to: string, data: string): Promise<string> {
|
||||
return (await p.request({ method: 'eth_call', params: [{ to, data }, 'latest'] })) as string;
|
||||
}
|
||||
async function send(p: RpcProvider, from: string, to: string, data: string): Promise<string> {
|
||||
return (await p.request({ method: 'eth_sendTransaction', params: [{ from, to, data, value: '0x0' }] })) as string;
|
||||
}
|
||||
function encString(s: string): { body: string; byteLen: number } {
|
||||
const b = Buffer.from(s, 'utf8');
|
||||
const padded = Buffer.alloc(Math.ceil(b.length / 32) * 32);
|
||||
b.copy(padded);
|
||||
return { body: encU(BigInt(b.length)) + padded.toString('hex'), byteLen: padded.length + 32 };
|
||||
}
|
||||
function encBytes(hex: string): { body: string; byteLen: number } {
|
||||
const clean = hex.replace(/^0x/, '');
|
||||
const len = clean.length / 2;
|
||||
const padded = Buffer.alloc(Math.ceil(len / 32) * 32);
|
||||
Buffer.from(clean, 'hex').copy(padded);
|
||||
return { body: encU(BigInt(len)) + padded.toString('hex'), byteLen: padded.length + 32 };
|
||||
}
|
||||
|
||||
/* ========================= AereAttestationGateway ========================= */
|
||||
|
||||
const ATT_SEL = {
|
||||
FOUNDATION: '0x65df2e51',
|
||||
MAX_INHERITANCE_DEPTH: '0xadf515af',
|
||||
isAuthorisedAttestor: '0x331735f0',
|
||||
latestFor: '0xfba8fcb9',
|
||||
isValidNow: '0xcf65b2b5',
|
||||
subjectCurrentlyAttested: '0x57e74a61',
|
||||
publishSchema: '0xce3f3aa3',
|
||||
setAttestor: '0xe65b98ba',
|
||||
attest: '0xc35b1106',
|
||||
revoke: '0xb75c7dc6',
|
||||
} as const;
|
||||
|
||||
export class AereAttestationGatewayClient {
|
||||
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
|
||||
|
||||
async isValidNow(attestationId: string): Promise<boolean> {
|
||||
const r = await call(this.provider, this.address, ATT_SEL.isValidNow + encB(attestationId));
|
||||
return BigInt(r) === 1n;
|
||||
}
|
||||
|
||||
async subjectCurrentlyAttested(subject: string, schemaId: string): Promise<boolean> {
|
||||
const data = ATT_SEL.subjectCurrentlyAttested + encB(subject) + encB(schemaId);
|
||||
return BigInt(await call(this.provider, this.address, data)) === 1n;
|
||||
}
|
||||
|
||||
async latestFor(subject: string, schemaId: string): Promise<string> {
|
||||
return await call(this.provider, this.address, ATT_SEL.latestFor + encB(subject) + encB(schemaId));
|
||||
}
|
||||
|
||||
async isAuthorisedAttestor(schemaId: string, attestor: string): Promise<boolean> {
|
||||
const data = ATT_SEL.isAuthorisedAttestor + encB(schemaId) + encA(attestor);
|
||||
return BigInt(await call(this.provider, this.address, data)) === 1n;
|
||||
}
|
||||
|
||||
/** Foundation-only. */
|
||||
publishSchema(from: string, schemaId: string, specHash: string, name: string, semver: string, specUri: string): Promise<string> {
|
||||
const n = encString(name), sv = encString(semver), su = encString(specUri);
|
||||
// head: schemaId(32) + specHash(32) + offset_n + offset_sv + offset_su = 5 slots
|
||||
const offN = 5n * 32n;
|
||||
const offSv = offN + BigInt(n.byteLen);
|
||||
const offSu = offSv + BigInt(sv.byteLen);
|
||||
const head = encB(schemaId) + encB(specHash) + encU(offN) + encU(offSv) + encU(offSu);
|
||||
return send(this.provider, from, this.address, ATT_SEL.publishSchema + head + n.body + sv.body + su.body);
|
||||
}
|
||||
|
||||
setAttestor(from: string, schemaId: string, attestor: string, authorised: boolean): Promise<string> {
|
||||
const data = ATT_SEL.setAttestor + encB(schemaId) + encA(attestor) + pad32(authorised ? '1' : '0');
|
||||
return send(this.provider, from, this.address, data);
|
||||
}
|
||||
|
||||
/** Authorised-attestor-only. parentId = 0x00...00 for no parent. */
|
||||
attest(
|
||||
from: string, schemaId: string, subject: string, parentId: string, payloadHash: string,
|
||||
validFrom: bigint, validUntil: bigint, payloadUri: string
|
||||
): Promise<string> {
|
||||
const p = encString(payloadUri);
|
||||
// 7 head slots + string tail
|
||||
const head = encB(schemaId) + encB(subject) + encB(parentId) + encB(payloadHash) +
|
||||
encU(validFrom) + encU(validUntil) + encU(7n * 32n);
|
||||
return send(this.provider, from, this.address, ATT_SEL.attest + head + p.body);
|
||||
}
|
||||
|
||||
revoke(from: string, attestationId: string): Promise<string> {
|
||||
return send(this.provider, from, this.address, ATT_SEL.revoke + encB(attestationId));
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================= AereCompliancePool ============================ */
|
||||
|
||||
const POOL_SEL = {
|
||||
TOKEN: '0x82bfefc8',
|
||||
DENOMINATION: '0x8b01429d',
|
||||
FOUNDATION: '0x65df2e51',
|
||||
VERIFIER: '0x08c84e70',
|
||||
POOL_ID: '0xe0d7d0e9',
|
||||
latestDepositRoot: '0x4e574c55',
|
||||
depositCount: '0x2dfdf0b5',
|
||||
isComplianceProvider: '0xff3d4ba1',
|
||||
complianceProviders: '0x05195996',
|
||||
isAssociationRootValid: '0x992d8d62',
|
||||
spentNullifiers: '0x1c70406a',
|
||||
associationRootPublishedAt: '0x68c46b72',
|
||||
deposit: '0xca0c3ad5',
|
||||
setComplianceProvider: '0xecc19f6c',
|
||||
publishDepositRoot: '0x0ce9659f',
|
||||
publishAssociationRoot: '0x9f92a5b1',
|
||||
withdraw: '0xb7433691',
|
||||
} as const;
|
||||
|
||||
export class AereCompliancePoolClient {
|
||||
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
|
||||
|
||||
async denomination(): Promise<bigint> {
|
||||
return BigInt(await call(this.provider, this.address, POOL_SEL.DENOMINATION));
|
||||
}
|
||||
async depositCount(): Promise<bigint> {
|
||||
return BigInt(await call(this.provider, this.address, POOL_SEL.depositCount));
|
||||
}
|
||||
async latestDepositRoot(): Promise<string> {
|
||||
return await call(this.provider, this.address, POOL_SEL.latestDepositRoot);
|
||||
}
|
||||
async isAssociationRootValid(root: string): Promise<boolean> {
|
||||
return BigInt(await call(this.provider, this.address, POOL_SEL.isAssociationRootValid + encB(root))) === 1n;
|
||||
}
|
||||
async isSpent(nullifierHash: string): Promise<boolean> {
|
||||
return BigInt(await call(this.provider, this.address, POOL_SEL.spentNullifiers + encB(nullifierHash))) === 1n;
|
||||
}
|
||||
|
||||
/** User deposit. Caller must have approved TOKEN to this address for DENOMINATION first. */
|
||||
deposit(from: string, commitment: string, travelRuleHash: string, complianceProvider: string, travelRuleUri: string): Promise<string> {
|
||||
const u = encString(travelRuleUri);
|
||||
const head = encB(commitment) + encB(travelRuleHash) + encA(complianceProvider) + encU(4n * 32n);
|
||||
return send(this.provider, from, this.address, POOL_SEL.deposit + head + u.body);
|
||||
}
|
||||
|
||||
publishAssociationRoot(from: string, root: string): Promise<string> {
|
||||
return send(this.provider, from, this.address, POOL_SEL.publishAssociationRoot + encB(root));
|
||||
}
|
||||
publishDepositRoot(from: string, root: string): Promise<string> {
|
||||
return send(this.provider, from, this.address, POOL_SEL.publishDepositRoot + encB(root));
|
||||
}
|
||||
setComplianceProvider(from: string, provider: string, allowed: boolean): Promise<string> {
|
||||
return send(this.provider, from, this.address, POOL_SEL.setComplianceProvider + encA(provider) + pad32(allowed ? '1' : '0'));
|
||||
}
|
||||
|
||||
/** Withdraw via zk proof. Relayer-friendly: feeRecipient/fee for relay-pay,
|
||||
* refund stays in pool for relayer reclaim. */
|
||||
withdraw(
|
||||
from: string,
|
||||
proof: string,
|
||||
depositRoot: string,
|
||||
associationRoot: string,
|
||||
nullifierHash: string,
|
||||
recipient: string,
|
||||
feeRecipient: string,
|
||||
fee: bigint,
|
||||
refund: bigint
|
||||
): Promise<string> {
|
||||
// 8 head slots: proof_offset, depositRoot, associationRoot, nullifierHash,
|
||||
// recipient, feeRecipient, fee, refund
|
||||
const offProof = 8n * 32n;
|
||||
const p = encBytes(proof);
|
||||
const head =
|
||||
encU(offProof) + encB(depositRoot) + encB(associationRoot) + encB(nullifierHash) +
|
||||
encA(recipient) + encA(feeRecipient) + encU(fee) + encU(refund);
|
||||
return send(this.provider, from, this.address, POOL_SEL.withdraw + head + p.body);
|
||||
}
|
||||
}
|
||||
185
src/compliance/index.ts
Normal file
185
src/compliance/index.ts
Normal file
@ -0,0 +1,185 @@
|
||||
/**
|
||||
* Compliance SDK clients — AereProof v0 stack + ZKScreen + AIProof.
|
||||
*
|
||||
* Contracts:
|
||||
* aerenew/contracts/contracts/compliance/AereSanctionsRegistry.sol
|
||||
* aerenew/contracts/contracts/compliance/ChainalysisOracleWrapper.sol
|
||||
* aerenew/contracts/contracts/compliance/AereTravelRuleHashRegistry.sol
|
||||
* aerenew/contracts/contracts/compliance/AereForensicEventRegistry.sol
|
||||
* aerenew/contracts/contracts/compliance/AereZKScreen.sol
|
||||
* aerenew/contracts/contracts/compliance/AereAIProof.sol
|
||||
*/
|
||||
|
||||
export interface RpcProvider {
|
||||
request(args: { method: string; params: unknown[] }): Promise<unknown>;
|
||||
}
|
||||
|
||||
const pad32 = (h: string) => h.toLowerCase().replace(/^0x/, '').padStart(64, '0');
|
||||
const encUint = (n: bigint) => pad32(n.toString(16));
|
||||
const encAddr = (a: string) => pad32(a.replace(/^0x/, ''));
|
||||
const encBytes32 = (b: string) => pad32(b.replace(/^0x/, ''));
|
||||
|
||||
async function ethCall(p: RpcProvider, to: string, data: string): Promise<string> {
|
||||
return await p.request({ method: 'eth_call', params: [{ to, data }, 'latest'] }) as string;
|
||||
}
|
||||
async function ethSend(p: RpcProvider, from: string, to: string, data: string): Promise<string> {
|
||||
return await p.request({ method: 'eth_sendTransaction', params: [{ from, to, data, value: '0x0' }] }) as string;
|
||||
}
|
||||
|
||||
function encodeDynamicBytes(hex: string): { offset: string; body: string } {
|
||||
const clean = hex.replace(/^0x/, '');
|
||||
const bytes = Buffer.from(clean, 'hex');
|
||||
const padded = Buffer.alloc(Math.ceil(bytes.length / 32) * 32);
|
||||
bytes.copy(padded);
|
||||
return { offset: encUint(BigInt(bytes.length)), body: padded.toString('hex') };
|
||||
}
|
||||
|
||||
/* ===================== AereSanctionsRegistry ====================== */
|
||||
|
||||
const SANCT_SEL = {
|
||||
currentRoot: '0x6f29ed46', // currentRoot()
|
||||
isSanctioned: '0x7ad8e02b', // isSanctioned(address,uint16,bytes32[])
|
||||
commitRoot: '0x9b30b9ae', // commitRoot(bytes32,uint16,string) — placeholder
|
||||
} as const;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===================== ChainalysisOracleWrapper =================== */
|
||||
|
||||
const CHAIN_SEL = {
|
||||
isSanctioned: '0xdf592f7d', // isSanctioned(address)
|
||||
upstream: '0x6c0360eb', // upstream()
|
||||
locked: '0xcf30901', // locked()
|
||||
} as const;
|
||||
|
||||
export class ChainalysisOracleWrapperClient {
|
||||
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
|
||||
|
||||
/** Returns true iff `addr` is sanctioned (fails closed — reverts also return true). */
|
||||
async isSanctioned(addr: string): Promise<boolean> {
|
||||
try {
|
||||
const res = await ethCall(this.provider, this.address, CHAIN_SEL.isSanctioned + encAddr(addr));
|
||||
return BigInt(res) === 1n;
|
||||
} catch {
|
||||
return true; // fails closed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ===================== AereTravelRuleHashRegistry ================= */
|
||||
|
||||
const TRR_SEL = {
|
||||
committedHashes: '0x6c5e6e95', // committedHashes(bytes32)
|
||||
status: '0x200d2ed2', // status(bytes32)
|
||||
} 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));
|
||||
}
|
||||
}
|
||||
|
||||
/* ===================== AereForensicEventRegistry ================== */
|
||||
|
||||
const FORENSIC_SEL = {
|
||||
subjectAlertCount: '0x7c1d3d0a', // subjectAlertCount(address)
|
||||
recentConfirmedAlertCount: '0x44e3a39d', // recentConfirmedAlertCount(address,uint8,uint256)
|
||||
nextBotId: '0xf4cae09e', // nextBotId()
|
||||
nextAlertId: '0xa0d49d3a', // nextAlertId()
|
||||
} as const;
|
||||
|
||||
export class AereForensicEventRegistryClient {
|
||||
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
|
||||
|
||||
async subjectAlertCount(subject: string): Promise<bigint> {
|
||||
return BigInt(await ethCall(this.provider, this.address, FORENSIC_SEL.subjectAlertCount + encAddr(subject)));
|
||||
}
|
||||
|
||||
/** Returns count of CONFIRMED alerts of `minSeverity` or higher within last `windowSeconds`. */
|
||||
async recentConfirmedAlertCount(subject: string, minSeverity: number, windowSeconds: bigint): Promise<bigint> {
|
||||
const data = FORENSIC_SEL.recentConfirmedAlertCount + encAddr(subject) +
|
||||
pad32(minSeverity.toString(16)) + encUint(windowSeconds);
|
||||
return BigInt(await ethCall(this.provider, this.address, data));
|
||||
}
|
||||
}
|
||||
|
||||
/* ===================== AereZKScreen =============================== */
|
||||
|
||||
const ZK_SEL = {
|
||||
isCleared: '0xb6a5d7de', // isCleared(address,bytes32,uint256)
|
||||
clearedAt: '0xc5f4dec0', // clearedAt(address,bytes32)
|
||||
submitProof: '0x77a91da7', // submitProof(bytes32,bytes,bytes)
|
||||
} as const;
|
||||
|
||||
export class AereZKScreenClient {
|
||||
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
|
||||
|
||||
async isCleared(user: string, programVKey: string, minTimestamp: bigint): Promise<boolean> {
|
||||
const data = ZK_SEL.isCleared + encAddr(user) + encBytes32(programVKey) + encUint(minTimestamp);
|
||||
const res = await ethCall(this.provider, this.address, data);
|
||||
return BigInt(res) === 1n;
|
||||
}
|
||||
|
||||
async clearedAt(user: string, programVKey: string): Promise<bigint> {
|
||||
return BigInt(await ethCall(this.provider, this.address, ZK_SEL.clearedAt + encAddr(user) + encBytes32(programVKey)));
|
||||
}
|
||||
|
||||
/** Submit an SP1 proof. Caller must equal the `user` encoded in publicValues. */
|
||||
async submitProof(from: string, programVKey: string, publicValues: string, proof: string): Promise<string> {
|
||||
const pv = encodeDynamicBytes(publicValues);
|
||||
const pr = encodeDynamicBytes(proof);
|
||||
// 3-arg encoding: vKey(32) + offset_pv(32) + offset_proof(32) + len_pv(32) + body_pv + len_proof(32) + body_proof
|
||||
const pvBodyLen = Math.ceil(publicValues.replace(/^0x/, '').length / 64) * 32; // bytes after length
|
||||
const offsetPv = 3n * 32n;
|
||||
const offsetProof = offsetPv + 32n + BigInt(pvBodyLen);
|
||||
const data = ZK_SEL.submitProof +
|
||||
encBytes32(programVKey) +
|
||||
encUint(offsetPv) +
|
||||
encUint(offsetProof) +
|
||||
pv.offset + pv.body +
|
||||
pr.offset + pr.body;
|
||||
return ethSend(this.provider, from, this.address, data);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===================== AereAIProof ================================ */
|
||||
|
||||
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)
|
||||
} as const;
|
||||
|
||||
export class AereAIProofClient {
|
||||
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
|
||||
|
||||
async attestationCount(modelId: string): Promise<bigint> {
|
||||
return BigInt(await ethCall(this.provider, this.address, AI_SEL.attestationCount + encBytes32(modelId)));
|
||||
}
|
||||
|
||||
async registerModel(from: string, modelId: string, signer: string, description: string): Promise<string> {
|
||||
const desc = Buffer.from(description, 'utf8');
|
||||
const padded = Buffer.alloc(Math.ceil(desc.length / 32) * 32);
|
||||
desc.copy(padded);
|
||||
const head = encBytes32(modelId) + encAddr(signer) + encUint(0x60n); // offset to string
|
||||
const dyn = encUint(BigInt(desc.length)) + padded.toString('hex');
|
||||
return ethSend(this.provider, from, this.address, AI_SEL.registerModel + head + dyn);
|
||||
}
|
||||
}
|
||||
|
||||
// 2026-06-08 ship batch — multi-regulator attestation + privacy pools.
|
||||
export {
|
||||
AereAttestationGatewayClient,
|
||||
AereCompliancePoolClient,
|
||||
} from './NewComplianceClients.js';
|
||||
18
src/index.ts
18
src/index.ts
@ -34,3 +34,21 @@ export type { ChannelState } from './channels/index.js';
|
||||
// MiCA Article 78 best-execution receipts — on-chain CASP commitments.
|
||||
export { BestExClient, OrderType, ClientClass } from './bestex/index.js';
|
||||
export type { BestExConfig, Receipt as BestExReceipt, CaspRegistration } from './bestex/index.js';
|
||||
|
||||
// 2026-06-08 ship batch — agentic accountability + EU AI Act + portable memory.
|
||||
export {
|
||||
AereAgentBondClient,
|
||||
AereAIReputationClient,
|
||||
AereInferNetClient,
|
||||
AereAgentMemoryVaultClient,
|
||||
} from './agentic/index.js';
|
||||
export type {
|
||||
BondInfo,
|
||||
AgentStats,
|
||||
MemoryVaultPermission,
|
||||
MemEntry,
|
||||
} from './agentic/index.js';
|
||||
export {
|
||||
AereAttestationGatewayClient,
|
||||
AereCompliancePoolClient,
|
||||
} from './compliance/index.js';
|
||||
|
||||
71
src/lending/InsuranceFundClient.ts
Normal file
71
src/lending/InsuranceFundClient.ts
Normal file
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* InsuranceFundClient — AereInsuranceFund helpers.
|
||||
* Permissionless donate; Foundation-only coverBadDebt.
|
||||
*
|
||||
* Contract: aerenew/contracts/contracts/lending/AereInsuranceFund.sol
|
||||
*/
|
||||
|
||||
export interface RpcProvider {
|
||||
request(args: { method: string; params: unknown[] }): Promise<unknown>;
|
||||
}
|
||||
|
||||
const pad32 = (h: string) => h.toLowerCase().replace(/^0x/, '').padStart(64, '0');
|
||||
const encUint = (n: bigint) => pad32(n.toString(16));
|
||||
const encAddr = (a: string) => pad32(a.replace(/^0x/, ''));
|
||||
|
||||
async function ethCall(p: RpcProvider, to: string, data: string): Promise<string> {
|
||||
return await p.request({ method: 'eth_call', params: [{ to, data }, 'latest'] }) as string;
|
||||
}
|
||||
async function ethSend(p: RpcProvider, from: string, to: string, data: string): Promise<string> {
|
||||
return await p.request({ method: 'eth_sendTransaction', params: [{ from, to, data, value: '0x0' }] }) as string;
|
||||
}
|
||||
|
||||
const SEL = {
|
||||
FOUNDATION: '0x4d09f93e', // FOUNDATION()
|
||||
COOLDOWN_SECONDS: '0xb47d5cf5', // COOLDOWN_SECONDS()
|
||||
markets: '0x2c43cab8', // 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)
|
||||
} as const;
|
||||
|
||||
export class AereInsuranceFundClient {
|
||||
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
|
||||
|
||||
async getFoundation(): Promise<string> {
|
||||
return '0x' + (await ethCall(this.provider, this.address, SEL.FOUNDATION)).slice(-40);
|
||||
}
|
||||
async getCooldownSeconds(): Promise<bigint> {
|
||||
return BigInt(await ethCall(this.provider, this.address, SEL.COOLDOWN_SECONDS));
|
||||
}
|
||||
async balanceOf(token: string): Promise<bigint> {
|
||||
return BigInt(await ethCall(this.provider, this.address, SEL.balances + encAddr(token)));
|
||||
}
|
||||
async coverageRemaining(market: string): Promise<bigint> {
|
||||
return BigInt(await ethCall(this.provider, this.address, SEL.coverageRemaining + encAddr(market)));
|
||||
}
|
||||
async cooldownReady(market: string): Promise<boolean> {
|
||||
const r = await ethCall(this.provider, this.address, SEL.cooldownReady + encAddr(market));
|
||||
return BigInt(r) === 1n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permissionless donate. Caller must approve `token` for `amount` on the fund first.
|
||||
*/
|
||||
async donate(from: string, token: string, amount: bigint): Promise<string> {
|
||||
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: pay down `amount` of `borrower`'s debt on `market`. */
|
||||
async coverBadDebt(from: string, market: string, borrower: string, amount: bigint): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SEL.coverBadDebt + encAddr(market) + encAddr(borrower) + encUint(amount));
|
||||
}
|
||||
}
|
||||
166
src/lending/LendingMarketClient.ts
Normal file
166
src/lending/LendingMarketClient.ts
Normal file
@ -0,0 +1,166 @@
|
||||
/**
|
||||
* LendingMarketClient — typed read/write helpers for AereLendingMarket
|
||||
* (Wave 1 isolated single-pair money markets).
|
||||
*
|
||||
* Contract: aerenew/contracts/contracts/lending/AereLendingMarket.sol
|
||||
* Audited 2026-06-07 (5 HIGH + 1 MED bugs fixed pre-deploy).
|
||||
*
|
||||
* Pattern: dependency-free EIP-1193 wrapper with precomputed 4-byte
|
||||
* selectors. Read methods just `eth_call`; write methods build calldata
|
||||
* + `eth_sendTransaction`. Caller handles signer + gas estimation.
|
||||
*/
|
||||
|
||||
export interface RpcProvider {
|
||||
request(args: { method: string; params: unknown[] }): Promise<unknown>;
|
||||
}
|
||||
|
||||
export interface LendingMarketConfig {
|
||||
address: `0x${string}`;
|
||||
provider: RpcProvider;
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
// state views
|
||||
collateralOf: '0x8aef7ddf', // collateralOf(address)
|
||||
debtOf: '0xcc4d2f49', // debtOf(address)
|
||||
supplierScaled: '0x9d7d6f0d', // supplierScaled(address)
|
||||
supplierBalance: '0x9d9f3ff6', // supplierBalance(address)
|
||||
debtBalance: '0x33ed81e4', // 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)
|
||||
|
||||
// write
|
||||
accrueInterest: '0x59601e8a', // accrueInterest()
|
||||
flushSinkFee: '0x71a5ba73', // flushSinkFee()
|
||||
supply: '0xf2b9fdb8', // supply(uint256)
|
||||
redeem: '0xdb006a75', // redeem(uint256)
|
||||
depositCollateral: '0x80aebec5', // depositCollateral(uint256)
|
||||
withdrawCollateral: '0xeb0aaa4b', // withdrawCollateral(uint256)
|
||||
borrow: '0xc5ebeaec', // borrow(uint256)
|
||||
repay: '0x0e752702', // repay(uint256)
|
||||
repayOnBehalfOf: '0x8f1a3bf0', // repayOnBehalfOf(address,uint256)
|
||||
liquidate: '0xf5e3c462', // liquidate(address,uint256)
|
||||
} as const;
|
||||
|
||||
/* ----------------------------- helpers ---------------------------------- */
|
||||
|
||||
function pad32(hex: string): string {
|
||||
const h = hex.toLowerCase().replace(/^0x/, '');
|
||||
return h.padStart(64, '0');
|
||||
}
|
||||
function encUint(n: bigint): string { return pad32(n.toString(16)); }
|
||||
function encAddr(addr: string): string { return pad32(addr.replace(/^0x/, '')); }
|
||||
|
||||
async function ethCall(p: RpcProvider, to: string, data: string): Promise<string> {
|
||||
return await p.request({ method: 'eth_call', params: [{ to, data }, 'latest'] }) as string;
|
||||
}
|
||||
async function ethSend(p: RpcProvider, from: string, to: string, data: string, value = '0x0'): Promise<string> {
|
||||
return await p.request({ method: 'eth_sendTransaction', params: [{ from, to, data, value }] }) as string;
|
||||
}
|
||||
|
||||
/* --------------------------- client ------------------------------------- */
|
||||
|
||||
export class LendingMarketClient {
|
||||
readonly address: `0x${string}`;
|
||||
readonly provider: RpcProvider;
|
||||
|
||||
constructor(cfg: LendingMarketConfig) {
|
||||
this.address = cfg.address;
|
||||
this.provider = cfg.provider;
|
||||
}
|
||||
|
||||
/* ---- immutable risk params (cached one-shot reads) ------------------- */
|
||||
|
||||
async getCollateral(): Promise<string> { return '0x' + (await ethCall(this.provider, this.address, SEL.COLLATERAL)).slice(-40); }
|
||||
async getDebtToken(): Promise<string> { return '0x' + (await ethCall(this.provider, this.address, SEL.DEBT_TOKEN)).slice(-40); }
|
||||
async getOracle(): Promise<string> { return '0x' + (await ethCall(this.provider, this.address, SEL.ORACLE)).slice(-40); }
|
||||
async getSink(): Promise<string> { return '0x' + (await ethCall(this.provider, this.address, SEL.SINK)).slice(-40); }
|
||||
async getLtvBps(): Promise<number> { return Number(BigInt(await ethCall(this.provider, this.address, SEL.LTV_BPS))); }
|
||||
async getLiqThresholdBps(): Promise<number> { return Number(BigInt(await ethCall(this.provider, this.address, SEL.LIQ_THRESHOLD_BPS))); }
|
||||
async getLiqBonusBps(): Promise<number> { return Number(BigInt(await ethCall(this.provider, this.address, SEL.LIQ_BONUS_BPS))); }
|
||||
async getBorrowFeeBps(): Promise<number> { return Number(BigInt(await ethCall(this.provider, this.address, SEL.BORROW_FEE_BPS))); }
|
||||
async getMarketDebtCap(): Promise<bigint> { return BigInt(await ethCall(this.provider, this.address, SEL.MARKET_DEBT_CAP)); }
|
||||
|
||||
/* ---- live state ------------------------------------------------------- */
|
||||
|
||||
async totalBorrowsCurrent(): Promise<bigint> { return BigInt(await ethCall(this.provider, this.address, SEL.totalBorrowsCurrent)); }
|
||||
async totalSuppliedCurrent(): Promise<bigint> { return BigInt(await ethCall(this.provider, this.address, SEL.totalSuppliedCurrent)); }
|
||||
async pendingSinkFee(): Promise<bigint> { return BigInt(await ethCall(this.provider, this.address, SEL.pendingSinkFee)); }
|
||||
async borrowRatePerSecond(): Promise<bigint> { return BigInt(await ethCall(this.provider, this.address, SEL.borrowRatePerSecond)); }
|
||||
async borrowPaused(): Promise<boolean> { return BigInt(await ethCall(this.provider, this.address, SEL.borrowPaused)) === 1n; }
|
||||
async depositPaused(): Promise<boolean> { return BigInt(await ethCall(this.provider, this.address, SEL.depositPaused)) === 1n; }
|
||||
|
||||
/* ---- per-user views --------------------------------------------------- */
|
||||
|
||||
async collateralOf(user: string): Promise<bigint> { return BigInt(await ethCall(this.provider, this.address, SEL.collateralOf + encAddr(user))); }
|
||||
async debtBalance(user: string): Promise<bigint> { return BigInt(await ethCall(this.provider, this.address, SEL.debtBalance + encAddr(user))); }
|
||||
async supplierBalance(user: string): Promise<bigint> { return BigInt(await ethCall(this.provider, this.address, SEL.supplierBalance + encAddr(user))); }
|
||||
async healthFactor(user: string): Promise<bigint> { return BigInt(await ethCall(this.provider, this.address, SEL.healthFactor + encAddr(user))); }
|
||||
|
||||
/* ---- write methods ---------------------------------------------------- */
|
||||
|
||||
async accrueInterest(from: string): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SEL.accrueInterest);
|
||||
}
|
||||
async flushSinkFee(from: string): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SEL.flushSinkFee);
|
||||
}
|
||||
async supply(from: string, amount: bigint): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SEL.supply + encUint(amount));
|
||||
}
|
||||
async redeem(from: string, amount: bigint): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SEL.redeem + encUint(amount));
|
||||
}
|
||||
async depositCollateral(from: string, amount: bigint): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SEL.depositCollateral + encUint(amount));
|
||||
}
|
||||
async withdrawCollateral(from: string, amount: bigint): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SEL.withdrawCollateral + encUint(amount));
|
||||
}
|
||||
async borrow(from: string, amount: bigint): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SEL.borrow + encUint(amount));
|
||||
}
|
||||
async repay(from: string, amount: bigint): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SEL.repay + encUint(amount));
|
||||
}
|
||||
async repayOnBehalfOf(from: string, account: string, amount: bigint): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SEL.repayOnBehalfOf + encAddr(account) + encUint(amount));
|
||||
}
|
||||
async liquidate(from: string, borrower: string, repayAmount: bigint): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SEL.liquidate + encAddr(borrower) + encUint(repayAmount));
|
||||
}
|
||||
|
||||
/* ---- helpers ---------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Returns the APY as a decimal (0.05 = 5%) computed from `borrowRatePerSecond`.
|
||||
* Linear approximation; accurate at low rates.
|
||||
*/
|
||||
async borrowApy(): Promise<number> {
|
||||
const rate = await this.borrowRatePerSecond();
|
||||
const r = Number(rate) / 1e27;
|
||||
return r * 365 * 86400;
|
||||
}
|
||||
}
|
||||
52
src/lending/RWATransferAdapterClient.ts
Normal file
52
src/lending/RWATransferAdapterClient.ts
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* RWATransferAdapterClient — permissioned RWA → USDC.e redemption.
|
||||
* Contract: aerenew/contracts/contracts/lending/RWATransferAdapter.sol
|
||||
*/
|
||||
|
||||
export interface RpcProvider {
|
||||
request(args: { method: string; params: unknown[] }): Promise<unknown>;
|
||||
}
|
||||
|
||||
const pad32 = (h: string) => h.toLowerCase().replace(/^0x/, '').padStart(64, '0');
|
||||
const encUint = (n: bigint) => pad32(n.toString(16));
|
||||
const encAddr = (a: string) => pad32(a.replace(/^0x/, ''));
|
||||
|
||||
async function ethCall(p: RpcProvider, to: string, data: string): Promise<string> {
|
||||
return await p.request({ method: 'eth_call', params: [{ to, data }, 'latest'] }) as string;
|
||||
}
|
||||
async function ethSend(p: RpcProvider, from: string, to: string, data: string): Promise<string> {
|
||||
return await p.request({ method: 'eth_sendTransaction', params: [{ from, to, data, value: '0x0' }] }) as 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()
|
||||
redeem: '0x1e9a6950', // redeem(address,uint256)
|
||||
} as const;
|
||||
|
||||
export class RWATransferAdapterClient {
|
||||
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
|
||||
|
||||
async getPayoutToken(): Promise<string> {
|
||||
return '0x' + (await ethCall(this.provider, this.address, SEL.PAYOUT_TOKEN)).slice(-40);
|
||||
}
|
||||
async getFoundation(): Promise<string> {
|
||||
return '0x' + (await ethCall(this.provider, this.address, SEL.FOUNDATION)).slice(-40);
|
||||
}
|
||||
async periodRemaining(asset: string): Promise<bigint> {
|
||||
return BigInt(await ethCall(this.provider, this.address, SEL.periodRemaining + encAddr(asset)));
|
||||
}
|
||||
async payoutLiquidity(): Promise<bigint> {
|
||||
return BigInt(await ethCall(this.provider, this.address, SEL.payoutLiquidity));
|
||||
}
|
||||
|
||||
/** Permissionless redeem. Caller must approve `asset` for `rwaAmount` on this contract first. */
|
||||
async redeem(from: string, asset: string, rwaAmount: bigint): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SEL.redeem + encAddr(asset) + encUint(rwaAmount));
|
||||
}
|
||||
}
|
||||
4
src/lending/index.ts
Normal file
4
src/lending/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export { LendingMarketClient } from './LendingMarketClient.js';
|
||||
export type { LendingMarketConfig } from './LendingMarketClient.js';
|
||||
export { AereInsuranceFundClient } from './InsuranceFundClient.js';
|
||||
export { RWATransferAdapterClient } from './RWATransferAdapterClient.js';
|
||||
179
src/raas/RaaSClient.ts
Normal file
179
src/raas/RaaSClient.ts
Normal file
@ -0,0 +1,179 @@
|
||||
/**
|
||||
* RaaSClient — AereRaaSFactory + AereRollupSettlement helpers.
|
||||
*
|
||||
* Contracts:
|
||||
* aerenew/contracts/contracts/raas/AereRaaSFactory.sol
|
||||
* aerenew/contracts/contracts/raas/AereRollupSettlement.sol
|
||||
*/
|
||||
|
||||
export interface RpcProvider {
|
||||
request(args: { method: string; params: unknown[] }): Promise<unknown>;
|
||||
}
|
||||
|
||||
const pad32 = (h: string) => h.toLowerCase().replace(/^0x/, '').padStart(64, '0');
|
||||
const encUint = (n: bigint) => pad32(n.toString(16));
|
||||
const encAddr = (a: string) => pad32(a.replace(/^0x/, ''));
|
||||
const encBytes32 = (b: string) => pad32(b.replace(/^0x/, ''));
|
||||
const encBool = (b: boolean) => pad32(b ? '1' : '0');
|
||||
|
||||
async function ethCall(p: RpcProvider, to: string, data: string): Promise<string> {
|
||||
return await p.request({ method: 'eth_call', params: [{ to, data }, 'latest'] }) as string;
|
||||
}
|
||||
async function ethSend(p: RpcProvider, from: string, to: string, data: string, value = '0x0'): Promise<string> {
|
||||
return await p.request({ method: 'eth_sendTransaction', params: [{ from, to, data, value }] }) as string;
|
||||
}
|
||||
|
||||
/* ============================== Factory =============================== */
|
||||
|
||||
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()
|
||||
// state views
|
||||
rollups: '0xdcecb8e3', // rollups(bytes32)
|
||||
rollupCount: '0xff19a3a4', // 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)
|
||||
} as const;
|
||||
|
||||
export class AereRaaSFactoryClient {
|
||||
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
|
||||
|
||||
async rollupCount(): Promise<bigint> {
|
||||
return BigInt(await ethCall(this.provider, this.address, FAC_SEL.rollupCount));
|
||||
}
|
||||
async getSink(): Promise<string> {
|
||||
return '0x' + (await ethCall(this.provider, this.address, FAC_SEL.SINK)).slice(-40);
|
||||
}
|
||||
async getRegistrationBond(): Promise<bigint> {
|
||||
return BigInt(await ethCall(this.provider, this.address, FAC_SEL.REGISTRATION_BOND));
|
||||
}
|
||||
async getMinSinkBps(): Promise<number> {
|
||||
return Number(BigInt(await ethCall(this.provider, this.address, FAC_SEL.MIN_SINK_BPS)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a rollup. Requires the caller to have approved BOND_TOKEN for
|
||||
* REGISTRATION_BOND on the factory beforehand.
|
||||
*/
|
||||
async registerRollup(
|
||||
from: string,
|
||||
opts: {
|
||||
rollupId: string; // bytes32 hex
|
||||
rollupOwner: string;
|
||||
sequencer: string;
|
||||
debtToken: string;
|
||||
sinkBps: number;
|
||||
rollupBps: number;
|
||||
sequencerBps: number;
|
||||
minChallengeBond: bigint;
|
||||
challengeWindow: number;
|
||||
metadataUri: string;
|
||||
}
|
||||
): Promise<string> {
|
||||
const meta = Buffer.from(opts.metadataUri, 'utf8');
|
||||
const head =
|
||||
encBytes32(opts.rollupId) +
|
||||
encAddr(opts.rollupOwner) +
|
||||
encAddr(opts.sequencer) +
|
||||
encAddr(opts.debtToken) +
|
||||
encUint(BigInt(opts.sinkBps)) +
|
||||
encUint(BigInt(opts.rollupBps)) +
|
||||
encUint(BigInt(opts.sequencerBps)) +
|
||||
encUint(opts.minChallengeBond) +
|
||||
encUint(BigInt(opts.challengeWindow)) +
|
||||
encUint(0x140n); // 10 head slots * 32 = 320 = 0x140
|
||||
const padded = Buffer.alloc(Math.ceil(meta.length / 32) * 32);
|
||||
meta.copy(padded);
|
||||
const dyn = encUint(BigInt(meta.length)) + padded.toString('hex');
|
||||
const data = FAC_SEL.registerRollup + head + dyn;
|
||||
return ethSend(this.provider, from, this.address, data);
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================ 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()
|
||||
|
||||
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)
|
||||
} as const;
|
||||
|
||||
export class AereRollupSettlementClient {
|
||||
constructor(public readonly provider: RpcProvider, public readonly address: string) {}
|
||||
|
||||
async sequencer(): Promise<string> {
|
||||
return '0x' + (await ethCall(this.provider, this.address, SET_SEL.sequencer)).slice(-40);
|
||||
}
|
||||
async rollupOwner(): Promise<string> {
|
||||
return '0x' + (await ethCall(this.provider, this.address, SET_SEL.rollupOwner)).slice(-40);
|
||||
}
|
||||
async latestEpoch(): Promise<bigint> {
|
||||
return BigInt(await ethCall(this.provider, this.address, SET_SEL.latestEpoch));
|
||||
}
|
||||
async isFinalised(epoch: bigint): Promise<boolean> {
|
||||
const res = await ethCall(this.provider, this.address, SET_SEL.isFinalised + encUint(epoch));
|
||||
return BigInt(res) === 1n;
|
||||
}
|
||||
async totalRevenueSettled(): Promise<bigint> {
|
||||
return BigInt(await ethCall(this.provider, this.address, SET_SEL.totalRevenueSettled));
|
||||
}
|
||||
|
||||
/* sequencer-only */
|
||||
async proposeStateRoot(from: string, epoch: bigint, root: string, l2BlockHash: string): Promise<string> {
|
||||
const data = SET_SEL.proposeStateRoot + encUint(epoch) + encBytes32(root) + encBytes32(l2BlockHash);
|
||||
return ethSend(this.provider, from, this.address, data);
|
||||
}
|
||||
|
||||
async resolveChallenge(from: string, epoch: bigint, accepted: boolean): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SET_SEL.resolveChallenge + encUint(epoch) + encBool(accepted));
|
||||
}
|
||||
|
||||
/* permissionless */
|
||||
async challenge(from: string, epoch: bigint, bond: bigint, reasonHash: string): Promise<string> {
|
||||
const data = SET_SEL.challenge + encUint(epoch) + encUint(bond) + encBytes32(reasonHash);
|
||||
return ethSend(this.provider, from, this.address, data);
|
||||
}
|
||||
async resolveChallengeExpired(from: string, epoch: bigint): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SET_SEL.resolveChallengeExpired + encUint(epoch));
|
||||
}
|
||||
async advanceFinalisation(from: string): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SET_SEL.advanceFinalisation);
|
||||
}
|
||||
|
||||
/* rollupOwner-only */
|
||||
async settleRevenue(from: string, amount: bigint): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SET_SEL.settleRevenue + encUint(amount));
|
||||
}
|
||||
async setSequencer(from: string, newSequencer: string): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SET_SEL.setSequencer + encAddr(newSequencer));
|
||||
}
|
||||
async transferOwner(from: string, newOwner: string): Promise<string> {
|
||||
return ethSend(this.provider, from, this.address, SET_SEL.transferOwner + encAddr(newOwner));
|
||||
}
|
||||
}
|
||||
1
src/raas/index.ts
Normal file
1
src/raas/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { AereRaaSFactoryClient, AereRollupSettlementClient } from './RaaSClient.js';
|
||||
Loading…
Reference in New Issue
Block a user