Initial @aere/sdk v0.1.0 — typed contract clients for AERE L1

This commit is contained in:
architect 2026-05-03 16:09:06 +03:00
commit 83f837b9b0
8 changed files with 749 additions and 0 deletions

79
README.md Normal file
View File

@ -0,0 +1,79 @@
# @aere/sdk
Official AERE Network TypeScript SDK. Wraps `ethers v6` with typed contract clients for every deployed AERE L1 contract — staking, lending, stablecoin, swap, bridge, identity (KYC), Lightning channels, ZK rollup, faucet.
Designed for **Bank28-class neobank backends**: a single `AereClient` gives you everything you need to integrate AERE as the on-chain layer of a regulated neobank.
## Install
```bash
npm i @aere/sdk ethers
```
## Quickstart
```ts
import { AereClient } from '@aere/sdk';
import { ethers } from 'ethers';
const aere = new AereClient({ privateKey: process.env.OPS_PRIVATE_KEY });
// Multi-asset balance for a user
const portfolio = await aere.getPortfolio('0xUser…');
console.log(ethers.formatEther(portfolio.aere), 'AERE');
console.log(ethers.formatEther(portfolio.aereUsd), 'AereUSD');
// Lock 100 AERE for 90 days at 15% APY (tier 1)
await aere.staking.lockTier90(ethers.parseEther('100'));
// Attest that the user cleared KYC tier 1 (writes to AereIdentity)
const oneYear = Math.floor(Date.now() / 1000) + 365 * 86400;
await aere.identity.addClaim(
'0xUser…',
'kyc-tier-1',
ethers.keccak256(ethers.toUtf8Bytes('SUMSUB:applicant-id-12345')),
oneYear,
);
// Watch for incoming AERE deposits — for a Bank28 webhook
const unsubscribe = await aere.watchTransfersTo('0xUser…', tx => {
console.log(`User received ${ethers.formatEther(tx.value)} AERE in tx ${tx.hash}`);
// → credit Bank28 user account, fire webhook to UI
});
```
## What's in the box
- `AereClient` — top-level handle; pass a `privateKey` or `signer`.
- `aere.staking` — locked staking V2 (4 tiers, 1030% APY).
- `aere.stakingV1` — delegated 8% APY staking.
- `aere.lending` — supply / borrow against AERE collateral.
- `aere.stable` — AereUSD CDP-style stablecoin (AERE-collateralised, NOT USD-reserve-backed).
- `aere.identity` — KYC attestation registry (`addClaim` / `revokeClaim` / `hasValidClaim`).
- `aere.bridge` — federated lock-and-release to ETH/BSC/Polygon/etc.
- `aere.swap` — AMM (Uniswap V2 fork) with pair handles.
- `aere.lightning` — off-chain payment channels.
- `aere.faucet` — programmatic faucet (rarely needed in production).
- `aere.waere` — wrapped native AERE for DeFi.
- `AERE_MAINNET` — canonical address book + RPC URLs.
## Network parameters
| | |
|---|---|
| Chain ID | 2800 (`0xAF0`) |
| RPC | `https://rpc.aere.network` |
| WSS | `wss://wss.aere.network` |
| Indexer | `https://api.aere.network` |
| Explorer | `https://explorer.aere.network` |
## Honest constraints
- **Non-custodial.** End users hold their own keys. For consumer neobanks use Privy / Magic / Web3Auth to provision wallets via email/social, then pass that signer into `AereClient`.
- **No fiat rails.** AERE is the crypto layer. For fiat IBANs / cards / SEPA / KYC ops you need a BaaS partner (Striga, Dipocket, Modulr, Solaris…).
- **AereUSD is AERE-collateralised**, not redeemable for fiat. For real USD on-chain, integrate USDC bridged from Ethereum.
- **3 validators today** (path to 7 staged). 0-of-3 fault tolerance — production-but-fragile. Plan accordingly.
## License
MIT

167
package-lock.json generated Normal file
View File

