sdk-js/src/lending/LendingMarketClient.ts
Liviu 99abfff98a 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.
2026-06-08 03:33:52 +03:00

167 lines
8.9 KiB
TypeScript

/**
* 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;
}
}