@agether/sdk 1.11.1 → 2.0.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 +2375 -0
- package/dist/index.d.mts +77 -32
- package/dist/index.d.ts +77 -32
- package/dist/index.js +304 -169
- package/dist/index.mjs +299 -167
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -55,38 +55,41 @@ var IDENTITY_REGISTRY_ABI = [
|
|
|
55
55
|
"function register() returns (uint256 agentId)",
|
|
56
56
|
"event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"
|
|
57
57
|
];
|
|
58
|
-
var
|
|
58
|
+
var SAFE_AGENT_FACTORY_ABI = [
|
|
59
59
|
"function getAccount(uint256 agentId) view returns (address)",
|
|
60
60
|
"function accountExists(uint256 agentId) view returns (bool)",
|
|
61
|
-
"function predictAddress(uint256 agentId) view returns (address)",
|
|
62
61
|
"function totalAccounts() view returns (uint256)",
|
|
63
62
|
"function getAgentId(address account) view returns (uint256)",
|
|
64
|
-
"function
|
|
65
|
-
"function
|
|
63
|
+
"function getAgentIdByIndex(uint256 index) view returns (uint256)",
|
|
64
|
+
"function getAllAgentIds() view returns (uint256[])",
|
|
65
|
+
"function createAccount(uint256 agentId) returns (address safeAccount)",
|
|
66
66
|
"function identityRegistry() view returns (address)",
|
|
67
|
-
"
|
|
67
|
+
"function validationModule() view returns (address)",
|
|
68
|
+
"function hookMultiplexer() view returns (address)",
|
|
69
|
+
"function safeSingleton() view returns (address)",
|
|
70
|
+
"function safe7579() view returns (address)",
|
|
71
|
+
"function bootstrap() view returns (address)",
|
|
72
|
+
"function SENTINEL_OWNER() view returns (address)",
|
|
73
|
+
"event AccountCreated(uint256 indexed agentId, address indexed safeAccount, address indexed owner)"
|
|
68
74
|
];
|
|
69
|
-
var
|
|
70
|
-
|
|
75
|
+
var ACCOUNT_FACTORY_ABI = SAFE_AGENT_FACTORY_ABI;
|
|
76
|
+
var ERC8004_VALIDATION_MODULE_ABI = [
|
|
77
|
+
// View
|
|
78
|
+
"function getConfig(address account) view returns (address registry, uint256 agentId)",
|
|
79
|
+
"function getOwner(address account) view returns (address)",
|
|
80
|
+
"function isInstalled(address account) view returns (bool)",
|
|
81
|
+
"function isKYAApproved(address account) view returns (bool)",
|
|
82
|
+
"function validationRegistry() view returns (address)",
|
|
71
83
|
"function owner() view returns (address)",
|
|
72
|
-
|
|
73
|
-
"function
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
"function
|
|
78
|
-
"function
|
|
79
|
-
|
|
80
|
-
"function
|
|
81
|
-
"function uninstallModule(uint256 moduleTypeId, address module, bytes deInitData) payable",
|
|
82
|
-
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)",
|
|
83
|
-
"function hook() view returns (address)",
|
|
84
|
-
// Funding (direct, no execution needed)
|
|
85
|
-
"function fund(address token, uint256 amount)",
|
|
86
|
-
"function withdraw(address token, uint256 amount, address to)",
|
|
87
|
-
"function withdrawETH(uint256 amount, address to)",
|
|
88
|
-
// EIP-1271
|
|
89
|
-
"function isValidSignature(bytes32 hash, bytes signature) view returns (bytes4)"
|
|
84
|
+
// Admin (via TimelockController)
|
|
85
|
+
"function setValidationRegistry(address registry_)"
|
|
86
|
+
];
|
|
87
|
+
var HOOK_MULTIPLEXER_ABI = [
|
|
88
|
+
"function getHooks() view returns (address[])",
|
|
89
|
+
"function hookCount() view returns (uint256)",
|
|
90
|
+
"function owner() view returns (address)",
|
|
91
|
+
"function addHook(address hook)",
|
|
92
|
+
"function removeHook(address hook)"
|
|
90
93
|
];
|
|
91
94
|
var AGENT_REPUTATION_ABI = [
|
|
92
95
|
"function getCreditScore(uint256 agentId) view returns (uint256)",
|
|
@@ -132,57 +135,113 @@ var ERC20_ABI = [
|
|
|
132
135
|
"function symbol() view returns (string)",
|
|
133
136
|
"function name() view returns (string)"
|
|
134
137
|
];
|
|
135
|
-
var
|
|
136
|
-
"function
|
|
137
|
-
"function
|
|
138
|
+
var ENTRYPOINT_V07_ABI = [
|
|
139
|
+
"function handleOps(tuple(address sender, uint256 nonce, bytes initCode, bytes callData, bytes32 accountGasLimits, uint256 preVerificationGas, bytes32 gasFees, bytes paymasterAndData, bytes signature)[] ops, address payable beneficiary)",
|
|
140
|
+
"function getUserOpHash(tuple(address sender, uint256 nonce, bytes initCode, bytes callData, bytes32 accountGasLimits, uint256 preVerificationGas, bytes32 gasFees, bytes paymasterAndData, bytes signature) userOp) view returns (bytes32)",
|
|
141
|
+
"function getNonce(address sender, uint192 key) view returns (uint256 nonce)",
|
|
142
|
+
"function balanceOf(address account) view returns (uint256)",
|
|
143
|
+
"function depositTo(address account) payable",
|
|
144
|
+
"event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed)"
|
|
145
|
+
];
|
|
146
|
+
var SAFE7579_ACCOUNT_ABI = [
|
|
147
|
+
// ERC-7579 execution (called via UserOp through Safe7579 fallback)
|
|
148
|
+
"function execute(bytes32 mode, bytes executionCalldata) payable",
|
|
149
|
+
"function executeFromExecutor(bytes32 mode, bytes executionCalldata) payable returns (bytes[])",
|
|
150
|
+
// ERC-7579 module queries
|
|
151
|
+
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)",
|
|
152
|
+
// EIP-1271
|
|
153
|
+
"function isValidSignature(bytes32 hash, bytes signature) view returns (bytes4)"
|
|
138
154
|
];
|
|
139
155
|
|
|
140
156
|
// src/utils/config.ts
|
|
157
|
+
var ZERO = "0x0000000000000000000000000000000000000000";
|
|
158
|
+
var ENTRYPOINT_V07 = "0x0000000071727De22E5E9d8BAf0edAc6f37da032";
|
|
159
|
+
var ERC8004_IDENTITY_REGISTRY = "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432";
|
|
160
|
+
var ERC8004_IDENTITY_REGISTRY_TESTNET = "0x8004A818BFB912233c491871b3d84c89A494BD9e";
|
|
161
|
+
var MORPHO_BLUE = "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb";
|
|
162
|
+
var SAFE_SINGLETON = "0x41675C099F32341bf84BFc5382aF534df5C7461a";
|
|
163
|
+
var SAFE_PROXY_FACTORY = "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67";
|
|
164
|
+
var SAFE7579 = "0x7579EE8307284F293B1927136486880611F20002";
|
|
141
165
|
var CONTRACT_ADDRESSES = {
|
|
142
166
|
[1 /* Ethereum */]: {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
167
|
+
safeSingleton: SAFE_SINGLETON,
|
|
168
|
+
safeProxyFactory: SAFE_PROXY_FACTORY,
|
|
169
|
+
safe7579: SAFE7579,
|
|
170
|
+
entryPoint: ENTRYPOINT_V07,
|
|
171
|
+
safeAgentFactory: ZERO,
|
|
172
|
+
safe7579Bootstrap: ZERO,
|
|
173
|
+
erc8004ValidationModule: ZERO,
|
|
174
|
+
hookMultiplexer: ZERO,
|
|
175
|
+
validationRegistry: ZERO,
|
|
176
|
+
agentReputation: ZERO,
|
|
177
|
+
timelockController: ZERO,
|
|
147
178
|
usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
148
|
-
identityRegistry:
|
|
149
|
-
morphoBlue:
|
|
179
|
+
identityRegistry: ERC8004_IDENTITY_REGISTRY,
|
|
180
|
+
morphoBlue: MORPHO_BLUE
|
|
150
181
|
},
|
|
151
182
|
[8453 /* Base */]: {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
183
|
+
safeSingleton: SAFE_SINGLETON,
|
|
184
|
+
safeProxyFactory: SAFE_PROXY_FACTORY,
|
|
185
|
+
safe7579: SAFE7579,
|
|
186
|
+
entryPoint: ENTRYPOINT_V07,
|
|
187
|
+
safeAgentFactory: "0xB0A88ffe28491E793F7706829278f20d724947d1",
|
|
188
|
+
safe7579Bootstrap: "0x72A636bc23B2644138489c3bBE3B05a0a7184b33",
|
|
189
|
+
erc8004ValidationModule: "0x49e27A6B4d012B87271897b51d0296ABcFCb0BBd",
|
|
190
|
+
hookMultiplexer: "0x12c77f17F91f06a11C2C34C618ce9d78f9a34541",
|
|
191
|
+
validationRegistry: "0x88E21e8883c093E4c8d0d0cE68f1c93Cf6190f51",
|
|
192
|
+
agentReputation: "0x4C2d42cbD35f6541f0902499CFEC27C1Cf5683E3",
|
|
193
|
+
timelockController: "0x0517b4f73b61774C88A2B1c5745141315E831015",
|
|
156
194
|
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
157
|
-
identityRegistry:
|
|
158
|
-
morphoBlue:
|
|
195
|
+
identityRegistry: ERC8004_IDENTITY_REGISTRY,
|
|
196
|
+
morphoBlue: MORPHO_BLUE
|
|
159
197
|
},
|
|
160
198
|
[84532 /* BaseSepolia */]: {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
199
|
+
safeSingleton: SAFE_SINGLETON,
|
|
200
|
+
safeProxyFactory: SAFE_PROXY_FACTORY,
|
|
201
|
+
safe7579: SAFE7579,
|
|
202
|
+
entryPoint: ENTRYPOINT_V07,
|
|
203
|
+
safeAgentFactory: ZERO,
|
|
204
|
+
safe7579Bootstrap: ZERO,
|
|
205
|
+
erc8004ValidationModule: ZERO,
|
|
206
|
+
hookMultiplexer: ZERO,
|
|
207
|
+
validationRegistry: ZERO,
|
|
208
|
+
agentReputation: ZERO,
|
|
209
|
+
timelockController: ZERO,
|
|
165
210
|
usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
166
|
-
identityRegistry:
|
|
167
|
-
morphoBlue:
|
|
211
|
+
identityRegistry: ERC8004_IDENTITY_REGISTRY_TESTNET,
|
|
212
|
+
morphoBlue: ZERO
|
|
168
213
|
},
|
|
169
214
|
[11155111 /* Sepolia */]: {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
215
|
+
safeSingleton: SAFE_SINGLETON,
|
|
216
|
+
safeProxyFactory: SAFE_PROXY_FACTORY,
|
|
217
|
+
safe7579: SAFE7579,
|
|
218
|
+
entryPoint: ENTRYPOINT_V07,
|
|
219
|
+
safeAgentFactory: ZERO,
|
|
220
|
+
safe7579Bootstrap: ZERO,
|
|
221
|
+
erc8004ValidationModule: ZERO,
|
|
222
|
+
hookMultiplexer: ZERO,
|
|
223
|
+
validationRegistry: ZERO,
|
|
224
|
+
agentReputation: ZERO,
|
|
225
|
+
timelockController: ZERO,
|
|
174
226
|
usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
175
|
-
identityRegistry:
|
|
176
|
-
morphoBlue:
|
|
227
|
+
identityRegistry: ERC8004_IDENTITY_REGISTRY_TESTNET,
|
|
228
|
+
morphoBlue: ZERO
|
|
177
229
|
},
|
|
178
230
|
[31337 /* Hardhat */]: {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
231
|
+
safeSingleton: SAFE_SINGLETON,
|
|
232
|
+
safeProxyFactory: SAFE_PROXY_FACTORY,
|
|
233
|
+
safe7579: SAFE7579,
|
|
234
|
+
entryPoint: ENTRYPOINT_V07,
|
|
235
|
+
safeAgentFactory: ZERO,
|
|
236
|
+
safe7579Bootstrap: ZERO,
|
|
237
|
+
erc8004ValidationModule: ZERO,
|
|
238
|
+
hookMultiplexer: ZERO,
|
|
239
|
+
validationRegistry: ZERO,
|
|
240
|
+
agentReputation: ZERO,
|
|
241
|
+
timelockController: ZERO,
|
|
183
242
|
usdc: "0x56d4d6aEe0278c5Df2FA23Ecb32eC146C9446FDf",
|
|
184
|
-
identityRegistry:
|
|
185
|
-
morphoBlue:
|
|
243
|
+
identityRegistry: ERC8004_IDENTITY_REGISTRY,
|
|
244
|
+
morphoBlue: ZERO
|
|
186
245
|
}
|
|
187
246
|
};
|
|
188
247
|
var RPC_URLS = {
|
|
@@ -227,9 +286,9 @@ var AgetherClient = class _AgetherClient {
|
|
|
227
286
|
this.agentId = options.agentId;
|
|
228
287
|
const provider = options.signer.provider;
|
|
229
288
|
if (!provider) throw new AgetherError("Signer must have a provider", "NO_PROVIDER");
|
|
230
|
-
this.
|
|
231
|
-
options.config.contracts.
|
|
232
|
-
|
|
289
|
+
this.safeAgentFactory = new Contract(
|
|
290
|
+
options.config.contracts.safeAgentFactory,
|
|
291
|
+
SAFE_AGENT_FACTORY_ABI,
|
|
233
292
|
options.signer
|
|
234
293
|
);
|
|
235
294
|
this.identityRegistry = new Contract(
|
|
@@ -237,16 +296,16 @@ var AgetherClient = class _AgetherClient {
|
|
|
237
296
|
IDENTITY_REGISTRY_ABI,
|
|
238
297
|
provider
|
|
239
298
|
);
|
|
240
|
-
this.validationRegistry = new Contract(
|
|
241
|
-
options.config.contracts.validationRegistry,
|
|
242
|
-
VALIDATION_REGISTRY_ABI,
|
|
243
|
-
provider
|
|
244
|
-
);
|
|
245
299
|
this.agentReputation = new Contract(
|
|
246
300
|
options.config.contracts.agentReputation,
|
|
247
301
|
AGENT_REPUTATION_ABI,
|
|
248
302
|
provider
|
|
249
303
|
);
|
|
304
|
+
this.validationModule = new Contract(
|
|
305
|
+
options.config.contracts.erc8004ValidationModule,
|
|
306
|
+
ERC8004_VALIDATION_MODULE_ABI,
|
|
307
|
+
provider
|
|
308
|
+
);
|
|
250
309
|
}
|
|
251
310
|
// Static Factory
|
|
252
311
|
static fromPrivateKey(privateKey, agentId, chainIdOrConfig) {
|
|
@@ -257,26 +316,26 @@ var AgetherClient = class _AgetherClient {
|
|
|
257
316
|
}
|
|
258
317
|
// Account Management
|
|
259
318
|
async createAccount() {
|
|
260
|
-
const tx = await this.
|
|
319
|
+
const tx = await this.safeAgentFactory.createAccount(this.agentId);
|
|
261
320
|
const receipt = await tx.wait();
|
|
262
321
|
const event = receipt.logs.map((log) => {
|
|
263
322
|
try {
|
|
264
|
-
return this.
|
|
323
|
+
return this.safeAgentFactory.interface.parseLog(log);
|
|
265
324
|
} catch (e) {
|
|
266
325
|
console.warn("[agether] createAccount parseLog skip:", e instanceof Error ? e.message : e);
|
|
267
326
|
return null;
|
|
268
327
|
}
|
|
269
328
|
}).find((e) => e?.name === "AccountCreated");
|
|
270
329
|
if (event) {
|
|
271
|
-
this.accountAddress = event.args.
|
|
330
|
+
this.accountAddress = event.args.safeAccount;
|
|
272
331
|
} else {
|
|
273
|
-
this.accountAddress = await this.
|
|
332
|
+
this.accountAddress = await this.safeAgentFactory.getAccount(this.agentId);
|
|
274
333
|
}
|
|
275
334
|
return this.accountAddress;
|
|
276
335
|
}
|
|
277
336
|
async getAccountAddress() {
|
|
278
337
|
if (this.accountAddress) return this.accountAddress;
|
|
279
|
-
const addr = await this.
|
|
338
|
+
const addr = await this.safeAgentFactory.getAccount(this.agentId);
|
|
280
339
|
if (addr === ethers.ZeroAddress) {
|
|
281
340
|
throw new AgetherError("No account found. Create one with createAccount().", "NO_ACCOUNT");
|
|
282
341
|
}
|
|
@@ -284,10 +343,7 @@ var AgetherClient = class _AgetherClient {
|
|
|
284
343
|
return addr;
|
|
285
344
|
}
|
|
286
345
|
async accountExists() {
|
|
287
|
-
return this.
|
|
288
|
-
}
|
|
289
|
-
async predictAddress() {
|
|
290
|
-
return this.accountFactory.predictAddress(this.agentId);
|
|
346
|
+
return this.safeAgentFactory.accountExists(this.agentId);
|
|
291
347
|
}
|
|
292
348
|
// Balances
|
|
293
349
|
async getBalances() {
|
|
@@ -309,10 +365,14 @@ var AgetherClient = class _AgetherClient {
|
|
|
309
365
|
usdc: ethers.formatUnits(acctUsdc, 6)
|
|
310
366
|
};
|
|
311
367
|
} catch (e) {
|
|
312
|
-
console.warn("[agether] getBalances: no
|
|
368
|
+
console.warn("[agether] getBalances: no Safe account or fetch failed:", e instanceof Error ? e.message : e);
|
|
313
369
|
}
|
|
314
370
|
return result;
|
|
315
371
|
}
|
|
372
|
+
/**
|
|
373
|
+
* Fund the Safe account with USDC from EOA.
|
|
374
|
+
* This is a simple ERC-20 transfer (does NOT require a UserOp).
|
|
375
|
+
*/
|
|
316
376
|
async fundAccount(usdcAmount) {
|
|
317
377
|
const acctAddr = await this.getAccountAddress();
|
|
318
378
|
const usdc = new Contract(this.config.contracts.usdc, ERC20_ABI, this.signer);
|
|
@@ -326,37 +386,30 @@ var AgetherClient = class _AgetherClient {
|
|
|
326
386
|
gasUsed: receipt.gasUsed
|
|
327
387
|
};
|
|
328
388
|
}
|
|
329
|
-
async withdrawUsdc(usdcAmount) {
|
|
330
|
-
const acctAddr = await this.getAccountAddress();
|
|
331
|
-
const account = new Contract(acctAddr, AGENT_ACCOUNT_ABI, this.signer);
|
|
332
|
-
const amount = ethers.parseUnits(usdcAmount, 6);
|
|
333
|
-
const eoaAddr = await this.signer.getAddress();
|
|
334
|
-
const tx = await account.withdraw(this.config.contracts.usdc, amount, eoaAddr);
|
|
335
|
-
const receipt = await tx.wait();
|
|
336
|
-
return {
|
|
337
|
-
txHash: receipt.hash,
|
|
338
|
-
blockNumber: receipt.blockNumber,
|
|
339
|
-
status: receipt.status === 1 ? "success" : "failed",
|
|
340
|
-
gasUsed: receipt.gasUsed
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
async withdrawEth(ethAmount) {
|
|
344
|
-
const acctAddr = await this.getAccountAddress();
|
|
345
|
-
const account = new Contract(acctAddr, AGENT_ACCOUNT_ABI, this.signer);
|
|
346
|
-
const amount = ethers.parseEther(ethAmount);
|
|
347
|
-
const eoaAddr = await this.signer.getAddress();
|
|
348
|
-
const tx = await account.withdrawETH(amount, eoaAddr);
|
|
349
|
-
const receipt = await tx.wait();
|
|
350
|
-
return {
|
|
351
|
-
txHash: receipt.hash,
|
|
352
|
-
blockNumber: receipt.blockNumber,
|
|
353
|
-
status: receipt.status === 1 ? "success" : "failed",
|
|
354
|
-
gasUsed: receipt.gasUsed
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
389
|
// Identity & Validation
|
|
390
|
+
/**
|
|
391
|
+
* Check if the KYA gate is active on the validation module.
|
|
392
|
+
* If validationRegistry is not set, all txs pass (KYA disabled).
|
|
393
|
+
*/
|
|
394
|
+
async isKyaRequired() {
|
|
395
|
+
try {
|
|
396
|
+
const registryAddr = await this.validationModule.validationRegistry();
|
|
397
|
+
return registryAddr !== ethers.ZeroAddress;
|
|
398
|
+
} catch {
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Check if this agent's code is KYA-approved.
|
|
404
|
+
* Uses the ERC8004ValidationModule.isKYAApproved(account) view.
|
|
405
|
+
*/
|
|
358
406
|
async isKyaApproved() {
|
|
359
|
-
|
|
407
|
+
try {
|
|
408
|
+
const acctAddr = await this.getAccountAddress();
|
|
409
|
+
return this.validationModule.isKYAApproved(acctAddr);
|
|
410
|
+
} catch {
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
360
413
|
}
|
|
361
414
|
async identityExists() {
|
|
362
415
|
try {
|
|
@@ -444,27 +497,24 @@ var MorphoClient = class {
|
|
|
444
497
|
this._eoaAddress = wallet.address;
|
|
445
498
|
}
|
|
446
499
|
const addrs = { ...defaultCfg.contracts, ...config.contracts };
|
|
447
|
-
this.
|
|
500
|
+
this.safeAgentFactory = new Contract2(addrs.safeAgentFactory, ACCOUNT_FACTORY_ABI, this._signer);
|
|
448
501
|
this.morphoBlue = new Contract2(addrs.morphoBlue, MORPHO_BLUE_ABI, this.provider);
|
|
449
502
|
this.agentReputation = new Contract2(addrs.agentReputation, AGENT_REPUTATION_ABI, this._signer);
|
|
450
503
|
this.identityRegistry = new Contract2(addrs.identityRegistry, IDENTITY_REGISTRY_ABI, this._signer);
|
|
504
|
+
this.entryPoint = new Contract2(addrs.entryPoint, ENTRYPOINT_V07_ABI, this._signer);
|
|
505
|
+
this.validationModule = new Contract2(addrs.erc8004ValidationModule, ERC8004_VALIDATION_MODULE_ABI, this.provider);
|
|
451
506
|
}
|
|
452
507
|
// ════════════════════════════════════════════════════════
|
|
453
508
|
// KYA Gate Check
|
|
454
509
|
// ════════════════════════════════════════════════════════
|
|
455
510
|
/**
|
|
456
511
|
* Check whether the KYA (Know Your Agent) code verification gate is active.
|
|
457
|
-
* Reads the
|
|
458
|
-
*
|
|
512
|
+
* Reads the ERC8004ValidationModule's validationRegistry — when set to
|
|
513
|
+
* a non-zero address, the module enforces KYA code approval.
|
|
459
514
|
*/
|
|
460
515
|
async isKyaRequired() {
|
|
461
516
|
try {
|
|
462
|
-
const
|
|
463
|
-
const account = new Contract2(acctAddr, AGENT_ACCOUNT_ABI, this.provider);
|
|
464
|
-
const hookAddr = await account.hook();
|
|
465
|
-
if (hookAddr === ethers2.ZeroAddress) return false;
|
|
466
|
-
const hook = new Contract2(hookAddr, KYA_HOOK_ABI, this.provider);
|
|
467
|
-
const registryAddr = await hook.validationRegistry();
|
|
517
|
+
const registryAddr = await this.validationModule.validationRegistry();
|
|
468
518
|
return registryAddr !== ethers2.ZeroAddress;
|
|
469
519
|
} catch {
|
|
470
520
|
return false;
|
|
@@ -481,7 +531,7 @@ var MorphoClient = class {
|
|
|
481
531
|
let lastErr;
|
|
482
532
|
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
483
533
|
try {
|
|
484
|
-
const addr = await this.
|
|
534
|
+
const addr = await this.safeAgentFactory.getAccount(BigInt(this.agentId));
|
|
485
535
|
if (addr === ethers2.ZeroAddress) {
|
|
486
536
|
throw new AgetherError("No AgentAccount found. Call register() first.", "NO_ACCOUNT");
|
|
487
537
|
}
|
|
@@ -559,9 +609,9 @@ var MorphoClient = class {
|
|
|
559
609
|
async register(_name) {
|
|
560
610
|
const eoaAddr = await this.getSignerAddress();
|
|
561
611
|
if (this.agentId) {
|
|
562
|
-
const exists = await this.
|
|
612
|
+
const exists = await this.safeAgentFactory.accountExists(BigInt(this.agentId));
|
|
563
613
|
if (exists) {
|
|
564
|
-
const acct = await this.
|
|
614
|
+
const acct = await this.safeAgentFactory.getAccount(BigInt(this.agentId));
|
|
565
615
|
this._accountAddress = acct;
|
|
566
616
|
const kyaRequired2 = await this.isKyaRequired();
|
|
567
617
|
return { agentId: this.agentId, address: eoaAddr, agentAccount: acct, alreadyRegistered: true, kyaRequired: kyaRequired2 };
|
|
@@ -579,15 +629,15 @@ var MorphoClient = class {
|
|
|
579
629
|
agentId = await this._mintNewIdentity();
|
|
580
630
|
}
|
|
581
631
|
this.agentId = agentId.toString();
|
|
582
|
-
const acctExists = await this.
|
|
632
|
+
const acctExists = await this.safeAgentFactory.accountExists(agentId);
|
|
583
633
|
let txHash;
|
|
584
634
|
if (!acctExists) {
|
|
585
|
-
const tx = await this.
|
|
635
|
+
const tx = await this.safeAgentFactory.createAccount(agentId);
|
|
586
636
|
const receipt = await tx.wait();
|
|
587
637
|
this._refreshSigner();
|
|
588
638
|
txHash = receipt.hash;
|
|
589
639
|
}
|
|
590
|
-
const acctAddr = await this.
|
|
640
|
+
const acctAddr = await this.safeAgentFactory.getAccount(agentId);
|
|
591
641
|
this._accountAddress = acctAddr;
|
|
592
642
|
const kyaRequired = await this.isKyaRequired();
|
|
593
643
|
return {
|
|
@@ -789,7 +839,7 @@ var MorphoClient = class {
|
|
|
789
839
|
const mkt = await this.morphoBlue.market(m.uniqueKey);
|
|
790
840
|
const totalBorrowShares = BigInt(mkt.totalBorrowShares);
|
|
791
841
|
const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
|
|
792
|
-
debt = totalBorrowShares > 0n ? BigInt(pos.borrowShares) * totalBorrowAssets / totalBorrowShares : 0n;
|
|
842
|
+
debt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
793
843
|
totalDebt += debt;
|
|
794
844
|
} catch (e) {
|
|
795
845
|
console.warn(`[agether] debt calc failed for market ${m.uniqueKey}:`, e instanceof Error ? e.message : e);
|
|
@@ -851,7 +901,7 @@ var MorphoClient = class {
|
|
|
851
901
|
const mktState = await this.morphoBlue.market(m.uniqueKey);
|
|
852
902
|
const totalBorrowShares = BigInt(mktState.totalBorrowShares);
|
|
853
903
|
const totalBorrowAssets = BigInt(mktState.totalBorrowAssets);
|
|
854
|
-
const currentDebt = totalBorrowShares > 0n ? BigInt(pos.borrowShares) * totalBorrowAssets / totalBorrowShares : 0n;
|
|
904
|
+
const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
855
905
|
let collateralValueInLoan;
|
|
856
906
|
try {
|
|
857
907
|
const oracleContract = new Contract2(m.oracle, [
|
|
@@ -1214,13 +1264,14 @@ var MorphoClient = class {
|
|
|
1214
1264
|
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
1215
1265
|
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
1216
1266
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1267
|
+
const usdcAddr = this.config.contracts.usdc;
|
|
1217
1268
|
const dest = receiver || await this.getSignerAddress();
|
|
1218
1269
|
let weiAmount;
|
|
1270
|
+
const markets = await this.getMarkets();
|
|
1271
|
+
const market = markets.find(
|
|
1272
|
+
(m) => m.collateralAsset?.address.toLowerCase() === colInfo.address.toLowerCase()
|
|
1273
|
+
);
|
|
1219
1274
|
if (amount === "all") {
|
|
1220
|
-
const markets = await this.getMarkets();
|
|
1221
|
-
const market = markets.find(
|
|
1222
|
-
(m) => m.collateralAsset.address.toLowerCase() === colInfo.address.toLowerCase()
|
|
1223
|
-
);
|
|
1224
1275
|
if (!market) throw new AgetherError("Market not found", "MARKET_NOT_FOUND");
|
|
1225
1276
|
const pos = await this.morphoBlue.position(market.uniqueKey, acctAddr);
|
|
1226
1277
|
weiAmount = pos.collateral;
|
|
@@ -1228,19 +1279,65 @@ var MorphoClient = class {
|
|
|
1228
1279
|
} else {
|
|
1229
1280
|
weiAmount = ethers2.parseUnits(amount, colInfo.decimals);
|
|
1230
1281
|
}
|
|
1231
|
-
|
|
1282
|
+
let hasDustDebt = false;
|
|
1283
|
+
let dustBorrowShares = 0n;
|
|
1284
|
+
let dustApproveAmount = 0n;
|
|
1285
|
+
if (market) {
|
|
1286
|
+
try {
|
|
1287
|
+
const pos = await this.morphoBlue.position(market.uniqueKey, acctAddr);
|
|
1288
|
+
dustBorrowShares = BigInt(pos.borrowShares);
|
|
1289
|
+
if (dustBorrowShares > 0n) {
|
|
1290
|
+
hasDustDebt = true;
|
|
1291
|
+
const onChainMkt = await this.morphoBlue.market(market.uniqueKey);
|
|
1292
|
+
const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
|
|
1293
|
+
const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
|
|
1294
|
+
const estimated = totalBorrowShares > 0n ? dustBorrowShares * totalBorrowAssets / totalBorrowShares + 10n : 0n;
|
|
1295
|
+
dustApproveAmount = estimated > 0n ? estimated : ethers2.parseUnits("1", 6);
|
|
1296
|
+
console.log(`[agether] dust borrow shares detected: ${dustBorrowShares} shares \u2248 ${ethers2.formatUnits(dustApproveAmount, 6)} USDC \u2014 auto-repaying before withdraw`);
|
|
1297
|
+
}
|
|
1298
|
+
} catch (e) {
|
|
1299
|
+
console.warn("[agether] failed to check borrow shares before withdraw:", e instanceof Error ? e.message : e);
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
const withdrawData = morphoIface.encodeFunctionData("withdrawCollateral", [
|
|
1232
1303
|
this._toTuple(params),
|
|
1233
1304
|
weiAmount,
|
|
1234
1305
|
acctAddr,
|
|
1235
1306
|
dest
|
|
1236
1307
|
]);
|
|
1237
|
-
|
|
1308
|
+
let receipt;
|
|
1309
|
+
if (hasDustDebt) {
|
|
1310
|
+
const usdcContract = new Contract2(usdcAddr, ERC20_ABI, this._signer);
|
|
1311
|
+
const acctBalance = await usdcContract.balanceOf(acctAddr);
|
|
1312
|
+
if (acctBalance < dustApproveAmount) {
|
|
1313
|
+
const shortfall = dustApproveAmount - acctBalance;
|
|
1314
|
+
const eoaBalance = await usdcContract.balanceOf(await this.getSignerAddress());
|
|
1315
|
+
if (eoaBalance >= shortfall) {
|
|
1316
|
+
console.log(`[agether] transferring ${ethers2.formatUnits(shortfall, 6)} USDC from EOA \u2192 AgentAccount for dust repay`);
|
|
1317
|
+
const transferTx = await usdcContract.transfer(acctAddr, shortfall);
|
|
1318
|
+
await transferTx.wait();
|
|
1319
|
+
this._refreshSigner();
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
const targets = [usdcAddr, morphoAddr, morphoAddr];
|
|
1323
|
+
const values = [0n, 0n, 0n];
|
|
1324
|
+
const datas = [
|
|
1325
|
+
erc20Iface.encodeFunctionData("approve", [morphoAddr, dustApproveAmount]),
|
|
1326
|
+
morphoIface.encodeFunctionData("repay", [
|
|
1327
|
+
this._toTuple(params),
|
|
1328
|
+
0n,
|
|
1329
|
+
dustBorrowShares,
|
|
1330
|
+
acctAddr,
|
|
1331
|
+
"0x"
|
|
1332
|
+
]),
|
|
1333
|
+
withdrawData
|
|
1334
|
+
];
|
|
1335
|
+
receipt = await this.batch(targets, values, datas);
|
|
1336
|
+
} else {
|
|
1337
|
+
receipt = await this.exec(morphoAddr, withdrawData);
|
|
1338
|
+
}
|
|
1238
1339
|
let remainingCollateral = "0";
|
|
1239
1340
|
try {
|
|
1240
|
-
const markets = await this.getMarkets();
|
|
1241
|
-
const market = markets.find(
|
|
1242
|
-
(m) => m.collateralAsset.address.toLowerCase() === colInfo.address.toLowerCase()
|
|
1243
|
-
);
|
|
1244
1341
|
if (market) {
|
|
1245
1342
|
const pos = await this.morphoBlue.position(market.uniqueKey, acctAddr);
|
|
1246
1343
|
remainingCollateral = ethers2.formatUnits(pos.collateral, colInfo.decimals);
|
|
@@ -1267,7 +1364,7 @@ var MorphoClient = class {
|
|
|
1267
1364
|
if (target.address) {
|
|
1268
1365
|
targetAddr = target.address;
|
|
1269
1366
|
} else if (target.agentId) {
|
|
1270
|
-
targetAddr = await this.
|
|
1367
|
+
targetAddr = await this.safeAgentFactory.getAccount(BigInt(target.agentId));
|
|
1271
1368
|
if (targetAddr === ethers2.ZeroAddress) throw new AgetherError("Target agent has no account", "NO_ACCOUNT");
|
|
1272
1369
|
} else {
|
|
1273
1370
|
throw new AgetherError("Provide agentId or address", "INVALID_TARGET");
|
|
@@ -1320,7 +1417,9 @@ var MorphoClient = class {
|
|
|
1320
1417
|
_refreshSigner() {
|
|
1321
1418
|
if (this._useExternalSigner) {
|
|
1322
1419
|
const addrs = this.config.contracts;
|
|
1323
|
-
this.
|
|
1420
|
+
this.safeAgentFactory = new Contract2(addrs.safeAgentFactory, ACCOUNT_FACTORY_ABI, this._signer);
|
|
1421
|
+
this.entryPoint = new Contract2(addrs.entryPoint, ENTRYPOINT_V07_ABI, this._signer);
|
|
1422
|
+
this.validationModule = new Contract2(addrs.erc8004ValidationModule, ERC8004_VALIDATION_MODULE_ABI, this.provider);
|
|
1324
1423
|
this.agentReputation = new Contract2(addrs.agentReputation, AGENT_REPUTATION_ABI, this._signer);
|
|
1325
1424
|
this.identityRegistry = new Contract2(addrs.identityRegistry, IDENTITY_REGISTRY_ABI, this._signer);
|
|
1326
1425
|
} else {
|
|
@@ -1329,57 +1428,87 @@ var MorphoClient = class {
|
|
|
1329
1428
|
this._signer = wallet;
|
|
1330
1429
|
this._eoaAddress = wallet.address;
|
|
1331
1430
|
const addrs = this.config.contracts;
|
|
1332
|
-
this.
|
|
1431
|
+
this.safeAgentFactory = new Contract2(addrs.safeAgentFactory, ACCOUNT_FACTORY_ABI, this._signer);
|
|
1432
|
+
this.entryPoint = new Contract2(addrs.entryPoint, ENTRYPOINT_V07_ABI, this._signer);
|
|
1433
|
+
this.validationModule = new Contract2(addrs.erc8004ValidationModule, ERC8004_VALIDATION_MODULE_ABI, this.provider);
|
|
1333
1434
|
this.agentReputation = new Contract2(addrs.agentReputation, AGENT_REPUTATION_ABI, this._signer);
|
|
1334
1435
|
this.identityRegistry = new Contract2(addrs.identityRegistry, IDENTITY_REGISTRY_ABI, this._signer);
|
|
1335
1436
|
}
|
|
1336
1437
|
}
|
|
1438
|
+
// ────────────────────────────────────────────────────────────
|
|
1439
|
+
// ERC-4337 UserOp helpers (Safe + Safe7579 + EntryPoint v0.7)
|
|
1440
|
+
// ────────────────────────────────────────────────────────────
|
|
1337
1441
|
/**
|
|
1338
|
-
*
|
|
1442
|
+
* Pack two uint128 values into a single bytes32:
|
|
1443
|
+
* bytes32 = (hi << 128) | lo
|
|
1444
|
+
*/
|
|
1445
|
+
_packUint128(hi, lo) {
|
|
1446
|
+
return ethers2.zeroPadValue(ethers2.toBeHex(hi << 128n | lo), 32);
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Build, sign and submit a PackedUserOperation through EntryPoint.handleOps.
|
|
1450
|
+
*
|
|
1451
|
+
* @param callData – the ABI-encoded calldata for the Safe7579 account
|
|
1452
|
+
* (e.g. `execute(mode, executionCalldata)`)
|
|
1453
|
+
* @returns the transaction receipt of the handleOps call
|
|
1454
|
+
*/
|
|
1455
|
+
async _submitUserOp(callData) {
|
|
1456
|
+
const sender = await this.getAccountAddress();
|
|
1457
|
+
const nonce = await this.entryPoint.getNonce(sender, 0);
|
|
1458
|
+
const feeData = await this.provider.getFeeData();
|
|
1459
|
+
const maxFeePerGas = feeData.maxFeePerGas ?? ethers2.parseUnits("0.5", "gwei");
|
|
1460
|
+
const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? ethers2.parseUnits("0.1", "gwei");
|
|
1461
|
+
const verificationGasLimit = 500000n;
|
|
1462
|
+
const callGasLimit = 800000n;
|
|
1463
|
+
const preVerificationGas = 100000n;
|
|
1464
|
+
const accountGasLimits = this._packUint128(verificationGasLimit, callGasLimit);
|
|
1465
|
+
const gasFees = this._packUint128(maxPriorityFeePerGas, maxFeePerGas);
|
|
1466
|
+
const userOp = {
|
|
1467
|
+
sender,
|
|
1468
|
+
nonce,
|
|
1469
|
+
initCode: "0x",
|
|
1470
|
+
callData,
|
|
1471
|
+
accountGasLimits,
|
|
1472
|
+
preVerificationGas,
|
|
1473
|
+
gasFees,
|
|
1474
|
+
paymasterAndData: "0x",
|
|
1475
|
+
signature: "0x"
|
|
1476
|
+
// placeholder — replaced after signing
|
|
1477
|
+
};
|
|
1478
|
+
const userOpHash = await this.entryPoint.getUserOpHash(userOp);
|
|
1479
|
+
const signature = await this._signer.signMessage(ethers2.getBytes(userOpHash));
|
|
1480
|
+
userOp.signature = signature;
|
|
1481
|
+
const tx = await this.entryPoint.handleOps([userOp], await this.getSignerAddress());
|
|
1482
|
+
const receipt = await tx.wait();
|
|
1483
|
+
this._refreshSigner();
|
|
1484
|
+
return receipt;
|
|
1485
|
+
}
|
|
1486
|
+
/**
|
|
1487
|
+
* Execute a single call via Safe7579 account (ERC-7579 single mode)
|
|
1488
|
+
* through an ERC-4337 UserOperation.
|
|
1339
1489
|
*/
|
|
1340
1490
|
async exec(target, data, value = 0n) {
|
|
1341
|
-
const acctAddr = await this.getAccountAddress();
|
|
1342
|
-
const account = new Contract2(acctAddr, AGENT_ACCOUNT_ABI, this._signer);
|
|
1343
1491
|
const executionCalldata = ethers2.AbiCoder.defaultAbiCoder().encode(
|
|
1344
1492
|
["address", "uint256", "bytes"],
|
|
1345
1493
|
[target, value, data]
|
|
1346
1494
|
);
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
gasLimit = estimate * 130n / 100n;
|
|
1351
|
-
} catch (e) {
|
|
1352
|
-
console.warn("[agether] exec gas estimation failed, using default 500k:", e instanceof Error ? e.message : e);
|
|
1353
|
-
gasLimit = 500000n;
|
|
1354
|
-
}
|
|
1355
|
-
const tx = await account.execute(MODE_SINGLE, executionCalldata, { gasLimit });
|
|
1356
|
-
const receipt = await tx.wait();
|
|
1357
|
-
this._refreshSigner();
|
|
1358
|
-
return receipt;
|
|
1495
|
+
const safe7579Iface = new ethers2.Interface(SAFE7579_ACCOUNT_ABI);
|
|
1496
|
+
const callData = safe7579Iface.encodeFunctionData("execute", [MODE_SINGLE, executionCalldata]);
|
|
1497
|
+
return this._submitUserOp(callData);
|
|
1359
1498
|
}
|
|
1360
1499
|
/**
|
|
1361
|
-
* Execute multiple calls via
|
|
1500
|
+
* Execute multiple calls via Safe7579 account (ERC-7579 batch mode)
|
|
1501
|
+
* through an ERC-4337 UserOperation.
|
|
1362
1502
|
*/
|
|
1363
1503
|
async batch(targets, values, datas) {
|
|
1364
|
-
const acctAddr = await this.getAccountAddress();
|
|
1365
|
-
const account = new Contract2(acctAddr, AGENT_ACCOUNT_ABI, this._signer);
|
|
1366
1504
|
const executions = targets.map((t, i) => [t, values[i], datas[i]]);
|
|
1367
1505
|
const executionCalldata = ethers2.AbiCoder.defaultAbiCoder().encode(
|
|
1368
1506
|
["(address,uint256,bytes)[]"],
|
|
1369
1507
|
[executions]
|
|
1370
1508
|
);
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
gasLimit = estimate * 130n / 100n;
|
|
1375
|
-
} catch (e) {
|
|
1376
|
-
console.warn("[agether] batch gas estimation failed, using default 800k:", e instanceof Error ? e.message : e);
|
|
1377
|
-
gasLimit = 800000n;
|
|
1378
|
-
}
|
|
1379
|
-
const tx = await account.execute(MODE_BATCH, executionCalldata, { gasLimit });
|
|
1380
|
-
const receipt = await tx.wait();
|
|
1381
|
-
this._refreshSigner();
|
|
1382
|
-
return receipt;
|
|
1509
|
+
const safe7579Iface = new ethers2.Interface(SAFE7579_ACCOUNT_ABI);
|
|
1510
|
+
const callData = safe7579Iface.encodeFunctionData("execute", [MODE_BATCH, executionCalldata]);
|
|
1511
|
+
return this._submitUserOp(callData);
|
|
1383
1512
|
}
|
|
1384
1513
|
/** Convert MorphoMarketParams to Solidity tuple. */
|
|
1385
1514
|
_toTuple(p) {
|
|
@@ -2205,19 +2334,22 @@ function rateToBps(rate) {
|
|
|
2205
2334
|
}
|
|
2206
2335
|
export {
|
|
2207
2336
|
ACCOUNT_FACTORY_ABI,
|
|
2208
|
-
AGENT_ACCOUNT_ABI,
|
|
2209
2337
|
AGENT_REPUTATION_ABI,
|
|
2210
2338
|
AgentIdentityClient,
|
|
2211
2339
|
AgentNotApprovedError,
|
|
2212
2340
|
AgetherClient,
|
|
2213
2341
|
AgetherError,
|
|
2214
2342
|
ChainId,
|
|
2343
|
+
ENTRYPOINT_V07_ABI,
|
|
2215
2344
|
ERC20_ABI,
|
|
2345
|
+
ERC8004_VALIDATION_MODULE_ABI,
|
|
2346
|
+
HOOK_MULTIPLEXER_ABI,
|
|
2216
2347
|
IDENTITY_REGISTRY_ABI,
|
|
2217
2348
|
InsufficientBalanceError,
|
|
2218
|
-
KYA_HOOK_ABI,
|
|
2219
2349
|
MORPHO_BLUE_ABI,
|
|
2220
2350
|
MorphoClient,
|
|
2351
|
+
SAFE7579_ACCOUNT_ABI,
|
|
2352
|
+
SAFE_AGENT_FACTORY_ABI,
|
|
2221
2353
|
ScoringClient,
|
|
2222
2354
|
ScoringRejectedError,
|
|
2223
2355
|
VALIDATION_REGISTRY_ABI,
|