@ -0,0 +1,167 @@
{
"name": "@aere/sdk",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@aere/sdk",
"version": "0.1.0",
"license": "MIT",
"devDependencies": {
"@types/node": "^22.5.0",
"ethers": "^6.13.4",
"typescript": "^5.6.0"
},
"peerDependencies": {
"ethers": "^6.13.0"
}
},
"node_modules/@adraffy/ens-normalize": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz",
"integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==",
"dev": true,
"license": "MIT"
},
"node_modules/@noble/curves": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.3.2"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@noble/hashes": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@types/node": {
"version": "22.19.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz",
"integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/aes-js": {
"version": "4.0.0-beta.5",
"resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz",
"integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==",
"dev": true,
"license": "MIT"
},
"node_modules/ethers": {
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/ethers/-/ethers-6.16.0.tgz",
"integrity": "sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/ethers-io/"
},
{
"type": "individual",
"url": "https://www.buymeacoffee.com/ricmoo"
}
],
"license": "MIT",
"dependencies": {
"@adraffy/ens-normalize": "1.10.1",
"@noble/curves": "1.2.0",
"@noble/hashes": "1.3.2",
"@types/node": "22.7.5",
"aes-js": "4.0.0-beta.5",
"tslib": "2.7.0",
"ws": "8.17.1"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/ethers/node_modules/@types/node": {
"version": "22.7.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz",
"integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
}
},
"node_modules/ethers/node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"dev": true,
"license": "MIT"
},
"node_modules/tslib": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
"dev": true,
"license": "0BSD"
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
}
}

31
package.json Normal file
View File

@ -0,0 +1,31 @@
{
"name": "@aere/sdk",
"version": "0.1.0",
"description": "Official AERE Network SDK — wraps ethers v6 with typed contract clients for the deployed AERE L1 contracts (stablecoin, lending, staking, swap, bridge, identity, Lightning, ZK rollup).",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": ["dist", "README.md"],
"scripts": {
"build": "tsc",
"test": "node --test dist/test/*.js"
},
"keywords": ["aere", "blockchain", "ethereum", "evm", "defi", "neobank", "sdk"],
"homepage": "https://aere.network/docs.html",
"repository": "https://git.aere.network/aere-network/sdk-js",
"license": "MIT",
"peerDependencies": {
"ethers": "^6.13.0"
},
"devDependencies": {
"@types/node": "^22.5.0",
"ethers": "^6.13.4",
"typescript": "^5.6.0"
}
}

112
src/abis.ts Normal file
View File

