@agether/sdk 2.8.1 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +763 -379
- package/dist/index.d.mts +165 -113
- package/dist/index.d.ts +165 -113
- package/dist/index.js +522 -337
- package/dist/index.mjs +522 -337
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -77,13 +77,13 @@ module.exports = __toCommonJS(index_exports);
|
|
|
77
77
|
var import_ethers = require("ethers");
|
|
78
78
|
|
|
79
79
|
// src/types/index.ts
|
|
80
|
-
var ChainId = /* @__PURE__ */ ((
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return
|
|
80
|
+
var ChainId = /* @__PURE__ */ ((ChainId3) => {
|
|
81
|
+
ChainId3[ChainId3["Ethereum"] = 1] = "Ethereum";
|
|
82
|
+
ChainId3[ChainId3["Base"] = 8453] = "Base";
|
|
83
|
+
ChainId3[ChainId3["BaseSepolia"] = 84532] = "BaseSepolia";
|
|
84
|
+
ChainId3[ChainId3["Sepolia"] = 11155111] = "Sepolia";
|
|
85
|
+
ChainId3[ChainId3["Hardhat"] = 31337] = "Hardhat";
|
|
86
|
+
return ChainId3;
|
|
87
87
|
})(ChainId || {});
|
|
88
88
|
var AgetherError = class extends Error {
|
|
89
89
|
constructor(message, code, details) {
|
|
@@ -247,13 +247,13 @@ var CONTRACT_ADDRESSES = {
|
|
|
247
247
|
safeProxyFactory: SAFE_PROXY_FACTORY,
|
|
248
248
|
safe7579: SAFE7579,
|
|
249
249
|
entryPoint: ENTRYPOINT_V07,
|
|
250
|
-
agether4337Factory: "
|
|
251
|
-
agether7579Bootstrap: "
|
|
252
|
-
erc8004ValidationModule: "
|
|
253
|
-
agetherHookMultiplexer: "
|
|
250
|
+
agether4337Factory: "0xb6363c2B5C72C14D3fC4261e3dd836D8966bE072",
|
|
251
|
+
agether7579Bootstrap: "0x055C2e70dd011C4ADEEfB795Ab77D74437be6D33",
|
|
252
|
+
erc8004ValidationModule: "0xE282fB8615abb8bA53F07b8BAB2937C78fE3867D",
|
|
253
|
+
agetherHookMultiplexer: "0xeD62ac874F58CEc9F065aB8e6872752Eb0F6eA14",
|
|
254
254
|
validationRegistry: ZERO,
|
|
255
|
-
agether8004Scorer: "
|
|
256
|
-
timelockController: "
|
|
255
|
+
agether8004Scorer: "0x960853769d52B14aA0daeab7E1E59f5c9299cb65",
|
|
256
|
+
timelockController: "0x78e0227f9DE577e583B8149C73F0bA1E7200AD01",
|
|
257
257
|
usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
258
258
|
identityRegistry: ERC8004_IDENTITY_REGISTRY,
|
|
259
259
|
morphoBlue: MORPHO_BLUE
|
|
@@ -263,13 +263,13 @@ var CONTRACT_ADDRESSES = {
|
|
|
263
263
|
safeProxyFactory: SAFE_PROXY_FACTORY,
|
|
264
264
|
safe7579: SAFE7579,
|
|
265
265
|
entryPoint: ENTRYPOINT_V07,
|
|
266
|
-
agether4337Factory: "
|
|
267
|
-
agether7579Bootstrap: "
|
|
268
|
-
erc8004ValidationModule: "
|
|
269
|
-
agetherHookMultiplexer: "
|
|
266
|
+
agether4337Factory: "0x73f4153bf1d46dB203Db27fc8FC942f6279D8d38",
|
|
267
|
+
agether7579Bootstrap: "0xbD0BDFE70fDB88fc03F2Ea22B81A2dfc99298E42",
|
|
268
|
+
erc8004ValidationModule: "0x85C8C97cE5AE540a4408D6A77a6D3aFcA9BCdB71",
|
|
269
|
+
agetherHookMultiplexer: "0x688cab46ce5A7450D706e9E3C8e0F31BaEa6c8BE",
|
|
270
270
|
validationRegistry: ZERO,
|
|
271
|
-
agether8004Scorer: "
|
|
272
|
-
timelockController: "
|
|
271
|
+
agether8004Scorer: "0x33eB904fe9975e2D8c577aD7e5B14CefBD4A65E1",
|
|
272
|
+
timelockController: "0xB3FD04f0B7c9DeC7f7B52d5c2CdfdCB3Fc9eE111",
|
|
273
273
|
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
274
274
|
identityRegistry: ERC8004_IDENTITY_REGISTRY,
|
|
275
275
|
morphoBlue: MORPHO_BLUE
|
|
@@ -367,93 +367,260 @@ function getContractAddresses(chainId) {
|
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
// src/clients/AgetherClient.ts
|
|
370
|
+
var MODE_SINGLE = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
371
|
+
var erc20Iface = new import_ethers.ethers.Interface(ERC20_ABI);
|
|
372
|
+
var KNOWN_TOKENS = {
|
|
373
|
+
[8453 /* Base */]: {
|
|
374
|
+
WETH: { address: "0x4200000000000000000000000000000000000006", symbol: "WETH", decimals: 18 },
|
|
375
|
+
wstETH: { address: "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452", symbol: "wstETH", decimals: 18 },
|
|
376
|
+
cbETH: { address: "0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22", symbol: "cbETH", decimals: 18 }
|
|
377
|
+
},
|
|
378
|
+
[1 /* Ethereum */]: {
|
|
379
|
+
WETH: { address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", symbol: "WETH", decimals: 18 },
|
|
380
|
+
wstETH: { address: "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", symbol: "wstETH", decimals: 18 },
|
|
381
|
+
cbETH: { address: "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", symbol: "cbETH", decimals: 18 }
|
|
382
|
+
}
|
|
383
|
+
};
|
|
370
384
|
var AgetherClient = class _AgetherClient {
|
|
371
385
|
constructor(options) {
|
|
372
386
|
this.config = options.config;
|
|
373
387
|
this.signer = options.signer;
|
|
374
388
|
this.agentId = options.agentId;
|
|
389
|
+
this._rpcUrl = options.config.rpcUrl;
|
|
390
|
+
if (options._privateKey) {
|
|
391
|
+
this._privateKey = options._privateKey;
|
|
392
|
+
this._useExternalSigner = false;
|
|
393
|
+
} else {
|
|
394
|
+
this._useExternalSigner = true;
|
|
395
|
+
}
|
|
375
396
|
const provider = options.signer.provider;
|
|
376
397
|
if (!provider) throw new AgetherError("Signer must have a provider", "NO_PROVIDER");
|
|
377
|
-
|
|
378
|
-
options.
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
);
|
|
382
|
-
this.identityRegistry = new import_ethers.Contract(
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
);
|
|
387
|
-
this.agether8004Scorer = new import_ethers.Contract(
|
|
388
|
-
options.config.contracts.agether8004Scorer,
|
|
389
|
-
AGETHER_8004_SCORER_ABI,
|
|
390
|
-
provider
|
|
391
|
-
);
|
|
392
|
-
this.validationModule = new import_ethers.Contract(
|
|
393
|
-
options.config.contracts.erc8004ValidationModule,
|
|
394
|
-
AGETHER_8004_VALIDATION_MODULE_ABI,
|
|
395
|
-
provider
|
|
396
|
-
);
|
|
398
|
+
if ("address" in options.signer && typeof options.signer.address === "string") {
|
|
399
|
+
this._eoaAddress = options.signer.address;
|
|
400
|
+
}
|
|
401
|
+
const c = options.config.contracts;
|
|
402
|
+
this.agether4337Factory = new import_ethers.Contract(c.agether4337Factory, AGETHER_4337_FACTORY_ABI, options.signer);
|
|
403
|
+
this.identityRegistry = new import_ethers.Contract(c.identityRegistry, IDENTITY_REGISTRY_ABI, options.signer);
|
|
404
|
+
this.agether8004Scorer = new import_ethers.Contract(c.agether8004Scorer, AGETHER_8004_SCORER_ABI, provider);
|
|
405
|
+
this.validationModule = new import_ethers.Contract(c.erc8004ValidationModule, AGETHER_8004_VALIDATION_MODULE_ABI, provider);
|
|
406
|
+
this.entryPoint = new import_ethers.Contract(c.entryPoint, ENTRYPOINT_V07_ABI, options.signer);
|
|
397
407
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
408
|
+
static fromPrivateKey(privateKey, agentIdOrChain, chainIdOrConfig) {
|
|
409
|
+
let agentId;
|
|
410
|
+
let config;
|
|
411
|
+
if (typeof agentIdOrChain === "bigint") {
|
|
412
|
+
agentId = agentIdOrChain;
|
|
413
|
+
config = typeof chainIdOrConfig === "number" ? getDefaultConfig(chainIdOrConfig) : chainIdOrConfig;
|
|
414
|
+
} else {
|
|
415
|
+
config = typeof agentIdOrChain === "number" ? getDefaultConfig(agentIdOrChain) : agentIdOrChain;
|
|
416
|
+
}
|
|
401
417
|
const provider = new import_ethers.ethers.JsonRpcProvider(config.rpcUrl);
|
|
402
418
|
const signer = new import_ethers.ethers.Wallet(privateKey, provider);
|
|
419
|
+
return new _AgetherClient({ config, signer, agentId, _privateKey: privateKey });
|
|
420
|
+
}
|
|
421
|
+
static fromSigner(signer, agentIdOrChain, chainIdOrConfig) {
|
|
422
|
+
if (!signer.provider) {
|
|
423
|
+
throw new AgetherError("Signer must be connected to a provider", "NO_PROVIDER");
|
|
424
|
+
}
|
|
425
|
+
let agentId;
|
|
426
|
+
let config;
|
|
427
|
+
if (typeof agentIdOrChain === "bigint") {
|
|
428
|
+
agentId = agentIdOrChain;
|
|
429
|
+
config = typeof chainIdOrConfig === "number" ? getDefaultConfig(chainIdOrConfig) : chainIdOrConfig;
|
|
430
|
+
} else {
|
|
431
|
+
config = typeof agentIdOrChain === "number" ? getDefaultConfig(agentIdOrChain) : agentIdOrChain;
|
|
432
|
+
}
|
|
403
433
|
return new _AgetherClient({ config, signer, agentId });
|
|
404
434
|
}
|
|
405
|
-
//
|
|
435
|
+
// ════════════════════════════════════════════════════════
|
|
436
|
+
// Registration
|
|
437
|
+
// ════════════════════════════════════════════════════════
|
|
438
|
+
/**
|
|
439
|
+
* Register: create ERC-8004 identity + Safe account in one flow.
|
|
440
|
+
* If already registered, returns existing state.
|
|
441
|
+
*
|
|
442
|
+
* Sets `this.agentId` on success so subsequent operations work immediately.
|
|
443
|
+
*/
|
|
444
|
+
async register() {
|
|
445
|
+
const eoaAddr = await this._getSignerAddress();
|
|
446
|
+
if (this.agentId !== void 0) {
|
|
447
|
+
const exists = await this.agether4337Factory.accountExists(this.agentId);
|
|
448
|
+
if (exists) {
|
|
449
|
+
const acct = await this.agether4337Factory.getAccount(this.agentId);
|
|
450
|
+
this.accountAddress = acct;
|
|
451
|
+
const kyaRequired2 = await this.isKyaRequired();
|
|
452
|
+
return {
|
|
453
|
+
agentId: this.agentId.toString(),
|
|
454
|
+
address: eoaAddr,
|
|
455
|
+
agentAccount: acct,
|
|
456
|
+
alreadyRegistered: true,
|
|
457
|
+
kyaRequired: kyaRequired2
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
let agentId;
|
|
462
|
+
if (this.agentId !== void 0) {
|
|
463
|
+
const balance = await this.identityRegistry.balanceOf(eoaAddr);
|
|
464
|
+
if (balance > 0n) {
|
|
465
|
+
agentId = this.agentId;
|
|
466
|
+
} else {
|
|
467
|
+
agentId = await this._mintNewIdentity();
|
|
468
|
+
}
|
|
469
|
+
} else {
|
|
470
|
+
agentId = await this._mintNewIdentity();
|
|
471
|
+
}
|
|
472
|
+
this.agentId = agentId;
|
|
473
|
+
const acctExists = await this.agether4337Factory.accountExists(agentId);
|
|
474
|
+
let txHash;
|
|
475
|
+
if (!acctExists) {
|
|
476
|
+
const tx = await this.agether4337Factory.createAccount(agentId);
|
|
477
|
+
const receipt = await tx.wait();
|
|
478
|
+
this._refreshSigner();
|
|
479
|
+
txHash = receipt.hash;
|
|
480
|
+
}
|
|
481
|
+
const acctAddr = await this.agether4337Factory.getAccount(agentId);
|
|
482
|
+
this.accountAddress = acctAddr;
|
|
483
|
+
const kyaRequired = await this.isKyaRequired();
|
|
484
|
+
return {
|
|
485
|
+
agentId: agentId.toString(),
|
|
486
|
+
address: eoaAddr,
|
|
487
|
+
agentAccount: acctAddr,
|
|
488
|
+
alreadyRegistered: acctExists,
|
|
489
|
+
kyaRequired,
|
|
490
|
+
tx: txHash
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
/** Mint a new ERC-8004 identity and return the agentId. */
|
|
494
|
+
async _mintNewIdentity() {
|
|
495
|
+
const regTx = await this.identityRegistry.register();
|
|
496
|
+
const regReceipt = await regTx.wait();
|
|
497
|
+
this._refreshSigner();
|
|
498
|
+
let agentId = 0n;
|
|
499
|
+
for (const log of regReceipt.logs) {
|
|
500
|
+
try {
|
|
501
|
+
const parsed = this.identityRegistry.interface.parseLog({
|
|
502
|
+
topics: log.topics,
|
|
503
|
+
data: log.data
|
|
504
|
+
});
|
|
505
|
+
if (parsed?.name === "Transfer") {
|
|
506
|
+
agentId = parsed.args[2];
|
|
507
|
+
break;
|
|
508
|
+
}
|
|
509
|
+
} catch (e) {
|
|
510
|
+
console.warn("[agether] parseLog skip:", e instanceof Error ? e.message : e);
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
if (agentId === 0n) {
|
|
515
|
+
throw new AgetherError("Failed to parse agentId from registration", "PARSE_ERROR");
|
|
516
|
+
}
|
|
517
|
+
return agentId;
|
|
518
|
+
}
|
|
519
|
+
// ════════════════════════════════════════════════════════
|
|
520
|
+
// Account Management
|
|
521
|
+
// ════════════════════════════════════════════════════════
|
|
522
|
+
/**
|
|
523
|
+
* Deploy a Safe account smart wallet for the agent.
|
|
524
|
+
* The caller must own the ERC-8004 NFT.
|
|
525
|
+
*/
|
|
406
526
|
async createAccount() {
|
|
407
|
-
const
|
|
527
|
+
const id = this._requireAgentId();
|
|
528
|
+
const tx = await this.agether4337Factory.createAccount(id);
|
|
408
529
|
const receipt = await tx.wait();
|
|
530
|
+
this._refreshSigner();
|
|
409
531
|
const event = receipt.logs.map((log) => {
|
|
410
532
|
try {
|
|
411
533
|
return this.agether4337Factory.interface.parseLog(log);
|
|
412
|
-
} catch
|
|
413
|
-
console.warn("[agether] createAccount parseLog skip:", e instanceof Error ? e.message : e);
|
|
534
|
+
} catch {
|
|
414
535
|
return null;
|
|
415
536
|
}
|
|
416
537
|
}).find((e) => e?.name === "AccountCreated");
|
|
417
538
|
if (event) {
|
|
418
539
|
this.accountAddress = event.args.safeAccount;
|
|
419
540
|
} else {
|
|
420
|
-
this.accountAddress = await this.agether4337Factory.getAccount(
|
|
541
|
+
this.accountAddress = await this.agether4337Factory.getAccount(id);
|
|
421
542
|
}
|
|
422
543
|
return this.accountAddress;
|
|
423
544
|
}
|
|
545
|
+
/** Get the Safe account address for the current agent. Cached after first call. */
|
|
424
546
|
async getAccountAddress() {
|
|
425
547
|
if (this.accountAddress) return this.accountAddress;
|
|
426
|
-
const
|
|
548
|
+
const id = this._requireAgentId();
|
|
549
|
+
const addr = await this.agether4337Factory.getAccount(id);
|
|
427
550
|
if (addr === import_ethers.ethers.ZeroAddress) {
|
|
428
|
-
throw new AgetherError(
|
|
551
|
+
throw new AgetherError(
|
|
552
|
+
"No account found. Create one with createAccount() or register().",
|
|
553
|
+
"NO_ACCOUNT"
|
|
554
|
+
);
|
|
429
555
|
}
|
|
430
556
|
this.accountAddress = addr;
|
|
431
557
|
return addr;
|
|
432
558
|
}
|
|
559
|
+
/** Check whether the Safe account has been deployed. */
|
|
433
560
|
async accountExists() {
|
|
434
|
-
|
|
561
|
+
const id = this._requireAgentId();
|
|
562
|
+
return this.agether4337Factory.accountExists(id);
|
|
435
563
|
}
|
|
436
|
-
//
|
|
564
|
+
// ════════════════════════════════════════════════════════
|
|
565
|
+
// Balances
|
|
566
|
+
// ════════════════════════════════════════════════════════
|
|
567
|
+
/**
|
|
568
|
+
* Get ETH, USDC, and collateral token balances for EOA and Safe account.
|
|
569
|
+
*
|
|
570
|
+
* Collateral tokens are resolved from a built-in registry of well-known
|
|
571
|
+
* tokens per chain (WETH, wstETH, cbETH).
|
|
572
|
+
*/
|
|
437
573
|
async getBalances() {
|
|
438
574
|
const provider = this.signer.provider;
|
|
439
|
-
const eoaAddr = await this.
|
|
575
|
+
const eoaAddr = await this._getSignerAddress();
|
|
440
576
|
const usdc = new import_ethers.Contract(this.config.contracts.usdc, ERC20_ABI, provider);
|
|
441
577
|
const ethBal = await provider.getBalance(eoaAddr);
|
|
442
578
|
const usdcBal = await usdc.balanceOf(eoaAddr);
|
|
579
|
+
const knownTokens = KNOWN_TOKENS[this.config.chainId] ?? {};
|
|
580
|
+
const eoaCollateral = {};
|
|
581
|
+
for (const [symbol, info] of Object.entries(knownTokens)) {
|
|
582
|
+
try {
|
|
583
|
+
const token = new import_ethers.Contract(info.address, ERC20_ABI, provider);
|
|
584
|
+
const bal = await token.balanceOf(eoaAddr);
|
|
585
|
+
eoaCollateral[symbol] = import_ethers.ethers.formatUnits(bal, info.decimals);
|
|
586
|
+
} catch (e) {
|
|
587
|
+
console.warn(`[agether] EOA collateral fetch failed for ${symbol}:`, e instanceof Error ? e.message : e);
|
|
588
|
+
eoaCollateral[symbol] = "0";
|
|
589
|
+
}
|
|
590
|
+
}
|
|
443
591
|
const result = {
|
|
444
|
-
|
|
592
|
+
agentId: this.agentId !== void 0 ? this.agentId.toString() : "?",
|
|
593
|
+
address: eoaAddr,
|
|
594
|
+
eth: import_ethers.ethers.formatEther(ethBal),
|
|
595
|
+
usdc: import_ethers.ethers.formatUnits(usdcBal, 6),
|
|
596
|
+
collateral: eoaCollateral
|
|
445
597
|
};
|
|
446
598
|
try {
|
|
447
599
|
const acctAddr = await this.getAccountAddress();
|
|
448
600
|
const acctEth = await provider.getBalance(acctAddr);
|
|
449
601
|
const acctUsdc = await usdc.balanceOf(acctAddr);
|
|
450
|
-
|
|
602
|
+
const acctCollateral = {};
|
|
603
|
+
for (const [symbol, info] of Object.entries(knownTokens)) {
|
|
604
|
+
try {
|
|
605
|
+
const token = new import_ethers.Contract(info.address, ERC20_ABI, provider);
|
|
606
|
+
const bal = await token.balanceOf(acctAddr);
|
|
607
|
+
acctCollateral[symbol] = import_ethers.ethers.formatUnits(bal, info.decimals);
|
|
608
|
+
} catch (e) {
|
|
609
|
+
console.warn(`[agether] Account collateral fetch failed for ${symbol}:`, e instanceof Error ? e.message : e);
|
|
610
|
+
acctCollateral[symbol] = "0";
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
result.agentAccount = {
|
|
451
614
|
address: acctAddr,
|
|
452
615
|
eth: import_ethers.ethers.formatEther(acctEth),
|
|
453
|
-
usdc: import_ethers.ethers.formatUnits(acctUsdc, 6)
|
|
616
|
+
usdc: import_ethers.ethers.formatUnits(acctUsdc, 6),
|
|
617
|
+
collateral: acctCollateral
|
|
454
618
|
};
|
|
455
619
|
} catch (e) {
|
|
456
|
-
|
|
620
|
+
if (e instanceof AgetherError && (e.code === "NO_ACCOUNT" || e.code === "NO_AGENT_ID")) {
|
|
621
|
+
} else {
|
|
622
|
+
console.warn("[agether] getBalances: failed to fetch Safe account data:", e.message ?? e);
|
|
623
|
+
}
|
|
457
624
|
}
|
|
458
625
|
return result;
|
|
459
626
|
}
|
|
@@ -467,6 +634,7 @@ var AgetherClient = class _AgetherClient {
|
|
|
467
634
|
const amount = import_ethers.ethers.parseUnits(usdcAmount, 6);
|
|
468
635
|
const tx = await usdc.transfer(acctAddr, amount);
|
|
469
636
|
const receipt = await tx.wait();
|
|
637
|
+
this._refreshSigner();
|
|
470
638
|
return {
|
|
471
639
|
txHash: receipt.hash,
|
|
472
640
|
blockNumber: receipt.blockNumber,
|
|
@@ -474,7 +642,89 @@ var AgetherClient = class _AgetherClient {
|
|
|
474
642
|
gasUsed: receipt.gasUsed
|
|
475
643
|
};
|
|
476
644
|
}
|
|
477
|
-
//
|
|
645
|
+
// ════════════════════════════════════════════════════════
|
|
646
|
+
// Withdrawals (Safe → EOA via UserOps)
|
|
647
|
+
// ════════════════════════════════════════════════════════
|
|
648
|
+
/**
|
|
649
|
+
* Withdraw an ERC-20 token from Safe account to EOA.
|
|
650
|
+
* Executes a transfer via Safe UserOp.
|
|
651
|
+
*
|
|
652
|
+
* @param tokenSymbol - Token to withdraw (e.g. 'USDC', 'WETH', 'wstETH') or 0x address
|
|
653
|
+
* @param amount - Amount to withdraw (human-readable, e.g. '100', or 'all')
|
|
654
|
+
*/
|
|
655
|
+
async withdrawToken(tokenSymbol, amount) {
|
|
656
|
+
const acctAddr = await this.getAccountAddress();
|
|
657
|
+
const eoaAddr = await this._getSignerAddress();
|
|
658
|
+
const tokenInfo = await this._resolveToken(tokenSymbol);
|
|
659
|
+
const tokenContract = new import_ethers.Contract(tokenInfo.address, ERC20_ABI, this.signer.provider);
|
|
660
|
+
let weiAmount;
|
|
661
|
+
if (amount === "all") {
|
|
662
|
+
weiAmount = await tokenContract.balanceOf(acctAddr);
|
|
663
|
+
if (weiAmount === 0n) {
|
|
664
|
+
throw new AgetherError(`No ${tokenInfo.symbol} in Safe account`, "INSUFFICIENT_BALANCE");
|
|
665
|
+
}
|
|
666
|
+
} else {
|
|
667
|
+
weiAmount = import_ethers.ethers.parseUnits(amount, tokenInfo.decimals);
|
|
668
|
+
}
|
|
669
|
+
const data = erc20Iface.encodeFunctionData("transfer", [eoaAddr, weiAmount]);
|
|
670
|
+
const receipt = await this._exec(tokenInfo.address, data);
|
|
671
|
+
const actualAmount = amount === "all" ? import_ethers.ethers.formatUnits(weiAmount, tokenInfo.decimals) : amount;
|
|
672
|
+
return { tx: receipt.hash, token: tokenInfo.symbol, amount: actualAmount, destination: eoaAddr };
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Withdraw ETH from Safe account to EOA.
|
|
676
|
+
* Executes a native ETH transfer via Safe UserOp.
|
|
677
|
+
*
|
|
678
|
+
* @param amount - ETH amount (e.g. '0.01' or 'all')
|
|
679
|
+
*/
|
|
680
|
+
async withdrawEth(amount) {
|
|
681
|
+
const acctAddr = await this.getAccountAddress();
|
|
682
|
+
const eoaAddr = await this._getSignerAddress();
|
|
683
|
+
let weiAmount;
|
|
684
|
+
if (amount === "all") {
|
|
685
|
+
weiAmount = await this.signer.provider.getBalance(acctAddr);
|
|
686
|
+
if (weiAmount === 0n) throw new AgetherError("No ETH in Safe account", "INSUFFICIENT_BALANCE");
|
|
687
|
+
} else {
|
|
688
|
+
weiAmount = import_ethers.ethers.parseEther(amount);
|
|
689
|
+
}
|
|
690
|
+
const receipt = await this._exec(eoaAddr, "0x", weiAmount);
|
|
691
|
+
const actualAmount = amount === "all" ? import_ethers.ethers.formatEther(weiAmount) : amount;
|
|
692
|
+
return { tx: receipt.hash, token: "ETH", amount: actualAmount, destination: eoaAddr };
|
|
693
|
+
}
|
|
694
|
+
// ════════════════════════════════════════════════════════
|
|
695
|
+
// Sponsorship
|
|
696
|
+
// ════════════════════════════════════════════════════════
|
|
697
|
+
/**
|
|
698
|
+
* Send tokens to another agent's Safe account (or any address).
|
|
699
|
+
* Transfers from EOA (does NOT require a UserOp).
|
|
700
|
+
*
|
|
701
|
+
* @param target - `{ agentId: '42' }` or `{ address: '0x...' }`
|
|
702
|
+
* @param tokenSymbol - Token to send (e.g. 'WETH', 'USDC') or 0x address
|
|
703
|
+
* @param amount - Amount to send (human-readable)
|
|
704
|
+
*/
|
|
705
|
+
async sponsor(target, tokenSymbol, amount) {
|
|
706
|
+
const tokenInfo = await this._resolveToken(tokenSymbol);
|
|
707
|
+
let targetAddr;
|
|
708
|
+
if (target.address) {
|
|
709
|
+
targetAddr = target.address;
|
|
710
|
+
} else if (target.agentId) {
|
|
711
|
+
targetAddr = await this.agether4337Factory.getAccount(BigInt(target.agentId));
|
|
712
|
+
if (targetAddr === import_ethers.ethers.ZeroAddress) {
|
|
713
|
+
throw new AgetherError("Target agent has no account", "NO_ACCOUNT");
|
|
714
|
+
}
|
|
715
|
+
} else {
|
|
716
|
+
throw new AgetherError("Provide agentId or address", "INVALID_TARGET");
|
|
717
|
+
}
|
|
718
|
+
const weiAmount = import_ethers.ethers.parseUnits(amount, tokenInfo.decimals);
|
|
719
|
+
const tokenContract = new import_ethers.Contract(tokenInfo.address, ERC20_ABI, this.signer);
|
|
720
|
+
const tx = await tokenContract.transfer(targetAddr, weiAmount);
|
|
721
|
+
const receipt = await tx.wait();
|
|
722
|
+
this._refreshSigner();
|
|
723
|
+
return { tx: receipt.hash, targetAccount: targetAddr, targetAgentId: target.agentId };
|
|
724
|
+
}
|
|
725
|
+
// ════════════════════════════════════════════════════════
|
|
726
|
+
// Identity & Validation
|
|
727
|
+
// ════════════════════════════════════════════════════════
|
|
478
728
|
/**
|
|
479
729
|
* Check if the KYA gate is active on the validation module.
|
|
480
730
|
* If validationRegistry is not set, all txs pass (KYA disabled).
|
|
@@ -499,31 +749,45 @@ var AgetherClient = class _AgetherClient {
|
|
|
499
749
|
return false;
|
|
500
750
|
}
|
|
501
751
|
}
|
|
752
|
+
/** Check whether the agent's ERC-8004 NFT exists. */
|
|
502
753
|
async identityExists() {
|
|
754
|
+
const id = this._requireAgentId();
|
|
503
755
|
try {
|
|
504
|
-
await this.identityRegistry.ownerOf(
|
|
756
|
+
await this.identityRegistry.ownerOf(id);
|
|
505
757
|
return true;
|
|
506
758
|
} catch (e) {
|
|
507
759
|
console.warn("[agether] identityExists check failed:", e instanceof Error ? e.message : e);
|
|
508
760
|
return false;
|
|
509
761
|
}
|
|
510
762
|
}
|
|
763
|
+
/** Get the owner address of the ERC-8004 NFT. */
|
|
511
764
|
async getIdentityOwner() {
|
|
512
|
-
|
|
765
|
+
const id = this._requireAgentId();
|
|
766
|
+
return this.identityRegistry.ownerOf(id);
|
|
513
767
|
}
|
|
514
|
-
//
|
|
768
|
+
// ════════════════════════════════════════════════════════
|
|
769
|
+
// Reputation
|
|
770
|
+
// ════════════════════════════════════════════════════════
|
|
771
|
+
/** Read the agent's current credit score from the Agether8004Scorer contract. */
|
|
515
772
|
async getCreditScore() {
|
|
516
|
-
|
|
773
|
+
const id = this._requireAgentId();
|
|
774
|
+
return this.agether8004Scorer.getCreditScore(id);
|
|
517
775
|
}
|
|
776
|
+
/** Check if the score is fresh (within MAX_ORACLE_AGE). */
|
|
518
777
|
async isScoreFresh() {
|
|
519
|
-
const
|
|
778
|
+
const id = this._requireAgentId();
|
|
779
|
+
const [fresh, age] = await this.agether8004Scorer.isScoreFresh(id);
|
|
520
780
|
return { fresh, age };
|
|
521
781
|
}
|
|
782
|
+
/** Check if the agent meets a minimum score threshold. */
|
|
522
783
|
async isEligible(minScore = 500n) {
|
|
523
|
-
const
|
|
784
|
+
const id = this._requireAgentId();
|
|
785
|
+
const [eligible, currentScore] = await this.agether8004Scorer.isEligible(id, minScore);
|
|
524
786
|
return { eligible, currentScore };
|
|
525
787
|
}
|
|
526
|
-
//
|
|
788
|
+
// ════════════════════════════════════════════════════════
|
|
789
|
+
// Getters
|
|
790
|
+
// ════════════════════════════════════════════════════════
|
|
527
791
|
get chainId() {
|
|
528
792
|
return this.config.chainId;
|
|
529
793
|
}
|
|
@@ -537,24 +801,209 @@ var AgetherClient = class _AgetherClient {
|
|
|
537
801
|
return this.signer;
|
|
538
802
|
}
|
|
539
803
|
getAgentId() {
|
|
804
|
+
return this._requireAgentId();
|
|
805
|
+
}
|
|
806
|
+
// ════════════════════════════════════════════════════════
|
|
807
|
+
// Private Helpers
|
|
808
|
+
// ════════════════════════════════════════════════════════
|
|
809
|
+
/** Require agentId to be set, throw a helpful error otherwise. */
|
|
810
|
+
_requireAgentId() {
|
|
811
|
+
if (this.agentId === void 0) {
|
|
812
|
+
throw new AgetherError("agentId not set. Call register() first.", "NO_AGENT_ID");
|
|
813
|
+
}
|
|
540
814
|
return this.agentId;
|
|
541
815
|
}
|
|
816
|
+
/** Resolve EOA signer address (async, cached). */
|
|
817
|
+
async _getSignerAddress() {
|
|
818
|
+
if (!this._eoaAddress) {
|
|
819
|
+
this._eoaAddress = await this.signer.getAddress();
|
|
820
|
+
}
|
|
821
|
+
return this._eoaAddress;
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Resolve a token symbol or address to { address, symbol, decimals }.
|
|
825
|
+
*
|
|
826
|
+
* Supports:
|
|
827
|
+
* - `'USDC'` → from chain config
|
|
828
|
+
* - Well-known symbols (`'WETH'`, `'wstETH'`, `'cbETH'`) → built-in per-chain registry
|
|
829
|
+
* - `'0x...'` address → reads decimals and symbol onchain
|
|
830
|
+
*/
|
|
831
|
+
async _resolveToken(symbolOrAddress) {
|
|
832
|
+
if (symbolOrAddress.toUpperCase() === "USDC") {
|
|
833
|
+
return { address: this.config.contracts.usdc, symbol: "USDC", decimals: 6 };
|
|
834
|
+
}
|
|
835
|
+
const chainTokens = KNOWN_TOKENS[this.config.chainId] ?? {};
|
|
836
|
+
const bySymbol = chainTokens[symbolOrAddress] || chainTokens[symbolOrAddress.toUpperCase()];
|
|
837
|
+
if (bySymbol) return bySymbol;
|
|
838
|
+
if (symbolOrAddress.startsWith("0x") && symbolOrAddress.length === 42) {
|
|
839
|
+
try {
|
|
840
|
+
const token = new import_ethers.Contract(
|
|
841
|
+
symbolOrAddress,
|
|
842
|
+
["function decimals() view returns (uint8)", "function symbol() view returns (string)"],
|
|
843
|
+
this.signer.provider
|
|
844
|
+
);
|
|
845
|
+
const [decimals, symbol] = await Promise.all([token.decimals(), token.symbol()]);
|
|
846
|
+
return { address: symbolOrAddress, symbol, decimals: Number(decimals) };
|
|
847
|
+
} catch (e) {
|
|
848
|
+
throw new AgetherError(
|
|
849
|
+
`Failed to read token at ${symbolOrAddress}: ${e instanceof Error ? e.message : e}`,
|
|
850
|
+
"UNKNOWN_TOKEN"
|
|
851
|
+
);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
throw new AgetherError(
|
|
855
|
+
`Unknown token: ${symbolOrAddress}. Use a known symbol (USDC, WETH, wstETH, cbETH) or a 0x address.`,
|
|
856
|
+
"UNKNOWN_TOKEN"
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Refresh signer and rebind contracts for fresh nonce.
|
|
861
|
+
*
|
|
862
|
+
* For the privateKey path: recreates provider + wallet.
|
|
863
|
+
* For external signers: just rebinds contract instances.
|
|
864
|
+
*/
|
|
865
|
+
_refreshSigner() {
|
|
866
|
+
if (!this._useExternalSigner && this._privateKey) {
|
|
867
|
+
const provider = new import_ethers.ethers.JsonRpcProvider(this._rpcUrl);
|
|
868
|
+
const wallet = new import_ethers.ethers.Wallet(this._privateKey, provider);
|
|
869
|
+
this.signer = wallet;
|
|
870
|
+
this._eoaAddress = wallet.address;
|
|
871
|
+
}
|
|
872
|
+
const c = this.config.contracts;
|
|
873
|
+
this.agether4337Factory = new import_ethers.Contract(c.agether4337Factory, AGETHER_4337_FACTORY_ABI, this.signer);
|
|
874
|
+
this.identityRegistry = new import_ethers.Contract(c.identityRegistry, IDENTITY_REGISTRY_ABI, this.signer);
|
|
875
|
+
this.agether8004Scorer = new import_ethers.Contract(c.agether8004Scorer, AGETHER_8004_SCORER_ABI, this.signer.provider);
|
|
876
|
+
this.validationModule = new import_ethers.Contract(c.erc8004ValidationModule, AGETHER_8004_VALIDATION_MODULE_ABI, this.signer.provider);
|
|
877
|
+
this.entryPoint = new import_ethers.Contract(c.entryPoint, ENTRYPOINT_V07_ABI, this.signer);
|
|
878
|
+
}
|
|
879
|
+
// ────────────────────────────────────────────────────────
|
|
880
|
+
// ERC-4337 UserOp helpers (Safe + Safe7579 + EntryPoint v0.7)
|
|
881
|
+
// ────────────────────────────────────────────────────────
|
|
882
|
+
/**
|
|
883
|
+
* Pack two uint128 values into a single bytes32:
|
|
884
|
+
* bytes32 = (hi << 128) | lo
|
|
885
|
+
*/
|
|
886
|
+
_packUint128(hi, lo) {
|
|
887
|
+
return import_ethers.ethers.zeroPadValue(import_ethers.ethers.toBeHex(hi << 128n | lo), 32);
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Build, sign and submit a PackedUserOperation through EntryPoint.handleOps.
|
|
891
|
+
*/
|
|
892
|
+
async _submitUserOp(callData) {
|
|
893
|
+
const sender = await this.getAccountAddress();
|
|
894
|
+
const eoaAddr = await this._getSignerAddress();
|
|
895
|
+
const validatorAddr = this.config.contracts.erc8004ValidationModule;
|
|
896
|
+
const nonceKey = BigInt(validatorAddr) << 32n;
|
|
897
|
+
const nonce = await this.entryPoint.getNonce(sender, nonceKey);
|
|
898
|
+
const feeData = await this.signer.provider.getFeeData();
|
|
899
|
+
const maxFeePerGas = feeData.maxFeePerGas ?? import_ethers.ethers.parseUnits("0.5", "gwei");
|
|
900
|
+
const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? import_ethers.ethers.parseUnits("0.1", "gwei");
|
|
901
|
+
const verificationGasLimit = 500000n;
|
|
902
|
+
const callGasLimit = 800000n;
|
|
903
|
+
const preVerificationGas = 100000n;
|
|
904
|
+
const accountGasLimits = this._packUint128(verificationGasLimit, callGasLimit);
|
|
905
|
+
const gasFees = this._packUint128(maxPriorityFeePerGas, maxFeePerGas);
|
|
906
|
+
const requiredPrefund = (verificationGasLimit + callGasLimit + preVerificationGas) * maxFeePerGas;
|
|
907
|
+
const accountBalance = await this.signer.provider.getBalance(sender);
|
|
908
|
+
if (accountBalance < requiredPrefund) {
|
|
909
|
+
const topUp = requiredPrefund - accountBalance;
|
|
910
|
+
const topUpWithBuffer = topUp * 120n / 100n;
|
|
911
|
+
const fundTx = await this.signer.sendTransaction({
|
|
912
|
+
to: sender,
|
|
913
|
+
value: topUpWithBuffer
|
|
914
|
+
});
|
|
915
|
+
await fundTx.wait();
|
|
916
|
+
this._refreshSigner();
|
|
917
|
+
}
|
|
918
|
+
const userOp = {
|
|
919
|
+
sender,
|
|
920
|
+
nonce,
|
|
921
|
+
initCode: "0x",
|
|
922
|
+
callData,
|
|
923
|
+
accountGasLimits,
|
|
924
|
+
preVerificationGas,
|
|
925
|
+
gasFees,
|
|
926
|
+
paymasterAndData: "0x",
|
|
927
|
+
signature: "0x"
|
|
928
|
+
};
|
|
929
|
+
const userOpHash = await this.entryPoint.getUserOpHash(userOp);
|
|
930
|
+
const signature = await this.signer.signMessage(import_ethers.ethers.getBytes(userOpHash));
|
|
931
|
+
userOp.signature = signature;
|
|
932
|
+
const tx = await this.entryPoint.handleOps([userOp], eoaAddr);
|
|
933
|
+
const receipt = await tx.wait();
|
|
934
|
+
this._refreshSigner();
|
|
935
|
+
const epIface = new import_ethers.ethers.Interface(ENTRYPOINT_V07_ABI);
|
|
936
|
+
for (const log of receipt.logs) {
|
|
937
|
+
try {
|
|
938
|
+
const parsed = epIface.parseLog({ topics: log.topics, data: log.data });
|
|
939
|
+
if (parsed?.name === "UserOperationEvent" && !parsed.args.success) {
|
|
940
|
+
let revertMsg = "UserOp inner execution reverted";
|
|
941
|
+
for (const rLog of receipt.logs) {
|
|
942
|
+
try {
|
|
943
|
+
const rParsed = epIface.parseLog({ topics: rLog.topics, data: rLog.data });
|
|
944
|
+
if (rParsed?.name === "UserOperationRevertReason") {
|
|
945
|
+
const reason = rParsed.args.revertReason;
|
|
946
|
+
try {
|
|
947
|
+
if (reason.length >= 10 && reason.slice(0, 10) === "0x08c379a0") {
|
|
948
|
+
const decoded = import_ethers.ethers.AbiCoder.defaultAbiCoder().decode(
|
|
949
|
+
["string"],
|
|
950
|
+
"0x" + reason.slice(10)
|
|
951
|
+
);
|
|
952
|
+
revertMsg = `UserOp reverted: ${decoded[0]}`;
|
|
953
|
+
} else {
|
|
954
|
+
revertMsg = `UserOp reverted with data: ${reason}`;
|
|
955
|
+
}
|
|
956
|
+
} catch {
|
|
957
|
+
revertMsg = `UserOp reverted with data: ${reason}`;
|
|
958
|
+
}
|
|
959
|
+
break;
|
|
960
|
+
}
|
|
961
|
+
} catch {
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
throw new AgetherError(revertMsg, "USEROP_EXECUTION_FAILED");
|
|
966
|
+
}
|
|
967
|
+
} catch (e) {
|
|
968
|
+
if (e instanceof AgetherError) throw e;
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
return receipt;
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* Execute a single call via Safe7579 account (ERC-7579 single mode)
|
|
976
|
+
* through an ERC-4337 UserOperation.
|
|
977
|
+
*/
|
|
978
|
+
async _exec(target, data, value = 0n) {
|
|
979
|
+
const valueHex = import_ethers.ethers.zeroPadValue(import_ethers.ethers.toBeHex(value), 32);
|
|
980
|
+
const executionCalldata = import_ethers.ethers.concat([target, valueHex, data]);
|
|
981
|
+
const safe7579Iface = new import_ethers.ethers.Interface(SAFE7579_ACCOUNT_ABI);
|
|
982
|
+
const callData = safe7579Iface.encodeFunctionData("execute", [MODE_SINGLE, executionCalldata]);
|
|
983
|
+
return this._submitUserOp(callData);
|
|
984
|
+
}
|
|
542
985
|
};
|
|
543
986
|
|
|
544
987
|
// src/clients/MorphoClient.ts
|
|
545
988
|
var import_ethers2 = require("ethers");
|
|
546
989
|
var import_axios = __toESM(require("axios"));
|
|
547
990
|
var MORPHO_API_URL = "https://api.morpho.org/graphql";
|
|
548
|
-
var
|
|
991
|
+
var MODE_SINGLE2 = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
549
992
|
var MODE_BATCH = "0x0100000000000000000000000000000000000000000000000000000000000000";
|
|
550
993
|
var morphoIface = new import_ethers2.ethers.Interface(MORPHO_BLUE_ABI);
|
|
551
|
-
var
|
|
994
|
+
var erc20Iface2 = new import_ethers2.ethers.Interface(ERC20_ABI);
|
|
552
995
|
var MorphoClient = class {
|
|
553
996
|
constructor(config) {
|
|
554
997
|
this._marketCache = /* @__PURE__ */ new Map();
|
|
555
998
|
/** Dynamic token registry: symbol (uppercase) → { address, symbol, decimals } */
|
|
556
999
|
this._tokenCache = /* @__PURE__ */ new Map();
|
|
557
1000
|
this._discoveredAt = 0;
|
|
1001
|
+
if (!config.agentId) {
|
|
1002
|
+
throw new AgetherError(
|
|
1003
|
+
"agentId is required. Use AgetherClient.register() first to get an agentId.",
|
|
1004
|
+
"NO_AGENT_ID"
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
558
1007
|
const chainId = config.chainId ?? 1 /* Ethereum */;
|
|
559
1008
|
const defaultCfg = getDefaultConfig(chainId);
|
|
560
1009
|
this.config = defaultCfg;
|
|
@@ -584,26 +1033,7 @@ var MorphoClient = class {
|
|
|
584
1033
|
const addrs = { ...defaultCfg.contracts, ...config.contracts };
|
|
585
1034
|
this.agether4337Factory = new import_ethers2.Contract(addrs.agether4337Factory, ACCOUNT_FACTORY_ABI, this._signer);
|
|
586
1035
|
this.morphoBlue = new import_ethers2.Contract(addrs.morphoBlue, MORPHO_BLUE_ABI, this.provider);
|
|
587
|
-
this.agether8004Scorer = new import_ethers2.Contract(addrs.agether8004Scorer, AGENT_REPUTATION_ABI, this._signer);
|
|
588
|
-
this.identityRegistry = new import_ethers2.Contract(addrs.identityRegistry, IDENTITY_REGISTRY_ABI, this._signer);
|
|
589
1036
|
this.entryPoint = new import_ethers2.Contract(addrs.entryPoint, ENTRYPOINT_V07_ABI, this._signer);
|
|
590
|
-
this.validationModule = new import_ethers2.Contract(addrs.erc8004ValidationModule, ERC8004_VALIDATION_MODULE_ABI, this.provider);
|
|
591
|
-
}
|
|
592
|
-
// ════════════════════════════════════════════════════════
|
|
593
|
-
// KYA Gate Check
|
|
594
|
-
// ════════════════════════════════════════════════════════
|
|
595
|
-
/**
|
|
596
|
-
* Check whether the KYA (Know Your Agent) code verification gate is active.
|
|
597
|
-
* Reads the ERC8004ValidationModule's validationRegistry — when set to
|
|
598
|
-
* a non-zero address, the module enforces KYA code approval.
|
|
599
|
-
*/
|
|
600
|
-
async isKyaRequired() {
|
|
601
|
-
try {
|
|
602
|
-
const registryAddr = await this.validationModule.validationRegistry();
|
|
603
|
-
return registryAddr !== import_ethers2.ethers.ZeroAddress;
|
|
604
|
-
} catch {
|
|
605
|
-
return false;
|
|
606
|
-
}
|
|
607
1037
|
}
|
|
608
1038
|
// ════════════════════════════════════════════════════════
|
|
609
1039
|
// Account Management
|
|
@@ -611,7 +1041,6 @@ var MorphoClient = class {
|
|
|
611
1041
|
/** Resolve the AgentAccount address (cached, with retry for flaky RPCs). */
|
|
612
1042
|
async getAccountAddress() {
|
|
613
1043
|
if (this._accountAddress) return this._accountAddress;
|
|
614
|
-
if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
|
|
615
1044
|
const MAX_RETRIES = 3;
|
|
616
1045
|
let lastErr;
|
|
617
1046
|
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
@@ -633,7 +1062,6 @@ var MorphoClient = class {
|
|
|
633
1062
|
throw lastErr;
|
|
634
1063
|
}
|
|
635
1064
|
getAgentId() {
|
|
636
|
-
if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
|
|
637
1065
|
return this.agentId;
|
|
638
1066
|
}
|
|
639
1067
|
/**
|
|
@@ -666,182 +1094,6 @@ var MorphoClient = class {
|
|
|
666
1094
|
}
|
|
667
1095
|
return this._eoaAddress;
|
|
668
1096
|
}
|
|
669
|
-
/** Mint a new ERC-8004 identity and return the agentId. */
|
|
670
|
-
async _mintNewIdentity() {
|
|
671
|
-
const regTx = await this.identityRegistry.register();
|
|
672
|
-
const regReceipt = await regTx.wait();
|
|
673
|
-
this._refreshSigner();
|
|
674
|
-
let agentId = 0n;
|
|
675
|
-
for (const log of regReceipt.logs) {
|
|
676
|
-
try {
|
|
677
|
-
const parsed = this.identityRegistry.interface.parseLog({ topics: log.topics, data: log.data });
|
|
678
|
-
if (parsed?.name === "Transfer") {
|
|
679
|
-
agentId = parsed.args[2];
|
|
680
|
-
break;
|
|
681
|
-
}
|
|
682
|
-
} catch (e) {
|
|
683
|
-
console.warn("[agether] parseLog skip:", e instanceof Error ? e.message : e);
|
|
684
|
-
continue;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
if (agentId === 0n) throw new AgetherError("Failed to parse agentId from registration", "PARSE_ERROR");
|
|
688
|
-
return agentId;
|
|
689
|
-
}
|
|
690
|
-
/**
|
|
691
|
-
* Register: create ERC-8004 identity + AgentAccount in one flow.
|
|
692
|
-
* If already registered, returns existing state.
|
|
693
|
-
*/
|
|
694
|
-
async register(_name) {
|
|
695
|
-
const eoaAddr = await this.getSignerAddress();
|
|
696
|
-
if (this.agentId) {
|
|
697
|
-
const exists = await this.agether4337Factory.accountExists(BigInt(this.agentId));
|
|
698
|
-
if (exists) {
|
|
699
|
-
const acct = await this.agether4337Factory.getAccount(BigInt(this.agentId));
|
|
700
|
-
this._accountAddress = acct;
|
|
701
|
-
const kyaRequired2 = await this.isKyaRequired();
|
|
702
|
-
return { agentId: this.agentId, address: eoaAddr, agentAccount: acct, alreadyRegistered: true, kyaRequired: kyaRequired2 };
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
let agentId;
|
|
706
|
-
if (this.agentId) {
|
|
707
|
-
const balance = await this.identityRegistry.balanceOf(eoaAddr);
|
|
708
|
-
if (balance > 0n) {
|
|
709
|
-
agentId = BigInt(this.agentId);
|
|
710
|
-
} else {
|
|
711
|
-
agentId = await this._mintNewIdentity();
|
|
712
|
-
}
|
|
713
|
-
} else {
|
|
714
|
-
agentId = await this._mintNewIdentity();
|
|
715
|
-
}
|
|
716
|
-
this.agentId = agentId.toString();
|
|
717
|
-
const acctExists = await this.agether4337Factory.accountExists(agentId);
|
|
718
|
-
let txHash;
|
|
719
|
-
if (!acctExists) {
|
|
720
|
-
const tx = await this.agether4337Factory.createAccount(agentId);
|
|
721
|
-
const receipt = await tx.wait();
|
|
722
|
-
this._refreshSigner();
|
|
723
|
-
txHash = receipt.hash;
|
|
724
|
-
}
|
|
725
|
-
const acctAddr = await this.agether4337Factory.getAccount(agentId);
|
|
726
|
-
this._accountAddress = acctAddr;
|
|
727
|
-
const kyaRequired = await this.isKyaRequired();
|
|
728
|
-
return {
|
|
729
|
-
agentId: this.agentId,
|
|
730
|
-
address: eoaAddr,
|
|
731
|
-
agentAccount: acctAddr,
|
|
732
|
-
alreadyRegistered: acctExists,
|
|
733
|
-
kyaRequired,
|
|
734
|
-
tx: txHash
|
|
735
|
-
};
|
|
736
|
-
}
|
|
737
|
-
/** Get ETH / USDC / collateral balances for EOA and AgentAccount. */
|
|
738
|
-
async getBalances() {
|
|
739
|
-
const eoaAddr = await this.getSignerAddress();
|
|
740
|
-
const usdc = new import_ethers2.Contract(this.config.contracts.usdc, ERC20_ABI, this.provider);
|
|
741
|
-
const ethBal = await this.provider.getBalance(eoaAddr);
|
|
742
|
-
const usdcBal = await usdc.balanceOf(eoaAddr);
|
|
743
|
-
const discoveredTokens = await this._getDiscoveredTokens();
|
|
744
|
-
const eoaCollateral = {};
|
|
745
|
-
for (const info of discoveredTokens) {
|
|
746
|
-
try {
|
|
747
|
-
const token = new import_ethers2.Contract(info.address, ERC20_ABI, this.provider);
|
|
748
|
-
const bal = await token.balanceOf(eoaAddr);
|
|
749
|
-
eoaCollateral[info.symbol] = import_ethers2.ethers.formatUnits(bal, info.decimals);
|
|
750
|
-
} catch (e) {
|
|
751
|
-
console.warn(`[agether] EOA collateral fetch failed for ${info.symbol}:`, e instanceof Error ? e.message : e);
|
|
752
|
-
eoaCollateral[info.symbol] = "0";
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
const result = {
|
|
756
|
-
agentId: this.agentId || "?",
|
|
757
|
-
address: eoaAddr,
|
|
758
|
-
eth: import_ethers2.ethers.formatEther(ethBal),
|
|
759
|
-
usdc: import_ethers2.ethers.formatUnits(usdcBal, 6),
|
|
760
|
-
collateral: eoaCollateral
|
|
761
|
-
};
|
|
762
|
-
try {
|
|
763
|
-
const acctAddr = await this.getAccountAddress();
|
|
764
|
-
const acctEth = await this.provider.getBalance(acctAddr);
|
|
765
|
-
const acctUsdc = await usdc.balanceOf(acctAddr);
|
|
766
|
-
const acctCollateral = {};
|
|
767
|
-
for (const info of discoveredTokens) {
|
|
768
|
-
try {
|
|
769
|
-
const token = new import_ethers2.Contract(info.address, ERC20_ABI, this.provider);
|
|
770
|
-
const bal = await token.balanceOf(acctAddr);
|
|
771
|
-
acctCollateral[info.symbol] = import_ethers2.ethers.formatUnits(bal, info.decimals);
|
|
772
|
-
} catch (e) {
|
|
773
|
-
console.warn(`[agether] AgentAccount collateral fetch failed for ${info.symbol}:`, e instanceof Error ? e.message : e);
|
|
774
|
-
acctCollateral[info.symbol] = "0";
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
result.agentAccount = {
|
|
778
|
-
address: acctAddr,
|
|
779
|
-
eth: import_ethers2.ethers.formatEther(acctEth),
|
|
780
|
-
usdc: import_ethers2.ethers.formatUnits(acctUsdc, 6),
|
|
781
|
-
collateral: acctCollateral
|
|
782
|
-
};
|
|
783
|
-
} catch (err) {
|
|
784
|
-
if (err instanceof AgetherError && err.code === "NO_ACCOUNT") {
|
|
785
|
-
} else {
|
|
786
|
-
console.warn("[agether] getBalances: failed to fetch AgentAccount data:", err.message ?? err);
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
return result;
|
|
790
|
-
}
|
|
791
|
-
/** Transfer USDC from EOA to AgentAccount. */
|
|
792
|
-
async fundAccount(usdcAmount) {
|
|
793
|
-
const acctAddr = await this.getAccountAddress();
|
|
794
|
-
const usdc = new import_ethers2.Contract(this.config.contracts.usdc, ERC20_ABI, this._signer);
|
|
795
|
-
const amount = import_ethers2.ethers.parseUnits(usdcAmount, 6);
|
|
796
|
-
const tx = await usdc.transfer(acctAddr, amount);
|
|
797
|
-
const receipt = await tx.wait();
|
|
798
|
-
this._refreshSigner();
|
|
799
|
-
return { tx: receipt.hash, amount: usdcAmount, agentAccount: acctAddr };
|
|
800
|
-
}
|
|
801
|
-
/**
|
|
802
|
-
* Withdraw (transfer) a token from AgentAccount to EOA.
|
|
803
|
-
* Executes an ERC-20 transfer via Safe UserOp.
|
|
804
|
-
*
|
|
805
|
-
* @param tokenSymbol - Token to withdraw (e.g. 'USDC', 'WETH', 'wstETH')
|
|
806
|
-
* @param amount - Amount to withdraw (human-readable, e.g. '100' for 100 USDC, or 'all')
|
|
807
|
-
*/
|
|
808
|
-
async withdrawToken(tokenSymbol, amount) {
|
|
809
|
-
const acctAddr = await this.getAccountAddress();
|
|
810
|
-
const eoaAddr = await this.getSignerAddress();
|
|
811
|
-
const tokenInfo = await this._resolveToken(tokenSymbol);
|
|
812
|
-
const tokenContract = new import_ethers2.Contract(tokenInfo.address, ERC20_ABI, this.provider);
|
|
813
|
-
let weiAmount;
|
|
814
|
-
if (amount === "all") {
|
|
815
|
-
weiAmount = await tokenContract.balanceOf(acctAddr);
|
|
816
|
-
if (weiAmount === 0n) throw new AgetherError(`No ${tokenSymbol} in AgentAccount`, "INSUFFICIENT_BALANCE");
|
|
817
|
-
} else {
|
|
818
|
-
weiAmount = import_ethers2.ethers.parseUnits(amount, tokenInfo.decimals);
|
|
819
|
-
}
|
|
820
|
-
const data = erc20Iface.encodeFunctionData("transfer", [eoaAddr, weiAmount]);
|
|
821
|
-
const receipt = await this.exec(tokenInfo.address, data);
|
|
822
|
-
const actualAmount = amount === "all" ? import_ethers2.ethers.formatUnits(weiAmount, tokenInfo.decimals) : amount;
|
|
823
|
-
return { tx: receipt.hash, token: tokenSymbol, amount: actualAmount, destination: eoaAddr };
|
|
824
|
-
}
|
|
825
|
-
/**
|
|
826
|
-
* Withdraw ETH from AgentAccount to EOA.
|
|
827
|
-
* Executes a native ETH transfer via Safe UserOp.
|
|
828
|
-
*
|
|
829
|
-
* @param amount - ETH amount (e.g. '0.01' or 'all')
|
|
830
|
-
*/
|
|
831
|
-
async withdrawEth(amount) {
|
|
832
|
-
const acctAddr = await this.getAccountAddress();
|
|
833
|
-
const eoaAddr = await this.getSignerAddress();
|
|
834
|
-
let weiAmount;
|
|
835
|
-
if (amount === "all") {
|
|
836
|
-
weiAmount = await this.provider.getBalance(acctAddr);
|
|
837
|
-
if (weiAmount === 0n) throw new AgetherError("No ETH in AgentAccount", "INSUFFICIENT_BALANCE");
|
|
838
|
-
} else {
|
|
839
|
-
weiAmount = import_ethers2.ethers.parseEther(amount);
|
|
840
|
-
}
|
|
841
|
-
const receipt = await this.exec(eoaAddr, "0x", weiAmount);
|
|
842
|
-
const actualAmount = amount === "all" ? import_ethers2.ethers.formatEther(weiAmount) : amount;
|
|
843
|
-
return { tx: receipt.hash, token: "ETH", amount: actualAmount, destination: eoaAddr };
|
|
844
|
-
}
|
|
845
1097
|
// ════════════════════════════════════════════════════════
|
|
846
1098
|
// Market Discovery (Morpho GraphQL API)
|
|
847
1099
|
// ════════════════════════════════════════════════════════
|
|
@@ -1020,7 +1272,7 @@ var MorphoClient = class {
|
|
|
1020
1272
|
}
|
|
1021
1273
|
}
|
|
1022
1274
|
return {
|
|
1023
|
-
agentId: this.agentId
|
|
1275
|
+
agentId: this.agentId,
|
|
1024
1276
|
agentAccount: acctAddr,
|
|
1025
1277
|
totalDebt: import_ethers2.ethers.formatUnits(totalDebt, 6),
|
|
1026
1278
|
positions
|
|
@@ -1266,7 +1518,7 @@ var MorphoClient = class {
|
|
|
1266
1518
|
const targets = [usdcAddr, morphoAddr];
|
|
1267
1519
|
const values = [0n, 0n];
|
|
1268
1520
|
const datas = [
|
|
1269
|
-
|
|
1521
|
+
erc20Iface2.encodeFunctionData("approve", [morphoAddr, amount]),
|
|
1270
1522
|
morphoIface.encodeFunctionData("supply", [
|
|
1271
1523
|
this._toTuple(params),
|
|
1272
1524
|
amount,
|
|
@@ -1498,7 +1750,7 @@ var MorphoClient = class {
|
|
|
1498
1750
|
const targets = [colInfo.address, morphoAddr];
|
|
1499
1751
|
const values = [0n, 0n];
|
|
1500
1752
|
const datas = [
|
|
1501
|
-
|
|
1753
|
+
erc20Iface2.encodeFunctionData("approve", [morphoAddr, weiAmount]),
|
|
1502
1754
|
morphoIface.encodeFunctionData("supplyCollateral", [
|
|
1503
1755
|
this._toTuple(params),
|
|
1504
1756
|
weiAmount,
|
|
@@ -1649,7 +1901,7 @@ var MorphoClient = class {
|
|
|
1649
1901
|
const targets = [colInfo.address, morphoAddr, morphoAddr];
|
|
1650
1902
|
const values = [0n, 0n, 0n];
|
|
1651
1903
|
const datas = [
|
|
1652
|
-
|
|
1904
|
+
erc20Iface2.encodeFunctionData("approve", [morphoAddr, colWei]),
|
|
1653
1905
|
morphoIface.encodeFunctionData("supplyCollateral", [
|
|
1654
1906
|
this._toTuple(params),
|
|
1655
1907
|
colWei,
|
|
@@ -1737,7 +1989,7 @@ var MorphoClient = class {
|
|
|
1737
1989
|
const targets = [usdcAddr, morphoAddr];
|
|
1738
1990
|
const values = [0n, 0n];
|
|
1739
1991
|
const datas = [
|
|
1740
|
-
|
|
1992
|
+
erc20Iface2.encodeFunctionData("approve", [morphoAddr, approveAmount]),
|
|
1741
1993
|
morphoIface.encodeFunctionData("repay", [
|
|
1742
1994
|
this._toTuple(params),
|
|
1743
1995
|
repayAssets,
|
|
@@ -1826,7 +2078,7 @@ var MorphoClient = class {
|
|
|
1826
2078
|
const targets = [usdcAddr, morphoAddr, morphoAddr];
|
|
1827
2079
|
const values = [0n, 0n, 0n];
|
|
1828
2080
|
const datas = [
|
|
1829
|
-
|
|
2081
|
+
erc20Iface2.encodeFunctionData("approve", [morphoAddr, dustApproveAmount]),
|
|
1830
2082
|
morphoIface.encodeFunctionData("repay", [
|
|
1831
2083
|
this._toTuple(params),
|
|
1832
2084
|
0n,
|
|
@@ -1857,50 +2109,6 @@ var MorphoClient = class {
|
|
|
1857
2109
|
destination: dest
|
|
1858
2110
|
};
|
|
1859
2111
|
}
|
|
1860
|
-
/**
|
|
1861
|
-
* Sponsor: transfer collateral to another agent's AgentAccount.
|
|
1862
|
-
* (The agent must then supplyCollateral themselves via their own account.)
|
|
1863
|
-
*/
|
|
1864
|
-
async sponsor(target, tokenSymbol, amount) {
|
|
1865
|
-
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1866
|
-
let targetAddr;
|
|
1867
|
-
if (target.address) {
|
|
1868
|
-
targetAddr = target.address;
|
|
1869
|
-
} else if (target.agentId) {
|
|
1870
|
-
targetAddr = await this.agether4337Factory.getAccount(BigInt(target.agentId));
|
|
1871
|
-
if (targetAddr === import_ethers2.ethers.ZeroAddress) throw new AgetherError("Target agent has no account", "NO_ACCOUNT");
|
|
1872
|
-
} else {
|
|
1873
|
-
throw new AgetherError("Provide agentId or address", "INVALID_TARGET");
|
|
1874
|
-
}
|
|
1875
|
-
const weiAmount = import_ethers2.ethers.parseUnits(amount, colInfo.decimals);
|
|
1876
|
-
const colToken = new import_ethers2.Contract(colInfo.address, ERC20_ABI, this._signer);
|
|
1877
|
-
const tx = await colToken.transfer(targetAddr, weiAmount);
|
|
1878
|
-
const receipt = await tx.wait();
|
|
1879
|
-
this._refreshSigner();
|
|
1880
|
-
return { tx: receipt.hash, targetAccount: targetAddr, targetAgentId: target.agentId };
|
|
1881
|
-
}
|
|
1882
|
-
// ════════════════════════════════════════════════════════
|
|
1883
|
-
// Reputation (Agether8004Scorer contract)
|
|
1884
|
-
// ════════════════════════════════════════════════════════
|
|
1885
|
-
async getCreditScore() {
|
|
1886
|
-
if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
|
|
1887
|
-
return this.agether8004Scorer.getCreditScore(BigInt(this.agentId));
|
|
1888
|
-
}
|
|
1889
|
-
async getAttestation() {
|
|
1890
|
-
if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
|
|
1891
|
-
const att = await this.agether8004Scorer.getAttestation(BigInt(this.agentId));
|
|
1892
|
-
return { score: att.score, timestamp: att.timestamp, signer: att.signer };
|
|
1893
|
-
}
|
|
1894
|
-
async isEligible(minScore = 500n) {
|
|
1895
|
-
if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
|
|
1896
|
-
const [eligible, currentScore] = await this.agether8004Scorer.isEligible(BigInt(this.agentId), minScore);
|
|
1897
|
-
return { eligible, currentScore };
|
|
1898
|
-
}
|
|
1899
|
-
async isScoreFresh() {
|
|
1900
|
-
if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
|
|
1901
|
-
const [fresh, age] = await this.agether8004Scorer.isScoreFresh(BigInt(this.agentId));
|
|
1902
|
-
return { fresh, age };
|
|
1903
|
-
}
|
|
1904
2112
|
// ════════════════════════════════════════════════════════
|
|
1905
2113
|
// Internal Helpers
|
|
1906
2114
|
// ════════════════════════════════════════════════════════
|
|
@@ -1922,9 +2130,6 @@ var MorphoClient = class {
|
|
|
1922
2130
|
const addrs = this.config.contracts;
|
|
1923
2131
|
this.agether4337Factory = new import_ethers2.Contract(addrs.agether4337Factory, ACCOUNT_FACTORY_ABI, this._signer);
|
|
1924
2132
|
this.entryPoint = new import_ethers2.Contract(addrs.entryPoint, ENTRYPOINT_V07_ABI, this._signer);
|
|
1925
|
-
this.validationModule = new import_ethers2.Contract(addrs.erc8004ValidationModule, ERC8004_VALIDATION_MODULE_ABI, this.provider);
|
|
1926
|
-
this.agether8004Scorer = new import_ethers2.Contract(addrs.agether8004Scorer, AGENT_REPUTATION_ABI, this._signer);
|
|
1927
|
-
this.identityRegistry = new import_ethers2.Contract(addrs.identityRegistry, IDENTITY_REGISTRY_ABI, this._signer);
|
|
1928
2133
|
} else {
|
|
1929
2134
|
this.provider = new import_ethers2.ethers.JsonRpcProvider(this._rpcUrl);
|
|
1930
2135
|
const wallet = new import_ethers2.ethers.Wallet(this._privateKey, this.provider);
|
|
@@ -1933,9 +2138,6 @@ var MorphoClient = class {
|
|
|
1933
2138
|
const addrs = this.config.contracts;
|
|
1934
2139
|
this.agether4337Factory = new import_ethers2.Contract(addrs.agether4337Factory, ACCOUNT_FACTORY_ABI, this._signer);
|
|
1935
2140
|
this.entryPoint = new import_ethers2.Contract(addrs.entryPoint, ENTRYPOINT_V07_ABI, this._signer);
|
|
1936
|
-
this.validationModule = new import_ethers2.Contract(addrs.erc8004ValidationModule, ERC8004_VALIDATION_MODULE_ABI, this.provider);
|
|
1937
|
-
this.agether8004Scorer = new import_ethers2.Contract(addrs.agether8004Scorer, AGENT_REPUTATION_ABI, this._signer);
|
|
1938
|
-
this.identityRegistry = new import_ethers2.Contract(addrs.identityRegistry, IDENTITY_REGISTRY_ABI, this._signer);
|
|
1939
2141
|
}
|
|
1940
2142
|
}
|
|
1941
2143
|
// ────────────────────────────────────────────────────────────
|
|
@@ -2042,7 +2244,7 @@ var MorphoClient = class {
|
|
|
2042
2244
|
const valueHex = import_ethers2.ethers.zeroPadValue(import_ethers2.ethers.toBeHex(value), 32);
|
|
2043
2245
|
const executionCalldata = import_ethers2.ethers.concat([target, valueHex, data]);
|
|
2044
2246
|
const safe7579Iface = new import_ethers2.ethers.Interface(SAFE7579_ACCOUNT_ABI);
|
|
2045
|
-
const callData = safe7579Iface.encodeFunctionData("execute", [
|
|
2247
|
+
const callData = safe7579Iface.encodeFunctionData("execute", [MODE_SINGLE2, executionCalldata]);
|
|
2046
2248
|
return this._submitUserOp(callData);
|
|
2047
2249
|
}
|
|
2048
2250
|
/**
|
|
@@ -2138,23 +2340,6 @@ var MorphoClient = class {
|
|
|
2138
2340
|
"UNKNOWN_COLLATERAL"
|
|
2139
2341
|
);
|
|
2140
2342
|
}
|
|
2141
|
-
/**
|
|
2142
|
-
* Get all discovered collateral tokens (for balance iteration, etc.).
|
|
2143
|
-
* Returns unique tokens from the Morpho API market discovery.
|
|
2144
|
-
*/
|
|
2145
|
-
async _getDiscoveredTokens() {
|
|
2146
|
-
await this.getMarkets();
|
|
2147
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2148
|
-
const tokens = [];
|
|
2149
|
-
for (const [key, info] of this._tokenCache) {
|
|
2150
|
-
if (key.startsWith("0x")) continue;
|
|
2151
|
-
if (seen.has(info.address.toLowerCase())) continue;
|
|
2152
|
-
seen.add(info.address.toLowerCase());
|
|
2153
|
-
if (info.symbol === "USDC" || info.symbol === "USDbC") continue;
|
|
2154
|
-
tokens.push(info);
|
|
2155
|
-
}
|
|
2156
|
-
return tokens;
|
|
2157
|
-
}
|
|
2158
2343
|
/**
|
|
2159
2344
|
* Compute net deposited amounts per market using Morpho GraphQL API.
|
|
2160
2345
|
*
|