agent0-sdk 1.0.3 → 1.1.1
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/README.md +28 -6
- package/dist/browser/eip6963.d.ts +60 -0
- package/dist/browser/eip6963.d.ts.map +1 -0
- package/dist/browser/eip6963.js +87 -0
- package/dist/browser/eip6963.js.map +1 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +2 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/viem-chain-client.d.ts +2 -0
- package/dist/browser/viem-chain-client.d.ts.map +1 -0
- package/dist/browser/viem-chain-client.js +2 -0
- package/dist/browser/viem-chain-client.js.map +1 -0
- package/dist/core/agent.d.ts +5 -2
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +204 -125
- package/dist/core/agent.js.map +1 -1
- package/dist/core/chain-client.d.ts +129 -0
- package/dist/core/chain-client.d.ts.map +1 -0
- package/dist/core/chain-client.js +2 -0
- package/dist/core/chain-client.js.map +1 -0
- package/dist/core/feedback-manager.d.ts +7 -8
- package/dist/core/feedback-manager.d.ts.map +1 -1
- package/dist/core/feedback-manager.js +107 -60
- package/dist/core/feedback-manager.js.map +1 -1
- package/dist/core/indexer.d.ts +1 -3
- package/dist/core/indexer.d.ts.map +1 -1
- package/dist/core/indexer.js +1 -2
- package/dist/core/indexer.js.map +1 -1
- package/dist/core/sdk.d.ts +19 -20
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +61 -69
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/subgraph-client.d.ts +30 -2
- package/dist/core/subgraph-client.d.ts.map +1 -1
- package/dist/core/subgraph-client.js.map +1 -1
- package/dist/core/viem-chain-client.d.ts +78 -0
- package/dist/core/viem-chain-client.d.ts.map +1 -0
- package/dist/core/viem-chain-client.js +255 -0
- package/dist/core/viem-chain-client.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/signatures.d.ts +22 -0
- package/dist/utils/signatures.d.ts.map +1 -0
- package/dist/utils/signatures.js +40 -0
- package/dist/utils/signatures.js.map +1 -0
- package/package.json +14 -4
package/dist/core/agent.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agent class for managing individual agents
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { decodeEventLog, getAddress, hashDomain } from 'viem';
|
|
5
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
5
6
|
import { EndpointType, TrustModel } from '../models/enums.js';
|
|
6
7
|
import { EndpointCrawler } from './endpoint-crawler.js';
|
|
7
8
|
import { parseAgentId } from '../utils/id-format.js';
|
|
8
9
|
import { TIMEOUTS } from '../utils/constants.js';
|
|
9
10
|
import { validateSkill, validateDomain } from './oasf-validator.js';
|
|
11
|
+
import { IDENTITY_REGISTRY_ABI } from './contracts.js';
|
|
12
|
+
import { normalizeEcdsaSignature, recoverTypedDataSigner } from '../utils/signatures.js';
|
|
10
13
|
/**
|
|
11
14
|
* Agent class for managing individual agents
|
|
12
15
|
*/
|
|
@@ -291,18 +294,23 @@ export class Agent {
|
|
|
291
294
|
throw new Error('Agent must be registered before setting agentWallet on-chain. ' +
|
|
292
295
|
'Register the agent first, then call setAgentWallet().');
|
|
293
296
|
}
|
|
294
|
-
if (
|
|
295
|
-
throw new Error('No
|
|
297
|
+
if (this.sdk.isReadOnly) {
|
|
298
|
+
throw new Error('No signer configured to submit setAgentWallet transaction');
|
|
296
299
|
}
|
|
297
300
|
// Validate newWallet address
|
|
298
|
-
if (!this.sdk.
|
|
301
|
+
if (!this.sdk.chainClient.isAddress(newWallet)) {
|
|
299
302
|
throw new Error(`Invalid newWallet address: ${newWallet}`);
|
|
300
303
|
}
|
|
301
304
|
const { tokenId } = parseAgentId(this.registrationFile.agentId);
|
|
302
|
-
const
|
|
305
|
+
const identityRegistryAddress = this.sdk.identityRegistryAddress();
|
|
303
306
|
// Optional short-circuit if already set
|
|
304
307
|
try {
|
|
305
|
-
const currentWallet = await this.sdk.
|
|
308
|
+
const currentWallet = await this.sdk.chainClient.readContract({
|
|
309
|
+
address: identityRegistryAddress,
|
|
310
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
311
|
+
functionName: 'getAgentWallet',
|
|
312
|
+
args: [BigInt(tokenId)],
|
|
313
|
+
});
|
|
306
314
|
if (typeof currentWallet === 'string' &&
|
|
307
315
|
currentWallet.toLowerCase() === newWallet.toLowerCase()) {
|
|
308
316
|
const chainId = await this.sdk.chainId();
|
|
@@ -317,8 +325,7 @@ export class Agent {
|
|
|
317
325
|
}
|
|
318
326
|
// Deadline: contract enforces a short window. Use chain time (latest block timestamp)
|
|
319
327
|
// rather than local system time to avoid clock skew causing reverts.
|
|
320
|
-
const
|
|
321
|
-
const chainNow = latestBlock?.timestamp ?? Math.floor(Date.now() / 1000);
|
|
328
|
+
const chainNow = Number(await this.sdk.chainClient.getBlockTimestamp('latest'));
|
|
322
329
|
const deadlineValue = opts?.deadline ?? chainNow + 60;
|
|
323
330
|
if (deadlineValue < chainNow) {
|
|
324
331
|
throw new Error(`Invalid deadline: ${deadlineValue} is in the past (chain time: ${chainNow})`);
|
|
@@ -329,16 +336,25 @@ export class Agent {
|
|
|
329
336
|
`(chain time: ${chainNow})`);
|
|
330
337
|
}
|
|
331
338
|
const chainId = await this.sdk.chainId();
|
|
332
|
-
const verifyingContract =
|
|
333
|
-
const owner = await this.sdk.
|
|
339
|
+
const verifyingContract = identityRegistryAddress;
|
|
340
|
+
const owner = await this.sdk.chainClient.readContract({
|
|
341
|
+
address: identityRegistryAddress,
|
|
342
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
343
|
+
functionName: 'ownerOf',
|
|
344
|
+
args: [BigInt(tokenId)],
|
|
345
|
+
});
|
|
334
346
|
// Prefer reading the actual EIP-712 domain from the contract (if supported)
|
|
335
347
|
// to avoid any future divergence in name/version.
|
|
336
348
|
let domainName;
|
|
337
349
|
let domainVersion;
|
|
338
350
|
try {
|
|
339
|
-
const domainInfo = await this.sdk.
|
|
351
|
+
const domainInfo = await this.sdk.chainClient.readContract({
|
|
352
|
+
address: identityRegistryAddress,
|
|
353
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
354
|
+
functionName: 'eip712Domain',
|
|
355
|
+
args: [],
|
|
356
|
+
});
|
|
340
357
|
// eip712Domain() returns: (fields, name, version, chainId, verifyingContract, salt, extensions)
|
|
341
|
-
// In ethers v6 this is typically a Result array-like object.
|
|
342
358
|
domainName = domainInfo?.name ?? domainInfo?.[1];
|
|
343
359
|
domainVersion = domainInfo?.version ?? domainInfo?.[2];
|
|
344
360
|
}
|
|
@@ -349,50 +365,41 @@ export class Agent {
|
|
|
349
365
|
// deterministically from common candidates.
|
|
350
366
|
let domainSeparatorOnChain;
|
|
351
367
|
try {
|
|
352
|
-
domainSeparatorOnChain = await this.sdk.
|
|
368
|
+
domainSeparatorOnChain = await this.sdk.chainClient.readContract({
|
|
369
|
+
address: identityRegistryAddress,
|
|
370
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
371
|
+
functionName: 'DOMAIN_SEPARATOR',
|
|
372
|
+
args: [],
|
|
373
|
+
});
|
|
353
374
|
}
|
|
354
375
|
catch {
|
|
355
376
|
// ignore
|
|
356
377
|
}
|
|
357
|
-
// Preflight estimateGas to catch signature/domain/type mismatches early.
|
|
358
|
-
const estimateSetAgentWallet = async (sig) => {
|
|
359
|
-
const fn = identityRegistry.getFunction
|
|
360
|
-
? identityRegistry.getFunction('setAgentWallet')
|
|
361
|
-
: null;
|
|
362
|
-
if (fn?.estimateGas) {
|
|
363
|
-
await fn.estimateGas(BigInt(tokenId), newWallet, BigInt(deadlineValue), sig);
|
|
364
|
-
}
|
|
365
|
-
else if (identityRegistry.estimateGas?.setAgentWallet) {
|
|
366
|
-
await identityRegistry.estimateGas.setAgentWallet(BigInt(tokenId), newWallet, BigInt(deadlineValue), sig);
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
378
|
// Determine signature
|
|
370
379
|
let signature;
|
|
371
380
|
if (opts?.signature) {
|
|
372
|
-
signature
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
381
|
+
const sig = typeof opts.signature === 'string'
|
|
382
|
+
? (opts.signature.startsWith('0x') ? opts.signature : `0x${opts.signature}`)
|
|
383
|
+
: (() => {
|
|
384
|
+
let hex = '0x';
|
|
385
|
+
for (const b of opts.signature) {
|
|
386
|
+
hex += b.toString(16).padStart(2, '0');
|
|
387
|
+
}
|
|
388
|
+
return hex;
|
|
389
|
+
})();
|
|
390
|
+
signature = normalizeEcdsaSignature(sig);
|
|
377
391
|
}
|
|
378
392
|
else {
|
|
379
393
|
// The new wallet MUST sign (EOA path). Support a few domain/type variants to match deployed registries.
|
|
380
|
-
const
|
|
381
|
-
const sdkSignerAddress = await this.sdk.web3Client.getAddress();
|
|
394
|
+
const sdkSignerAddress = await this.sdk.chainClient.getAddress();
|
|
382
395
|
// If no explicit signer was provided, allow the one-wallet case (SDK signer == newWallet)
|
|
383
|
-
if (!
|
|
396
|
+
if (!opts?.newWalletPrivateKey) {
|
|
384
397
|
if (!sdkSignerAddress || sdkSignerAddress.toLowerCase() !== newWallet.toLowerCase()) {
|
|
385
398
|
throw new Error(`The new wallet must sign the EIP-712 message. ` +
|
|
386
|
-
`Pass opts.
|
|
399
|
+
`Pass opts.newWalletPrivateKey or opts.signature. ` +
|
|
387
400
|
`SDK signer is ${sdkSignerAddress || 'unknown'}, newWallet is ${newWallet}.`);
|
|
388
401
|
}
|
|
389
402
|
}
|
|
390
|
-
else {
|
|
391
|
-
const signerAddress = await this.sdk.web3Client.addressOf(signerForNewWallet);
|
|
392
|
-
if (signerAddress.toLowerCase() !== newWallet.toLowerCase()) {
|
|
393
|
-
throw new Error(`newWalletSigner address (${signerAddress}) does not match newWallet (${newWallet}).`);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
403
|
const domainNames = [];
|
|
397
404
|
if (domainName)
|
|
398
405
|
domainNames.push(domainName);
|
|
@@ -403,11 +410,11 @@ export class Agent {
|
|
|
403
410
|
if (domainSeparatorOnChain) {
|
|
404
411
|
const match = domainNames.flatMap((dn) => domainVersions.map((dv) => ({ dn, dv }))).find(({ dn, dv }) => {
|
|
405
412
|
try {
|
|
406
|
-
const computed =
|
|
413
|
+
const computed = hashDomain({
|
|
407
414
|
name: dn,
|
|
408
415
|
version: dv,
|
|
409
416
|
chainId,
|
|
410
|
-
verifyingContract,
|
|
417
|
+
verifyingContract: verifyingContract,
|
|
411
418
|
});
|
|
412
419
|
return computed.toLowerCase() === String(domainSeparatorOnChain).toLowerCase();
|
|
413
420
|
}
|
|
@@ -424,55 +431,87 @@ export class Agent {
|
|
|
424
431
|
const variants = [];
|
|
425
432
|
for (const dn of domainNames) {
|
|
426
433
|
for (const dv of domainVersions) {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
owner,
|
|
431
|
-
deadline: BigInt(deadlineValue),
|
|
432
|
-
chainId,
|
|
433
|
-
verifyingContract,
|
|
434
|
-
domainName: dn,
|
|
435
|
-
domainVersion: dv,
|
|
436
|
-
}));
|
|
437
|
-
variants.push(this.sdk.web3Client.buildAgentWalletSetTypedDataNoOwner({
|
|
438
|
-
agentId: BigInt(tokenId),
|
|
439
|
-
newWallet,
|
|
440
|
-
deadline: BigInt(deadlineValue),
|
|
434
|
+
const domain = {
|
|
435
|
+
name: dn,
|
|
436
|
+
version: dv,
|
|
441
437
|
chainId,
|
|
442
438
|
verifyingContract,
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
439
|
+
};
|
|
440
|
+
variants.push({
|
|
441
|
+
domain,
|
|
442
|
+
primaryType: 'AgentWalletSet',
|
|
443
|
+
types: {
|
|
444
|
+
AgentWalletSet: [
|
|
445
|
+
{ name: 'agentId', type: 'uint256' },
|
|
446
|
+
{ name: 'newWallet', type: 'address' },
|
|
447
|
+
{ name: 'owner', type: 'address' },
|
|
448
|
+
{ name: 'deadline', type: 'uint256' },
|
|
449
|
+
],
|
|
450
|
+
},
|
|
451
|
+
message: {
|
|
452
|
+
agentId: BigInt(tokenId),
|
|
453
|
+
newWallet,
|
|
454
|
+
owner,
|
|
455
|
+
deadline: BigInt(deadlineValue),
|
|
456
|
+
},
|
|
457
|
+
});
|
|
458
|
+
variants.push({
|
|
459
|
+
domain,
|
|
460
|
+
primaryType: 'AgentWalletSet',
|
|
461
|
+
types: {
|
|
462
|
+
AgentWalletSet: [
|
|
463
|
+
{ name: 'agentId', type: 'uint256' },
|
|
464
|
+
{ name: 'newWallet', type: 'address' },
|
|
465
|
+
{ name: 'deadline', type: 'uint256' },
|
|
466
|
+
],
|
|
467
|
+
},
|
|
468
|
+
message: {
|
|
469
|
+
agentId: BigInt(tokenId),
|
|
470
|
+
newWallet,
|
|
471
|
+
deadline: BigInt(deadlineValue),
|
|
472
|
+
},
|
|
473
|
+
});
|
|
446
474
|
}
|
|
447
475
|
}
|
|
448
476
|
let lastError;
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
}
|
|
462
|
-
await estimateSetAgentWallet(sig);
|
|
463
|
-
signature = sig;
|
|
464
|
-
return;
|
|
477
|
+
for (const v of variants) {
|
|
478
|
+
try {
|
|
479
|
+
let sig;
|
|
480
|
+
if (opts?.newWalletPrivateKey) {
|
|
481
|
+
const acc = privateKeyToAccount((opts.newWalletPrivateKey.startsWith('0x')
|
|
482
|
+
? opts.newWalletPrivateKey
|
|
483
|
+
: `0x${opts.newWalletPrivateKey}`));
|
|
484
|
+
sig = normalizeEcdsaSignature((await acc.signTypedData({
|
|
485
|
+
domain: v.domain,
|
|
486
|
+
types: v.types,
|
|
487
|
+
primaryType: v.primaryType,
|
|
488
|
+
message: v.message,
|
|
489
|
+
})));
|
|
465
490
|
}
|
|
466
|
-
|
|
467
|
-
|
|
491
|
+
else {
|
|
492
|
+
sig = await this.sdk.chainClient.signTypedData({
|
|
493
|
+
domain: v.domain,
|
|
494
|
+
types: v.types,
|
|
495
|
+
primaryType: v.primaryType,
|
|
496
|
+
message: v.message,
|
|
497
|
+
});
|
|
468
498
|
}
|
|
499
|
+
const recovered = await recoverTypedDataSigner({
|
|
500
|
+
domain: v.domain,
|
|
501
|
+
types: v.types,
|
|
502
|
+
primaryType: v.primaryType,
|
|
503
|
+
message: v.message,
|
|
504
|
+
signature: sig,
|
|
505
|
+
});
|
|
506
|
+
if (recovered.toLowerCase() !== getAddress(newWallet).toLowerCase()) {
|
|
507
|
+
throw new Error(`EIP-712 recovery mismatch: recovered ${recovered}, expected ${newWallet}`);
|
|
508
|
+
}
|
|
509
|
+
signature = sig;
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
catch (e) {
|
|
513
|
+
lastError = e;
|
|
469
514
|
}
|
|
470
|
-
};
|
|
471
|
-
// Preferred: newWallet signs (spec-aligned)
|
|
472
|
-
await trySignAndEstimate('newWallet');
|
|
473
|
-
// Fallback: some legacy deployments may require the agent owner (tx sender) to sign instead.
|
|
474
|
-
if (!signature && sdkSignerAddress) {
|
|
475
|
-
await trySignAndEstimate('owner');
|
|
476
515
|
}
|
|
477
516
|
if (!signature) {
|
|
478
517
|
const msg = lastError instanceof Error ? lastError.message : String(lastError);
|
|
@@ -480,7 +519,12 @@ export class Agent {
|
|
|
480
519
|
}
|
|
481
520
|
}
|
|
482
521
|
// Call contract function (tx sender is SDK signer: owner/operator)
|
|
483
|
-
const txHash = await this.sdk.
|
|
522
|
+
const txHash = await this.sdk.chainClient.writeContract({
|
|
523
|
+
address: identityRegistryAddress,
|
|
524
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
525
|
+
functionName: 'setAgentWallet',
|
|
526
|
+
args: [BigInt(tokenId), newWallet, BigInt(deadlineValue), signature],
|
|
527
|
+
});
|
|
484
528
|
// Update local registration file
|
|
485
529
|
this.registrationFile.walletAddress = newWallet;
|
|
486
530
|
this.registrationFile.walletChainId = chainId;
|
|
@@ -560,7 +604,7 @@ export class Agent {
|
|
|
560
604
|
// Agent already registered - update registration file and redeploy
|
|
561
605
|
// Option 2D: Add logging and timeout handling
|
|
562
606
|
const chainId = await this.sdk.chainId();
|
|
563
|
-
const identityRegistryAddress =
|
|
607
|
+
const identityRegistryAddress = this.sdk.identityRegistryAddress();
|
|
564
608
|
const ipfsCid = await this.sdk.ipfsClient.addRegistrationFile(this.registrationFile, chainId, identityRegistryAddress);
|
|
565
609
|
// Update metadata on-chain if changed
|
|
566
610
|
// Only send transactions for dirty (changed) metadata to save gas
|
|
@@ -574,11 +618,16 @@ export class Agent {
|
|
|
574
618
|
}
|
|
575
619
|
// Update agent URI on-chain
|
|
576
620
|
const { tokenId } = parseAgentId(this.registrationFile.agentId);
|
|
577
|
-
const txHash = await this.sdk.
|
|
621
|
+
const txHash = await this.sdk.chainClient.writeContract({
|
|
622
|
+
address: identityRegistryAddress,
|
|
623
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
624
|
+
functionName: 'setAgentURI',
|
|
625
|
+
args: [BigInt(tokenId), `ipfs://${ipfsCid}`],
|
|
626
|
+
});
|
|
578
627
|
// Wait for transaction to be confirmed (30 second timeout like Python)
|
|
579
628
|
// If timeout, continue - transaction was sent and will eventually confirm
|
|
580
629
|
try {
|
|
581
|
-
await this.sdk.
|
|
630
|
+
await this.sdk.chainClient.waitForTransaction({ hash: txHash, timeoutMs: TIMEOUTS.TRANSACTION_WAIT });
|
|
582
631
|
}
|
|
583
632
|
catch {
|
|
584
633
|
// Transaction was sent and will eventually confirm - continue silently
|
|
@@ -596,13 +645,18 @@ export class Agent {
|
|
|
596
645
|
await this._registerWithoutUri();
|
|
597
646
|
// Step 2: Upload to IPFS
|
|
598
647
|
const chainId = await this.sdk.chainId();
|
|
599
|
-
const identityRegistryAddress =
|
|
648
|
+
const identityRegistryAddress = this.sdk.identityRegistryAddress();
|
|
600
649
|
const ipfsCid = await this.sdk.ipfsClient.addRegistrationFile(this.registrationFile, chainId, identityRegistryAddress);
|
|
601
650
|
// Step 3: Set agent URI on-chain
|
|
602
651
|
const { tokenId } = parseAgentId(this.registrationFile.agentId);
|
|
603
|
-
const txHash = await this.sdk.
|
|
652
|
+
const txHash = await this.sdk.chainClient.writeContract({
|
|
653
|
+
address: identityRegistryAddress,
|
|
654
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
655
|
+
functionName: 'setAgentURI',
|
|
656
|
+
args: [BigInt(tokenId), `ipfs://${ipfsCid}`],
|
|
657
|
+
});
|
|
604
658
|
// Wait for transaction to be confirmed
|
|
605
|
-
await this.sdk.
|
|
659
|
+
await this.sdk.chainClient.waitForTransaction({ hash: txHash });
|
|
606
660
|
// Clear dirty flags
|
|
607
661
|
this._lastRegisteredWallet = this.walletAddress;
|
|
608
662
|
this._lastRegisteredEns = this.ensEndpoint;
|
|
@@ -637,7 +691,13 @@ export class Agent {
|
|
|
637
691
|
throw new Error('Agent must be registered before setting URI');
|
|
638
692
|
}
|
|
639
693
|
const { tokenId } = parseAgentId(this.registrationFile.agentId);
|
|
640
|
-
|
|
694
|
+
const identityRegistryAddress = this.sdk.identityRegistryAddress();
|
|
695
|
+
await this.sdk.chainClient.writeContract({
|
|
696
|
+
address: identityRegistryAddress,
|
|
697
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
698
|
+
functionName: 'setAgentURI',
|
|
699
|
+
args: [BigInt(tokenId), agentURI],
|
|
700
|
+
});
|
|
641
701
|
this.registrationFile.agentURI = agentURI;
|
|
642
702
|
this.registrationFile.updatedAt = Math.floor(Date.now() / 1000);
|
|
643
703
|
}
|
|
@@ -649,13 +709,10 @@ export class Agent {
|
|
|
649
709
|
throw new Error('Agent must be registered before transfer');
|
|
650
710
|
}
|
|
651
711
|
const { tokenId } = parseAgentId(this.registrationFile.agentId);
|
|
652
|
-
const currentOwner = this.sdk.
|
|
653
|
-
if (!currentOwner) {
|
|
654
|
-
throw new Error('No signer available');
|
|
655
|
-
}
|
|
712
|
+
const currentOwner = await this.sdk.chainClient.ensureAddress();
|
|
656
713
|
// Validate address - normalize to lowercase first
|
|
657
714
|
const normalizedAddress = newOwner.toLowerCase();
|
|
658
|
-
if (!this.sdk.
|
|
715
|
+
if (!this.sdk.chainClient.isAddress(normalizedAddress)) {
|
|
659
716
|
throw new Error(`Invalid address: ${newOwner}`);
|
|
660
717
|
}
|
|
661
718
|
// Validate not zero address (check before expensive operations)
|
|
@@ -663,13 +720,18 @@ export class Agent {
|
|
|
663
720
|
throw new Error('Cannot transfer agent to zero address');
|
|
664
721
|
}
|
|
665
722
|
// Convert to checksum format
|
|
666
|
-
const checksumAddress = this.sdk.
|
|
723
|
+
const checksumAddress = this.sdk.chainClient.toChecksumAddress(normalizedAddress);
|
|
667
724
|
// Validate not transferring to self
|
|
668
725
|
if (checksumAddress.toLowerCase() === currentOwner.toLowerCase()) {
|
|
669
726
|
throw new Error('Cannot transfer agent to yourself');
|
|
670
727
|
}
|
|
671
|
-
const
|
|
672
|
-
const txHash = await this.sdk.
|
|
728
|
+
const identityRegistryAddress = this.sdk.identityRegistryAddress();
|
|
729
|
+
const txHash = await this.sdk.chainClient.writeContract({
|
|
730
|
+
address: identityRegistryAddress,
|
|
731
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
732
|
+
functionName: 'transferFrom',
|
|
733
|
+
args: [currentOwner, checksumAddress, BigInt(tokenId)],
|
|
734
|
+
});
|
|
673
735
|
return {
|
|
674
736
|
txHash,
|
|
675
737
|
from: currentOwner,
|
|
@@ -683,23 +745,28 @@ export class Agent {
|
|
|
683
745
|
async _registerWithoutUri() {
|
|
684
746
|
// Collect metadata for registration
|
|
685
747
|
const metadataEntries = this._collectMetadataForRegistration();
|
|
686
|
-
|
|
687
|
-
const identityRegistry = this.sdk.getIdentityRegistry();
|
|
748
|
+
const identityRegistryAddress = this.sdk.identityRegistryAddress();
|
|
688
749
|
// If we have metadata, use register(string, tuple[])
|
|
689
750
|
// Otherwise use register() with no args
|
|
690
751
|
let txHash;
|
|
691
752
|
if (metadataEntries.length > 0) {
|
|
692
|
-
txHash = await this.sdk.
|
|
693
|
-
|
|
694
|
-
|
|
753
|
+
txHash = await this.sdk.chainClient.writeContract({
|
|
754
|
+
address: identityRegistryAddress,
|
|
755
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
756
|
+
functionName: 'register',
|
|
757
|
+
args: ['', metadataEntries],
|
|
758
|
+
});
|
|
695
759
|
}
|
|
696
760
|
else {
|
|
697
|
-
txHash = await this.sdk.
|
|
698
|
-
|
|
699
|
-
|
|
761
|
+
txHash = await this.sdk.chainClient.writeContract({
|
|
762
|
+
address: identityRegistryAddress,
|
|
763
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
764
|
+
functionName: 'register',
|
|
765
|
+
args: [],
|
|
766
|
+
});
|
|
700
767
|
}
|
|
701
768
|
// Wait for transaction
|
|
702
|
-
const receipt = await this.sdk.
|
|
769
|
+
const receipt = await this.sdk.chainClient.waitForTransaction({ hash: txHash });
|
|
703
770
|
// Extract agent ID from events
|
|
704
771
|
const agentId = this._extractAgentIdFromReceipt(receipt);
|
|
705
772
|
// Update registration file
|
|
@@ -710,11 +777,15 @@ export class Agent {
|
|
|
710
777
|
async _registerWithUri(agentUri) {
|
|
711
778
|
// Collect metadata for registration
|
|
712
779
|
const metadataEntries = this._collectMetadataForRegistration();
|
|
713
|
-
|
|
714
|
-
const
|
|
715
|
-
|
|
780
|
+
const identityRegistryAddress = this.sdk.identityRegistryAddress();
|
|
781
|
+
const txHash = await this.sdk.chainClient.writeContract({
|
|
782
|
+
address: identityRegistryAddress,
|
|
783
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
784
|
+
functionName: 'register',
|
|
785
|
+
args: [agentUri, metadataEntries],
|
|
786
|
+
});
|
|
716
787
|
// Wait for transaction
|
|
717
|
-
const receipt = await this.sdk.
|
|
788
|
+
const receipt = await this.sdk.chainClient.waitForTransaction({ hash: txHash });
|
|
718
789
|
// Extract agent ID from events
|
|
719
790
|
const agentId = this._extractAgentIdFromReceipt(receipt);
|
|
720
791
|
// Update registration file
|
|
@@ -727,16 +798,21 @@ export class Agent {
|
|
|
727
798
|
async _updateMetadataOnChain() {
|
|
728
799
|
const metadataEntries = this._collectMetadataForRegistration();
|
|
729
800
|
const { tokenId } = parseAgentId(this.registrationFile.agentId);
|
|
730
|
-
const
|
|
801
|
+
const identityRegistryAddress = this.sdk.identityRegistryAddress();
|
|
731
802
|
// Update metadata one by one (like Python SDK)
|
|
732
803
|
// Only send transactions for dirty (changed) metadata keys
|
|
733
804
|
for (const entry of metadataEntries) {
|
|
734
805
|
if (this._dirtyMetadata.has(entry.metadataKey)) {
|
|
735
|
-
const txHash = await this.sdk.
|
|
806
|
+
const txHash = await this.sdk.chainClient.writeContract({
|
|
807
|
+
address: identityRegistryAddress,
|
|
808
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
809
|
+
functionName: 'setMetadata',
|
|
810
|
+
args: [BigInt(tokenId), entry.metadataKey, entry.metadataValue],
|
|
811
|
+
});
|
|
736
812
|
// Wait with 30 second timeout (like Python SDK)
|
|
737
813
|
// If timeout, log warning but continue - transaction was sent and will eventually confirm
|
|
738
814
|
try {
|
|
739
|
-
await this.sdk.
|
|
815
|
+
await this.sdk.chainClient.waitForTransaction({ hash: txHash, timeoutMs: TIMEOUTS.TRANSACTION_WAIT });
|
|
740
816
|
}
|
|
741
817
|
catch (error) {
|
|
742
818
|
// Transaction was sent and will eventually confirm - continue silently
|
|
@@ -770,19 +846,22 @@ export class Agent {
|
|
|
770
846
|
return entries;
|
|
771
847
|
}
|
|
772
848
|
_extractAgentIdFromReceipt(receipt) {
|
|
773
|
-
// Parse events from receipt to find Registered event
|
|
774
|
-
const identityRegistry = this.sdk.getIdentityRegistry();
|
|
775
849
|
const transferEventTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'; // Transfer(address,address,uint256)
|
|
776
850
|
// Find the event in the logs
|
|
777
851
|
for (const log of receipt.logs || []) {
|
|
778
852
|
try {
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
853
|
+
if (!log.topics || log.topics.length === 0) {
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
const parsed = decodeEventLog({
|
|
857
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
858
|
+
data: log.data,
|
|
859
|
+
topics: log.topics,
|
|
783
860
|
});
|
|
784
|
-
if (parsed && parsed.
|
|
785
|
-
|
|
861
|
+
if (parsed && parsed.eventName === 'Registered') {
|
|
862
|
+
const agentId = parsed.args?.agentId;
|
|
863
|
+
if (agentId !== undefined)
|
|
864
|
+
return BigInt(agentId);
|
|
786
865
|
}
|
|
787
866
|
}
|
|
788
867
|
catch {
|
|
@@ -791,10 +870,10 @@ export class Agent {
|
|
|
791
870
|
const topics = Array.isArray(log.topics) ? log.topics : [];
|
|
792
871
|
// Transfer event has topic[0] = Transfer signature, topic[3] = tokenId (if 4 topics)
|
|
793
872
|
if (topics.length >= 4) {
|
|
794
|
-
const topic0 =
|
|
873
|
+
const topic0 = String(topics[0]);
|
|
795
874
|
if (topic0 === transferEventTopic || topic0.toLowerCase() === transferEventTopic.toLowerCase()) {
|
|
796
875
|
// Extract tokenId from topic[3]
|
|
797
|
-
const tokenIdHex =
|
|
876
|
+
const tokenIdHex = String(topics[3]);
|
|
798
877
|
// Remove 0x prefix if present and convert
|
|
799
878
|
const tokenIdStr = tokenIdHex.startsWith('0x') ? tokenIdHex.slice(2) : tokenIdHex;
|
|
800
879
|
return BigInt('0x' + tokenIdStr);
|