@ -0,0 +1,112 @@
/**
* Hand-curated ABI fragments only the methods Bank28-class integrations actually call.
* Full ABIs are at https://aere.network/abis/<ContractName>.json
*/
export const ERC20_ABI = [
'function balanceOf(address) view returns (uint256)',
'function transfer(address to, uint256 amount) returns (bool)',
'function approve(address spender, uint256 amount) returns (bool)',
'function allowance(address owner, address spender) view returns (uint256)',
'function totalSupply() view returns (uint256)',
'function decimals() view returns (uint8)',
'function symbol() view returns (string)',
'function name() view returns (string)',
'event Transfer(address indexed from, address indexed to, uint256 value)',
'event Approval(address indexed owner, address indexed spender, uint256 value)',
] as const;
export const WAERE_ABI = [
...ERC20_ABI,
'function deposit() payable',
'function withdraw(uint256 amount)',
] as const;
export const STAKING_V2_ABI = [
'function lock(uint256 tier) external payable', // tier 0..3 → 30/90/180/365 days
'function withdraw(uint256 lockId) external',
'function earlyExit(uint256 lockId) external',
'function getLocks(address user) external view returns (tuple(uint256 amount, uint64 startTime, uint64 unlockTime, uint8 tier, bool withdrawn)[])',
'function tierAPY(uint256 tier) external view returns (uint256)', // basis points
'function pendingRewards(address user, uint256 lockId) external view returns (uint256)',
] as const;
export const STAKING_V1_ABI = [
'function delegate(address validator) external payable',
'function undelegate(address validator, uint256 amount) external',
'function claimRewards(address validator) external',
'function balanceOf(address user) external view returns (uint256)',
'function pendingRewards(address user, address validator) external view returns (uint256)',
'function currentAPR() external view returns (uint256)', // basis points
] as const;
export const LENDING_ABI = [
'function supply(uint256 amount) external payable',
'function withdraw(uint256 amount) external',
'function lockCollateral(uint256 amount) external payable',
'function borrow(uint256 amount) external',
'function repay(uint256 amount) external payable',
'function getAccount(address user) external view returns (tuple(uint256 supplied, uint256 borrowed, uint256 collateral))',
'function supplyAPY() external view returns (uint256)', // basis points
'function borrowAPY() external view returns (uint256)',
'function ltvLimit() external view returns (uint256)', // basis points (e.g. 7500 = 75%)
] as const;
export const STABLE_ABI = [
...ERC20_ABI,
'function depositCollateral() external payable',
'function withdrawCollateral(uint256 amount) external',
'function mint(uint256 amount) external',
'function burn(uint256 amount) external',
'function getVault(address user) external view returns (tuple(uint256 collateral, uint256 debt))',
'function collateralRatio(address user) external view returns (uint256)', // basis points
'function liquidationThreshold() external view returns (uint256)',
'function minCollateralRatio() external view returns (uint256)',
] as const;
export const IDENTITY_ABI = [
'function addClaim(address subject, bytes32 claimType, bytes32 valueHash, uint64 expires) external',
'function revokeClaim(address subject, bytes32 claimType) external',
'function getClaim(address subject, bytes32 claimType, address attestor) external view returns (tuple(bytes32 valueHash, uint64 issued, uint64 expires, bool revoked))',
'function hasValidClaim(address subject, bytes32 claimType, address attestor) external view returns (bool)',
'event ClaimAdded(address indexed subject, bytes32 indexed claimType, address indexed attestor, uint64 expires)',
'event ClaimRevoked(address indexed subject, bytes32 indexed claimType, address indexed attestor)',
] as const;
export const BRIDGE_ABI = [
'function lockAndBridge(uint256 toChainId, address recipient) external payable',
'function release(uint256 srcChainId, uint256 nonce, address recipient, uint256 amount, bytes[] calldata signatures) external',
'function isReleased(uint256 srcChainId, uint256 nonce) external view returns (bool)',
'function threshold() external view returns (uint256)',
'event Locked(uint256 indexed nonce, uint256 indexed toChainId, address indexed recipient, uint256 amount)',
'event Released(uint256 indexed srcChainId, uint256 indexed nonce, address indexed recipient, uint256 amount)',
] as const;
export const FAUCET_ABI = [
'function claim() external',
'function lastClaim(address user) external view returns (uint64)',
'function dripAmount() external view returns (uint256)',
'function cooldown() external view returns (uint64)',
] as const;
export const SWAP_FACTORY_ABI = [
'function getPair(address tokenA, address tokenB) external view returns (address)',
'function createPair(address tokenA, address tokenB) external returns (address)',
'function allPairsLength() external view returns (uint256)',
'function allPairs(uint256) external view returns (address)',
] as const;
export const SWAP_PAIR_ABI = [
'function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)',
'function token0() external view returns (address)',
'function token1() external view returns (address)',
'function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external',
] as const;
export const LIGHTNING_CHANNELS_ABI = [
'function open(address counterparty) external payable returns (uint256 id)',
'function fund(uint256 id) external payable',
'function closeWithState(uint256 id, uint256 balanceA, uint256 balanceB, uint256 nonce, tuple(address payer, address payee, uint256 amount, bytes32 hashlock, uint64 timeout, uint8 status)[] htlcs, bytes sigA, bytes sigB) external',
'function settle(uint256 id) external',
'function channels(uint256) view returns (address partyA, address partyB, uint256 balanceA, uint256 balanceB, uint256 nonce, uint64 challengeDeadline, uint8 status)',
'function getHTLCs(uint256 id) view returns (tuple(address payer, address payee, uint256 amount, bytes32 hashlock, uint64 timeout, uint8 status)[])',
] as const;

77
src/addresses.ts Normal file
View File

@ -0,0 +1,77 @@
/**
* Canonical AERE Network mainnet contract addresses (chain 2800).
* Last updated: 2026-05-03. Source of truth: https://aere.network/docs.html
*/
export const AERE_MAINNET = {
chainId: 2800,
rpc: 'https://rpc.aere.network',
ws: 'wss://wss.aere.network',
explorer: 'https://explorer.aere.network',
indexer: 'https://api.aere.network',
// Core
WAERE: '0x805193E3d03AFbffced984f43C10b56D7F7E0b35',
AereToken: '0xf0A13823A4bFa86358Fe30aaf1f44A36AcbCf399',
AereGovernance: '0x3280518e571fBaB2eF9C7138c1251a89A849f773',
AereGovernanceV2: '0xc0F76914d1538c079f07BDeBDd00Ce5b9f791d83',
AereTreasury: '0x658dD2CD1F798AAb19fEc8FF69A270B2d192CaD1',
AereSecurity: '0x687933AE7ea4927867AC227F1b60d476003e6119',
AereConsensus: '0xf0a8df7BDc25721892475B21271e52D77B0e84DC',
// Staking
AereStaking: '0x78a72669a8C48FaC02E7BD8C76baEca6e6a809A7',
AereStakingV2: '0x1d4444AaD20Beb034c0A97D2E32cdAD06E80e28D',
AereYieldFarm: '0xf7C78Cfad1c2CB92051CB1fF7afeaae1A96A7D62',
// DeFi
AereSwapFactory: '0xDdBe942aD9eB0F3E7C541BdCF7CC2cfA29d35aE4',
AereLending: '0xdAd25D2163187dF8aaecf9eA31b6355315bb69F1',
AereStable: '0xa44178085387bcCED29C8d51b05976D5e04D5C0E',
AereOracle: '0xebFb8BBc2955719bb11AC460b6fc47f46F3356EB',
// Identity / KYC attestation
AereIdentity: '0xfd8dF7fc2FB7bA2019c5ef7df31c204589B6DAEe',
// Bridge
AereBridge: '0x71944DD6A57B9e0530ea86aC577a4F8F57037D7E',
AereBridgeCounterparty: '0xda6F00570090eC7759582f0b574269F1d96a7613',
WAEREForeign: '0xd7190f47FA7e8D95E5f09f249E479E836CcC1b3f',
// Faucet / Mining
AereFaucet: '0xaD305e4D91e0a9160Bd338Fd1ecb2Ee1645daC44',
AereMiningSubscription: '0x267035e16f6F34633fC91f8a410d21A24EB1403E',
AereMiningPool: '0x772089A61E9d0C7745bA98487159C688CD9aED5C',
// Burn / supply
AereFeeBurnVault: '0xF0a90fEA2FDA12CC2545ddAa817F5C253F348801',
AereDAOBurner: '0x31642bD7901A0405950026072c121bC3Cf25F4fC',
// NFT
AereNFT: '0xeb5420EdabD616499acD037E812cbb6911a890bC',
AereNFTMarketplace: '0xF8bDDad4aDACF9d38711e8f9aFC8a2697aBF0d47',
// Layer 2
AerePaymentChannels: '0xAbDb01d9A4f41792129b2654Fb6DDB9689360DEc',
AereLightningChannels: '0xc58Fc171C962C1C8AC356538FdFd2E3f04c15B62',
AereChannelRegistry: '0x8Eba6a0E0875FE5b02A75cfF09E0fB7ACa9CeDfE',
AereHTLC: '0x71851094e5bAaC3e70Ca76A399510B2Ed9100988',
AereShardCoordinator: '0x852e07F2619F7F4Ad10D9F2AC681310301d99528',
// ZK
AereZKVerifierGroth16: '0x94AF0740ad4b149F0bb860b0Dda61027322Bc29F',
AereZKCommitments: '0x34F7b38dDC6b9d4f2ADC165AcF2b22B2349AA299',
AereZKRollup8Verifier: '0xC6936869D0F2eA227d92e0DdC709c6BCB1Cf4548',
AereZKRollup8: '0x74cdEbdd929A430037f68CBDB85EC04a89cb623e',
AereZKRollup: '0x6F5c279A3436d8b21AF6f8f7E48174A12D4a94a1',
AereZKRollupVerifier: '0x99fe69077B3DDaA81B94D0F95f6724F6819CC320',
// Plasma — 6 production chains
AerePlasmaChain1: '0xFBd34Ad45ef07b0B7616E77a8a6cef2cb62f98d4',
AerePlasmaChain2: '0xE4E53a1eb1F6C40bbD334C191901dBF99BCb8af0',
AerePlasmaChain3: '0x584b89a91445BD69F214d30F040E09cA6db9c5c8',
AerePlasmaChain4: '0xb5456D48Bfda70635c13b6cBE1ad0310Dc0171aD',
AerePlasmaChain5: '0x9CA479c8c52c0EBb4599319a36a5a017BCC70628',
AerePlasmaChain6: '0xA6BFECa9f5d6E7728883e71A10006Cd522Fe40b3',
} as const;
export type AereContractName = keyof Omit<typeof AERE_MAINNET, 'chainId' | 'rpc' | 'ws' | 'explorer' | 'indexer'>;

261
src/client.ts Normal file
View File

@ -0,0 +1,261 @@
import {
Contract, JsonRpcProvider, Wallet, ethers, type Provider, type Signer,
type ContractRunner, WebSocketProvider, keccak256, toUtf8Bytes,
} from 'ethers';
import { AERE_MAINNET } from './addresses.js';
import {
ERC20_ABI, WAERE_ABI, STAKING_V2_ABI, STAKING_V1_ABI, LENDING_ABI, STABLE_ABI,
IDENTITY_ABI, BRIDGE_ABI, FAUCET_ABI, SWAP_FACTORY_ABI, SWAP_PAIR_ABI, LIGHTNING_CHANNELS_ABI,
} from './abis.js';
export interface AereClientOptions {
rpcUrl?: string; // default https://rpc.aere.network
wsUrl?: string; // default wss://wss.aere.network
privateKey?: string; // optional — if provided, becomes the default signer
signer?: Signer; // alternatively pass a pre-built signer (e.g. browser wallet)
}
/**
* Top-level AERE Network client. Provides a typed handle to every deployed contract
* Bank28-class integrations need.
*
* @example
* const aere = new AereClient({ privateKey: process.env.PK });
* const bal = await aere.getNativeBalance(userAddr);
* await aere.staking.lockTier1(ethers.parseEther('100')); // 90-day lock
*/
export class AereClient {
readonly provider: Provider;
readonly signer?: Signer;
readonly chainId = AERE_MAINNET.chainId;
readonly addresses = AERE_MAINNET;
constructor(opts: AereClientOptions = {}) {
const rpc = opts.rpcUrl ?? AERE_MAINNET.rpc;
this.provider = new JsonRpcProvider(rpc, {
name: 'aere', chainId: AERE_MAINNET.chainId,
});
if (opts.signer) this.signer = opts.signer;
else if (opts.privateKey) this.signer = new Wallet(opts.privateKey, this.provider);
}
// ────────────────────────────── Chain primitives ──────────────────────────────
async getBlockNumber(): Promise<number> {
return Number(await this.provider.getBlockNumber());
}
async getNativeBalance(addr: string): Promise<bigint> {
return this.provider.getBalance(addr);
}
async getValidators(): Promise<string[]> {
const res: any = await (this.provider as any).send('qbft_getValidatorsByBlockNumber', ['latest']);
return Array.isArray(res) ? res : [];
}
async sendNative(to: string, amountWei: bigint, sigOverride?: Signer): Promise<string> {
const s = sigOverride ?? this.signer;
if (!s) throw new Error('AereClient: no signer configured');
const tx = await s.sendTransaction({ to, value: amountWei });
return tx.hash;
}
// ────────────────────────────── Contract handles ──────────────────────────────
private contract(addr: string, abi: readonly string[], signer = false): Contract {
const runner: ContractRunner = (signer ? this.signer ?? this.provider : this.provider) as ContractRunner;
return new Contract(addr, abi as any, runner);
}
/** WAERE — wrapped native AERE (ERC-20) */
get waere() {
const c = this.contract(this.addresses.WAERE, WAERE_ABI, true);
return {
contract: c,
balanceOf: (a: string) => c.balanceOf(a) as Promise<bigint>,
deposit: (amountWei: bigint) => c.deposit({ value: amountWei }),
withdraw: (amountWei: bigint) => c.withdraw(amountWei),
transfer: (to: string, amount: bigint) => c.transfer(to, amount),
};
}
/** AereStakingV2 — locked-period staking with 4 tiers (30/90/180/365 days, 10/15/22/30% APY) */
get staking() {
const c = this.contract(this.addresses.AereStakingV2, STAKING_V2_ABI, true);
return {
contract: c,
tierAPY: (tier: 0 | 1 | 2 | 3) => c.tierAPY(tier) as Promise<bigint>,
lock: (tier: 0 | 1 | 2 | 3, amountWei: bigint) => c.lock(tier, { value: amountWei }),
lockTier30: (a: bigint) => c.lock(0, { value: a }),
lockTier90: (a: bigint) => c.lock(1, { value: a }),
lockTier180: (a: bigint) => c.lock(2, { value: a }),
lockTier365: (a: bigint) => c.lock(3, { value: a }),
withdraw: (lockId: number) => c.withdraw(lockId),
earlyExit: (lockId: number) => c.earlyExit(lockId),
getLocks: (user: string) => c.getLocks(user),
pendingRewards: (user: string, lockId: number) => c.pendingRewards(user, lockId) as Promise<bigint>,
};
}
/** AereStaking V1 — delegated 8% APY staking */
get stakingV1() {
const c = this.contract(this.addresses.AereStaking, STAKING_V1_ABI, true);
return {
contract: c,
delegate: (validator: string, amountWei: bigint) => c.delegate(validator, { value: amountWei }),
undelegate: (validator: string, amount: bigint) => c.undelegate(validator, amount),
claimRewards: (validator: string) => c.claimRewards(validator),
balanceOf: (user: string) => c.balanceOf(user) as Promise<bigint>,
pendingRewards: (user: string, validator: string) => c.pendingRewards(user, validator) as Promise<bigint>,
currentAPR: () => c.currentAPR() as Promise<bigint>,
};
}
/** AereLending — supply / borrow against AERE collateral */
get lending() {
const c = this.contract(this.addresses.AereLending, LENDING_ABI, true);
return {
contract: c,
supply: (amountWei: bigint) => c.supply(amountWei, { value: amountWei }),
withdraw: (amount: bigint) => c.withdraw(amount),
lockCollateral: (amountWei: bigint) => c.lockCollateral(amountWei, { value: amountWei }),
borrow: (amount: bigint) => c.borrow(amount),
repay: (amountWei: bigint) => c.repay(amountWei, { value: amountWei }),
getAccount: (user: string) => c.getAccount(user),
supplyAPY: () => c.supplyAPY() as Promise<bigint>,
borrowAPY: () => c.borrowAPY() as Promise<bigint>,
ltvLimit: () => c.ltvLimit() as Promise<bigint>,
};
}
/** AereStable (AereUSD) — CDP-style stablecoin · AERE-collateralised, NOT USD-reserve-backed */
get stable() {
const c = this.contract(this.addresses.AereStable, STABLE_ABI, true);
return {
contract: c,
balanceOf: (a: string) => c.balanceOf(a) as Promise<bigint>,
depositCollateral: (amountWei: bigint) => c.depositCollateral({ value: amountWei }),
withdrawCollateral: (amount: bigint) => c.withdrawCollateral(amount),
mint: (amount: bigint) => c.mint(amount),
burn: (amount: bigint) => c.burn(amount),
getVault: (user: string) => c.getVault(user),
collateralRatio: (user: string) => c.collateralRatio(user) as Promise<bigint>,
};
}
/** AereIdentity — DID / KYC attestation registry */
get identity() {
const c = this.contract(this.addresses.AereIdentity, IDENTITY_ABI, true);
return {
contract: c,
/** Attest that `subject` has cleared a KYC tier. `claimType` is hashed via keccak256. */
addClaim: (subject: string, claimType: string, valueHash: string, expiresUnix: number) =>
c.addClaim(subject, keccak256(toUtf8Bytes(claimType)), valueHash, expiresUnix),
revokeClaim: (subject: string, claimType: string) =>
c.revokeClaim(subject, keccak256(toUtf8Bytes(claimType))),
getClaim: (subject: string, claimType: string, attestor: string) =>
c.getClaim(subject, keccak256(toUtf8Bytes(claimType)), attestor),
hasValidClaim: (subject: string, claimType: string, attestor: string) =>
c.hasValidClaim(subject, keccak256(toUtf8Bytes(claimType)), attestor) as Promise<boolean>,
};
}
/** AereBridge — federated lock-and-release to 8 EVM chains */
get bridge() {
const c = this.contract(this.addresses.AereBridge, BRIDGE_ABI, true);
return {
contract: c,
lockAndBridge: (toChainId: number, recipient: string, amountWei: bigint) =>
c.lockAndBridge(toChainId, recipient, { value: amountWei }),
isReleased: (srcChainId: number, nonce: bigint) =>
c.isReleased(srcChainId, nonce) as Promise<boolean>,
threshold: () => c.threshold() as Promise<bigint>,
};
}
/** AereFaucet — 0.05 AERE drip per address per 24h */
get faucet() {
const c = this.contract(this.addresses.AereFaucet, FAUCET_ABI, true);
return {
contract: c,
claim: () => c.claim(),
lastClaim: (user: string) => c.lastClaim(user) as Promise<bigint>,
dripAmount: () => c.dripAmount() as Promise<bigint>,
cooldown: () => c.cooldown() as Promise<bigint>,
};
}
/** AereSwapFactory + on-demand pair handles */
get swap() {
const factory = this.contract(this.addresses.AereSwapFactory, SWAP_FACTORY_ABI, true);
return {
factory,
getPair: (tokenA: string, tokenB: string) => factory.getPair(tokenA, tokenB) as Promise<string>,
pair: (pairAddr: string) => this.contract(pairAddr, SWAP_PAIR_ABI, true),
};
}
/** AereLightningChannels — off-chain payment channels with HTLCs */
get lightning() {
const c = this.contract(this.addresses.AereLightningChannels, LIGHTNING_CHANNELS_ABI, true);
return {
contract: c,
open: (counterparty: string, depositWei: bigint) =>
c.open(counterparty, { value: depositWei }),
fund: (id: bigint, amountWei: bigint) => c.fund(id, { value: amountWei }),
channels: (id: bigint) => c.channels(id),
getHTLCs: (id: bigint) => c.getHTLCs(id),
};
}
// ────────────────────────────── Higher-level helpers ──────────────────────────────
/** Convenience: ERC-20 balance for any token (USDC bridged from Ethereum, etc.) */
async erc20BalanceOf(tokenAddr: string, holder: string): Promise<bigint> {
const c = new Contract(tokenAddr, ERC20_ABI, this.provider);
return c.balanceOf(holder) as Promise<bigint>;
}
/** Convenience: send any ERC-20 token */
async erc20Transfer(tokenAddr: string, to: string, amount: bigint): Promise<string> {
if (!this.signer) throw new Error('no signer');
const c = new Contract(tokenAddr, ERC20_ABI, this.signer);
const tx = await c.transfer(to, amount);
return tx.hash;
}
/** Convenience: aggregate a Bank28-style multi-asset balance for a user */
async getPortfolio(user: string): Promise<{
aere: bigint;
waere: bigint;
aereUsd: bigint;
}> {
const [aere, waere, aereUsd] = await Promise.all([
this.getNativeBalance(user),
this.waere.balanceOf(user),
this.stable.balanceOf(user),
]);
return { aere, waere, aereUsd };
}
/** Subscribe to new blocks via WebSocket. Returns an unsubscribe fn. */
watchBlocks(handler: (blockNumber: number) => void, wsUrl?: string): () => void {
const ws = new WebSocketProvider(wsUrl ?? AERE_MAINNET.ws);
ws.on('block', handler);
return () => { ws.removeAllListeners('block'); ws.destroy(); };
}
/** Watch native AERE deposits to a specific address. Useful for "credit user's neobank account when they receive AERE". */
async watchTransfersTo(addr: string, handler: (tx: { hash: string; value: bigint; from: string; blockNumber: number }) => void, wsUrl?: string): Promise<() => void> {
const ws = new WebSocketProvider(wsUrl ?? AERE_MAINNET.ws);
const onBlock = async (blockNumber: number) => {
const block = await ws.getBlock(blockNumber, true);
if (!block) return;
for (const tx of block.prefetchedTransactions ?? []) {
if (tx.to?.toLowerCase() === addr.toLowerCase() && tx.value > 0n) {
handler({ hash: tx.hash, value: tx.value, from: tx.from, blockNumber });
}
}
};
ws.on('block', onBlock);
return () => { ws.off('block', onBlock); ws.destroy(); };
}
}

6
src/index.ts Normal file
View File

@ -0,0 +1,6 @@
export { AereClient, type AereClientOptions } from './client.js';
export { AERE_MAINNET, type AereContractName } from './addresses.js';
export {
ERC20_ABI, WAERE_ABI, STAKING_V2_ABI, STAKING_V1_ABI, LENDING_ABI, STABLE_ABI,
IDENTITY_ABI, BRIDGE_ABI, FAUCET_ABI, SWAP_FACTORY_ABI, SWAP_PAIR_ABI, LIGHTNING_CHANNELS_ABI,
} from './abis.js';

16
tsconfig.json Normal file
View File

@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "es2022",
"module": "es2022",
"moduleResolution": "node",
"outDir": "dist",
"rootDir": "src",
"strict": true,
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}