@agentic-trust/agentic-trust-sdk 1.0.43
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/LICENSE +22 -0
- package/README.md +213 -0
- package/abis/BaseRegistrarImplementation.json +1013 -0
- package/abis/ETHRegistrarController.json +1004 -0
- package/abis/IdentityRegistry.json +1044 -0
- package/abis/NameWrapper.json +2026 -0
- package/abis/PublicResolver.json +1772 -0
- package/abis/ReputationRegistry.json +701 -0
- package/abis/ValidationRegistry.json +505 -0
- package/dist/AIAgentAssociationClient.d.ts +58 -0
- package/dist/AIAgentAssociationClient.d.ts.map +1 -0
- package/dist/AIAgentAssociationClient.js +100 -0
- package/dist/AIAgentAssociationClient.js.map +1 -0
- package/dist/AIAgentDiscoveryClient.d.ts +673 -0
- package/dist/AIAgentDiscoveryClient.d.ts.map +1 -0
- package/dist/AIAgentDiscoveryClient.js +3184 -0
- package/dist/AIAgentDiscoveryClient.js.map +1 -0
- package/dist/AIAgentENSClient.d.ts +149 -0
- package/dist/AIAgentENSClient.d.ts.map +1 -0
- package/dist/AIAgentENSClient.js +958 -0
- package/dist/AIAgentENSClient.js.map +1 -0
- package/dist/AIAgentIdentityClient.d.ts +159 -0
- package/dist/AIAgentIdentityClient.d.ts.map +1 -0
- package/dist/AIAgentIdentityClient.js +660 -0
- package/dist/AIAgentIdentityClient.js.map +1 -0
- package/dist/AIAgentL2ENSDurenClient.d.ts +120 -0
- package/dist/AIAgentL2ENSDurenClient.d.ts.map +1 -0
- package/dist/AIAgentL2ENSDurenClient.js +735 -0
- package/dist/AIAgentL2ENSDurenClient.js.map +1 -0
- package/dist/AIAgentL2ENSNamespaceClient.d.ts +58 -0
- package/dist/AIAgentL2ENSNamespaceClient.d.ts.map +1 -0
- package/dist/AIAgentL2ENSNamespaceClient.js +214 -0
- package/dist/AIAgentL2ENSNamespaceClient.js.map +1 -0
- package/dist/AIAgentReputationClient.d.ts +69 -0
- package/dist/AIAgentReputationClient.d.ts.map +1 -0
- package/dist/AIAgentReputationClient.js +203 -0
- package/dist/AIAgentReputationClient.js.map +1 -0
- package/dist/AIAgentValidationClient.d.ts +60 -0
- package/dist/AIAgentValidationClient.d.ts.map +1 -0
- package/dist/AIAgentValidationClient.js +123 -0
- package/dist/AIAgentValidationClient.js.map +1 -0
- package/dist/OrgIdentityClient.d.ts +27 -0
- package/dist/OrgIdentityClient.d.ts.map +1 -0
- package/dist/OrgIdentityClient.js +169 -0
- package/dist/OrgIdentityClient.js.map +1 -0
- package/dist/abis/BaseRegistrarImplementation.json +1013 -0
- package/dist/abis/ETHRegistrarController.json +1004 -0
- package/dist/abis/IdentityRegistry.json +1044 -0
- package/dist/abis/NameWrapper.json +2026 -0
- package/dist/abis/PublicResolver.json +1772 -0
- package/dist/abis/ReputationRegistry.json +701 -0
- package/dist/abis/ValidationRegistry.json +505 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/schema.d.ts +13 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +696 -0
- package/dist/schema.js.map +1 -0
- package/dist/schemaKb.d.ts +12 -0
- package/dist/schemaKb.d.ts.map +1 -0
- package/dist/schemaKb.js +593 -0
- package/dist/schemaKb.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/utils/did8004.d.ts +57 -0
- package/dist/utils/did8004.d.ts.map +1 -0
- package/dist/utils/did8004.js +127 -0
- package/dist/utils/did8004.js.map +1 -0
- package/dist/utils/didEns.d.ts +46 -0
- package/dist/utils/didEns.d.ts.map +1 -0
- package/dist/utils/didEns.js +107 -0
- package/dist/utils/didEns.js.map +1 -0
- package/dist/utils/didEthr.d.ts +40 -0
- package/dist/utils/didEthr.d.ts.map +1 -0
- package/dist/utils/didEthr.js +87 -0
- package/dist/utils/didEthr.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentic Trust SDK - Identity Client
|
|
3
|
+
* Extends the base ERC-8004 IdentityClient with AA-centric helpers.
|
|
4
|
+
*
|
|
5
|
+
* Uses AccountProvider (Ports & Adapters pattern) for chain I/O.
|
|
6
|
+
*/
|
|
7
|
+
import { createPublicClient, http, hexToString, getAddress, } from 'viem';
|
|
8
|
+
import { sepolia, baseSepolia, optimismSepolia, linea, lineaSepolia } from 'viem/chains';
|
|
9
|
+
import { BaseIdentityClient, ViemAccountProvider, } from '@agentic-trust/8004-sdk';
|
|
10
|
+
import IdentityRegistryABI from './abis/IdentityRegistry.json';
|
|
11
|
+
function getChainById(chainId) {
|
|
12
|
+
switch (chainId) {
|
|
13
|
+
case 11155111: // ETH Sepolia
|
|
14
|
+
return sepolia;
|
|
15
|
+
case 84532: // Base Sepolia
|
|
16
|
+
return baseSepolia;
|
|
17
|
+
case 11155420: // Optimism Sepolia
|
|
18
|
+
return optimismSepolia;
|
|
19
|
+
case 59144: // Linea Mainnet
|
|
20
|
+
return linea;
|
|
21
|
+
case 59141: // Linea Sepolia
|
|
22
|
+
return lineaSepolia;
|
|
23
|
+
default:
|
|
24
|
+
console.warn(`Unknown chainId ${chainId}, defaulting to ETH Sepolia`);
|
|
25
|
+
return sepolia;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export class AIAgentIdentityClient extends BaseIdentityClient {
|
|
29
|
+
chain = null;
|
|
30
|
+
identityRegistryAddress;
|
|
31
|
+
publicClient = null;
|
|
32
|
+
walletClient = null;
|
|
33
|
+
// accountProvider is protected in BaseIdentityClient, so we need to keep it accessible
|
|
34
|
+
accountProvider;
|
|
35
|
+
constructor(options) {
|
|
36
|
+
let accountProvider;
|
|
37
|
+
let chain = null;
|
|
38
|
+
let publicClient = null;
|
|
39
|
+
let walletClient = null;
|
|
40
|
+
let identityRegistryAddress;
|
|
41
|
+
if ('accountProvider' in options) {
|
|
42
|
+
// Option 1: Use provided AccountProvider (recommended)
|
|
43
|
+
accountProvider = options.accountProvider;
|
|
44
|
+
identityRegistryAddress = options.identityRegistryAddress;
|
|
45
|
+
// Try to extract publicClient from AccountProvider if it's a ViemAccountProvider
|
|
46
|
+
const viemProvider = accountProvider;
|
|
47
|
+
if (viemProvider.publicClient) {
|
|
48
|
+
publicClient = viemProvider.publicClient;
|
|
49
|
+
}
|
|
50
|
+
if (viemProvider.walletClient) {
|
|
51
|
+
walletClient = viemProvider.walletClient;
|
|
52
|
+
}
|
|
53
|
+
if (viemProvider.chainConfig?.chain) {
|
|
54
|
+
chain = viemProvider.chainConfig.chain;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else if ('publicClient' in options) {
|
|
58
|
+
// Option 2: Use viem clients directly (simplest, native viem)
|
|
59
|
+
publicClient = options.publicClient;
|
|
60
|
+
walletClient = options.walletClient ?? null;
|
|
61
|
+
identityRegistryAddress = options.identityRegistryAddress;
|
|
62
|
+
// Create ChainConfig
|
|
63
|
+
const chainConfig = options.chainConfig || {
|
|
64
|
+
id: publicClient.chain?.id || 11155111,
|
|
65
|
+
rpcUrl: publicClient.transport?.url || '',
|
|
66
|
+
name: publicClient.chain?.name || 'Unknown',
|
|
67
|
+
chain: publicClient.chain || undefined,
|
|
68
|
+
};
|
|
69
|
+
// Create ViemAccountProvider from the clients
|
|
70
|
+
accountProvider = new ViemAccountProvider({
|
|
71
|
+
publicClient,
|
|
72
|
+
walletClient: walletClient ?? null,
|
|
73
|
+
account: walletClient?.account,
|
|
74
|
+
chainConfig,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
// Option 3: Legacy pattern - create from chainId/rpcUrl
|
|
79
|
+
chain = getChainById(options.chainId);
|
|
80
|
+
// @ts-ignore - viem version compatibility issue
|
|
81
|
+
publicClient = createPublicClient({ chain, transport: http(options.rpcUrl) });
|
|
82
|
+
walletClient = options.walletClient ?? null;
|
|
83
|
+
// Create ChainConfig
|
|
84
|
+
const chainConfig = {
|
|
85
|
+
id: options.chainId,
|
|
86
|
+
rpcUrl: options.rpcUrl,
|
|
87
|
+
name: chain.name,
|
|
88
|
+
chain: chain,
|
|
89
|
+
bundlerUrl: options.bundlerUrl,
|
|
90
|
+
paymasterUrl: options.paymasterUrl,
|
|
91
|
+
};
|
|
92
|
+
// Create ViemAccountProvider
|
|
93
|
+
accountProvider = new ViemAccountProvider({
|
|
94
|
+
publicClient,
|
|
95
|
+
walletClient: walletClient ?? null,
|
|
96
|
+
account: options.account || walletClient?.account,
|
|
97
|
+
chainConfig,
|
|
98
|
+
});
|
|
99
|
+
identityRegistryAddress = options.identityRegistryAddress;
|
|
100
|
+
}
|
|
101
|
+
// Pass accountProvider to BaseIdentityClient
|
|
102
|
+
super(accountProvider, identityRegistryAddress);
|
|
103
|
+
this.chain = chain;
|
|
104
|
+
this.publicClient = publicClient;
|
|
105
|
+
this.walletClient = walletClient;
|
|
106
|
+
this.identityRegistryAddress = identityRegistryAddress;
|
|
107
|
+
this.accountProvider = accountProvider;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get metadata using AccountProvider
|
|
111
|
+
*/
|
|
112
|
+
async getMetadata(agentId, key) {
|
|
113
|
+
const bytes = await this.accountProvider.call({
|
|
114
|
+
to: this.identityRegistryAddress,
|
|
115
|
+
abi: IdentityRegistryABI,
|
|
116
|
+
functionName: 'getMetadata',
|
|
117
|
+
args: [agentId, key],
|
|
118
|
+
});
|
|
119
|
+
// Most keys store UTF-8 encoded bytes, but some (like reserved agentWallet) store raw bytes.
|
|
120
|
+
// Avoid lossy UTF-8 decoding by falling back to raw hex if decoding fails.
|
|
121
|
+
const raw = String(bytes || '').trim();
|
|
122
|
+
if (!raw || raw === '0x')
|
|
123
|
+
return '';
|
|
124
|
+
// Common case: reserved agentWallet is stored as abi.encodePacked(address) => 20 bytes.
|
|
125
|
+
// If the raw bytes look like a 20-byte hex, return it as a checksummed address string.
|
|
126
|
+
if (key === 'agentWallet' && /^0x[0-9a-fA-F]{40}$/.test(raw)) {
|
|
127
|
+
return getAddress(raw);
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
return hexToString(raw);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return raw;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get the verified agent wallet address (IdentityRegistry.getAgentWallet).
|
|
138
|
+
* This is the canonical way to read the "agentWallet" value as an address.
|
|
139
|
+
*/
|
|
140
|
+
async getAgentWallet(agentId) {
|
|
141
|
+
const wallet = await this.accountProvider.call({
|
|
142
|
+
to: this.identityRegistryAddress,
|
|
143
|
+
abi: IdentityRegistryABI,
|
|
144
|
+
functionName: 'getAgentWallet',
|
|
145
|
+
args: [agentId],
|
|
146
|
+
});
|
|
147
|
+
return getAddress(wallet);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get all available metadata from the Agent NFT by trying a comprehensive list of common keys.
|
|
151
|
+
* Returns a record of all metadata key-value pairs that exist on-chain.
|
|
152
|
+
*
|
|
153
|
+
* Processes requests in batches to avoid rate limiting.
|
|
154
|
+
*
|
|
155
|
+
* IMPORTANT: This method makes many on-chain RPC calls and should ONLY be used
|
|
156
|
+
* for detailed agent views (via loadAgentDetail). It should NOT be called for
|
|
157
|
+
* list queries - use GraphQL/discovery data instead.
|
|
158
|
+
*/
|
|
159
|
+
async getAllMetadata(agentId) {
|
|
160
|
+
// Comprehensive list of common metadata keys to check
|
|
161
|
+
const METADATA_KEYS = [
|
|
162
|
+
// Standard ERC-8004 fields
|
|
163
|
+
'agentName',
|
|
164
|
+
'agentAccount',
|
|
165
|
+
'description',
|
|
166
|
+
'image',
|
|
167
|
+
'external_url',
|
|
168
|
+
'version',
|
|
169
|
+
'type',
|
|
170
|
+
'name',
|
|
171
|
+
'url',
|
|
172
|
+
'website',
|
|
173
|
+
'email',
|
|
174
|
+
'twitter',
|
|
175
|
+
'github',
|
|
176
|
+
'discord',
|
|
177
|
+
'telegram',
|
|
178
|
+
'metadata',
|
|
179
|
+
'attributes',
|
|
180
|
+
'createdAt',
|
|
181
|
+
'updatedAt',
|
|
182
|
+
// Additional common fields
|
|
183
|
+
'tags',
|
|
184
|
+
'glbUrl',
|
|
185
|
+
'glbCid',
|
|
186
|
+
'glbFileName',
|
|
187
|
+
'glbSource',
|
|
188
|
+
'agentWallet',
|
|
189
|
+
'capabilities',
|
|
190
|
+
'role',
|
|
191
|
+
'rating',
|
|
192
|
+
'pricing',
|
|
193
|
+
'pka',
|
|
194
|
+
'uri',
|
|
195
|
+
'endpoints',
|
|
196
|
+
'supportedTrust',
|
|
197
|
+
'registrations',
|
|
198
|
+
'agentUrl',
|
|
199
|
+
'contractAddress',
|
|
200
|
+
'did',
|
|
201
|
+
'didIdentity',
|
|
202
|
+
'didAccount',
|
|
203
|
+
'didName',
|
|
204
|
+
'active',
|
|
205
|
+
'x402support',
|
|
206
|
+
'mcp',
|
|
207
|
+
'a2aEndpoint',
|
|
208
|
+
'mcpEndpoint',
|
|
209
|
+
'ensEndpoint',
|
|
210
|
+
'agentAccountEndpoint',
|
|
211
|
+
// Agent registration metadata
|
|
212
|
+
'registeredBy',
|
|
213
|
+
'registryNamespace',
|
|
214
|
+
'uaid',
|
|
215
|
+
];
|
|
216
|
+
const metadata = {};
|
|
217
|
+
// Fast path: use viem multicall when available to reduce RPC round-trips.
|
|
218
|
+
// This turns "N keys => N RPC calls" into ~1 RPC call (provider-dependent).
|
|
219
|
+
if (this.publicClient && typeof this.publicClient.multicall === 'function') {
|
|
220
|
+
try {
|
|
221
|
+
const contracts = METADATA_KEYS.map((key) => ({
|
|
222
|
+
address: this.identityRegistryAddress,
|
|
223
|
+
abi: IdentityRegistryABI,
|
|
224
|
+
functionName: 'getMetadata',
|
|
225
|
+
args: [agentId, key],
|
|
226
|
+
}));
|
|
227
|
+
const results = await this.publicClient.multicall({
|
|
228
|
+
contracts,
|
|
229
|
+
allowFailure: true,
|
|
230
|
+
});
|
|
231
|
+
for (let i = 0; i < METADATA_KEYS.length; i += 1) {
|
|
232
|
+
const key = METADATA_KEYS[i];
|
|
233
|
+
const r = results?.[i];
|
|
234
|
+
const ok = r && (r.status === 'success' || r.status === undefined);
|
|
235
|
+
const raw = ok ? String(r.result ?? '').trim() : '';
|
|
236
|
+
if (!raw || raw === '0x')
|
|
237
|
+
continue;
|
|
238
|
+
// Same decoding behavior as getMetadata()
|
|
239
|
+
if (key === 'agentWallet' && /^0x[0-9a-fA-F]{40}$/.test(raw)) {
|
|
240
|
+
metadata[key] = getAddress(raw);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const decoded = hexToString(raw);
|
|
245
|
+
if (decoded && decoded.trim().length > 0) {
|
|
246
|
+
metadata[key] = decoded;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
metadata[key] = raw;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
metadata[key] = raw;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return metadata;
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
// Fall back to per-key calls below
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Process requests in batches to avoid rate limiting
|
|
263
|
+
// Batch size: 5 requests at a time
|
|
264
|
+
// Delay between batches: 200ms
|
|
265
|
+
const BATCH_SIZE = 5;
|
|
266
|
+
const BATCH_DELAY_MS = 200;
|
|
267
|
+
for (let i = 0; i < METADATA_KEYS.length; i += BATCH_SIZE) {
|
|
268
|
+
const batch = METADATA_KEYS.slice(i, i + BATCH_SIZE);
|
|
269
|
+
// Process batch in parallel
|
|
270
|
+
const batchPromises = batch.map(async (key) => {
|
|
271
|
+
try {
|
|
272
|
+
const value = await this.getMetadata(agentId, key);
|
|
273
|
+
if (value && value.trim().length > 0) {
|
|
274
|
+
return { key, value };
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
// Check if it's a rate limit error (429)
|
|
280
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
281
|
+
if (errorMessage.includes('429') || errorMessage.includes('Too Many Requests')) {
|
|
282
|
+
// For rate limit errors, wait longer before retrying
|
|
283
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
284
|
+
try {
|
|
285
|
+
const value = await this.getMetadata(agentId, key);
|
|
286
|
+
if (value && value.trim().length > 0) {
|
|
287
|
+
return { key, value };
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
catch (retryError) {
|
|
291
|
+
// Silently skip on retry failure
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// Silently skip if metadata key doesn't exist or fails
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
const batchResults = await Promise.all(batchPromises);
|
|
299
|
+
// Collect successful results from this batch
|
|
300
|
+
for (const result of batchResults) {
|
|
301
|
+
if (result) {
|
|
302
|
+
metadata[result.key] = result.value;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// Delay before next batch (except for the last batch)
|
|
306
|
+
if (i + BATCH_SIZE < METADATA_KEYS.length) {
|
|
307
|
+
await new Promise(resolve => setTimeout(resolve, BATCH_DELAY_MS));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return metadata;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Encode function call data using AccountProvider
|
|
314
|
+
*/
|
|
315
|
+
async encodeFunctionData(abi, functionName, args) {
|
|
316
|
+
return await this.accountProvider.encodeFunctionData({
|
|
317
|
+
abi,
|
|
318
|
+
functionName,
|
|
319
|
+
args,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Legacy method - delegates to encodeFunctionData
|
|
324
|
+
* @deprecated Use encodeFunctionData instead
|
|
325
|
+
*/
|
|
326
|
+
encodeCall(abi, functionName, args) {
|
|
327
|
+
// This is a synchronous method, but encodeFunctionData is async
|
|
328
|
+
// For backward compatibility, we'll use ethers for now
|
|
329
|
+
// TODO: Consider making this async or removing it
|
|
330
|
+
const { ethers } = require('ethers');
|
|
331
|
+
const iface = new ethers.Interface(abi);
|
|
332
|
+
return iface.encodeFunctionData(functionName, args);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Encode register calldata without sending (for bundler/AA - like EAS SDK pattern)
|
|
336
|
+
* This override exists in the Agentic Trust SDK to keep AA helpers here.
|
|
337
|
+
*/
|
|
338
|
+
async encodeRegisterWithMetadata(tokenUri, metadata = []) {
|
|
339
|
+
// Format metadata: convert string values to hex strings (Viem expects hex for bytes)
|
|
340
|
+
const metadataFormatted = metadata.map(m => {
|
|
341
|
+
// Use stringToBytes from base class (via inheritance)
|
|
342
|
+
const bytes = this.stringToBytes(m.value);
|
|
343
|
+
// Convert to hex string (Viem requires hex strings, not Uint8Array)
|
|
344
|
+
const hexString = this.bytesToHex(bytes);
|
|
345
|
+
return {
|
|
346
|
+
// Updated ABI uses struct fields: { metadataKey, metadataValue }
|
|
347
|
+
metadataKey: m.key,
|
|
348
|
+
metadataValue: hexString,
|
|
349
|
+
};
|
|
350
|
+
});
|
|
351
|
+
// Use AccountProvider's encodeFunctionData
|
|
352
|
+
return await this.accountProvider.encodeFunctionData({
|
|
353
|
+
abi: IdentityRegistryABI,
|
|
354
|
+
functionName: 'register',
|
|
355
|
+
args: [tokenUri, metadataFormatted],
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
async encodeRegister(name, agentAccount, tokenUri) {
|
|
359
|
+
console.info("name: ", name);
|
|
360
|
+
console.info("agentAccount: ", agentAccount);
|
|
361
|
+
return await this.encodeRegisterWithMetadata(tokenUri, [{ key: 'agentName', value: name }, { key: 'agentAccount', value: agentAccount }]);
|
|
362
|
+
}
|
|
363
|
+
async prepareRegisterCalls(name, agentAccount, tokenUri, additionalMetadata) {
|
|
364
|
+
const metadata = [
|
|
365
|
+
{ key: 'agentName', value: name },
|
|
366
|
+
{ key: 'agentAccount', value: agentAccount },
|
|
367
|
+
...(additionalMetadata || []),
|
|
368
|
+
];
|
|
369
|
+
const data = await this.encodeRegisterWithMetadata(tokenUri, metadata);
|
|
370
|
+
const calls = [];
|
|
371
|
+
calls.push({
|
|
372
|
+
to: this.identityRegistryAddress,
|
|
373
|
+
data: data
|
|
374
|
+
});
|
|
375
|
+
return { calls };
|
|
376
|
+
}
|
|
377
|
+
async encodeSetRegistrationUri(agentId, uri) {
|
|
378
|
+
const data = await this.accountProvider.encodeFunctionData({
|
|
379
|
+
abi: IdentityRegistryABI,
|
|
380
|
+
// Updated ABI name is setAgentURI (capital URI)
|
|
381
|
+
functionName: 'setAgentURI',
|
|
382
|
+
args: [agentId, uri],
|
|
383
|
+
});
|
|
384
|
+
return data;
|
|
385
|
+
}
|
|
386
|
+
async prepareSetRegistrationUriCalls(agentId, uri) {
|
|
387
|
+
const calls = [];
|
|
388
|
+
const data = await this.encodeSetRegistrationUri(agentId, uri);
|
|
389
|
+
calls.push({
|
|
390
|
+
to: this.identityRegistryAddress,
|
|
391
|
+
data: data
|
|
392
|
+
});
|
|
393
|
+
return { calls };
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Encode `setAgentWallet` calldata without sending.
|
|
397
|
+
*
|
|
398
|
+
* IdentityRegistry ABI:
|
|
399
|
+
* setAgentWallet(uint256 agentId, address newWallet, uint256 deadline, bytes signature)
|
|
400
|
+
*/
|
|
401
|
+
async encodeSetAgentWallet(agentId, newWallet, deadline, signature) {
|
|
402
|
+
const data = await this.accountProvider.encodeFunctionData({
|
|
403
|
+
abi: IdentityRegistryABI,
|
|
404
|
+
functionName: 'setAgentWallet',
|
|
405
|
+
args: [agentId, newWallet, deadline, signature],
|
|
406
|
+
});
|
|
407
|
+
return data;
|
|
408
|
+
}
|
|
409
|
+
async prepareSetAgentWalletCalls(agentId, newWallet, deadline, signature) {
|
|
410
|
+
const calls = [];
|
|
411
|
+
const data = await this.encodeSetAgentWallet(agentId, newWallet, deadline, signature);
|
|
412
|
+
calls.push({
|
|
413
|
+
to: this.identityRegistryAddress,
|
|
414
|
+
data,
|
|
415
|
+
});
|
|
416
|
+
return { calls };
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Prepare a complete transaction for client-side signing (similar to prepareCall for bundlers)
|
|
420
|
+
* All Ethereum logic (encoding, gas estimation, nonce) is handled server-side
|
|
421
|
+
* Client only needs to sign and send with MetaMask
|
|
422
|
+
* @param tokenUri - IPFS token URI for the agent registration
|
|
423
|
+
* @param metadata - Metadata entries for the agent
|
|
424
|
+
* @param fromAddress - Address that will sign the transaction (only address needed, no client)
|
|
425
|
+
* @returns Prepared transaction object ready for client-side signing
|
|
426
|
+
*/
|
|
427
|
+
async prepareRegisterTransaction(tokenUri, metadata, fromAddress) {
|
|
428
|
+
// Encode the transaction data
|
|
429
|
+
const encodedData = await this.encodeRegisterWithMetadata(tokenUri, metadata);
|
|
430
|
+
// Get chain ID using AccountProvider
|
|
431
|
+
const chainId = await this.accountProvider.chainId();
|
|
432
|
+
// Initialize gas estimation variables
|
|
433
|
+
let gasEstimate;
|
|
434
|
+
let gasPrice;
|
|
435
|
+
let maxFeePerGas;
|
|
436
|
+
let maxPriorityFeePerGas;
|
|
437
|
+
let nonce;
|
|
438
|
+
try {
|
|
439
|
+
// Get current block data to check for EIP-1559 support
|
|
440
|
+
const blockData = await this.accountProvider.getBlock('latest');
|
|
441
|
+
// Prefer EIP-1559 (maxFeePerGas/maxPriorityFeePerGas) if available
|
|
442
|
+
// Otherwise fall back to legacy gasPrice
|
|
443
|
+
if (blockData && 'baseFeePerGas' in blockData && blockData.baseFeePerGas) {
|
|
444
|
+
// EIP-1559: Use maxFeePerGas and maxPriorityFeePerGas
|
|
445
|
+
// Set a reasonable priority fee (1-2 gwei typically)
|
|
446
|
+
// maxFeePerGas should be baseFeePerGas + maxPriorityFeePerGas + buffer
|
|
447
|
+
maxPriorityFeePerGas = 1000000000n; // 1 gwei as priority fee
|
|
448
|
+
maxFeePerGas = (blockData.baseFeePerGas * 2n) + maxPriorityFeePerGas; // 2x base + priority (buffer for safety)
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
// Legacy: Use gasPrice
|
|
452
|
+
gasPrice = await this.accountProvider.getGasPrice();
|
|
453
|
+
}
|
|
454
|
+
// Estimate gas using AccountProvider
|
|
455
|
+
gasEstimate = await this.accountProvider.estimateGas({
|
|
456
|
+
account: fromAddress,
|
|
457
|
+
to: this.identityRegistryAddress,
|
|
458
|
+
data: encodedData,
|
|
459
|
+
});
|
|
460
|
+
// Get nonce using AccountProvider
|
|
461
|
+
nonce = await this.accountProvider.getTransactionCount(fromAddress, 'pending');
|
|
462
|
+
}
|
|
463
|
+
catch (error) {
|
|
464
|
+
console.warn('Could not estimate gas or get transaction parameters:', error);
|
|
465
|
+
// Continue without gas estimates - client can estimate
|
|
466
|
+
}
|
|
467
|
+
// Build transaction object - return hex strings for all bigint values (Viem accepts hex strings directly)
|
|
468
|
+
// This format can be used directly with Viem's sendTransaction without client-side conversion
|
|
469
|
+
const txParams = {
|
|
470
|
+
to: this.identityRegistryAddress,
|
|
471
|
+
data: encodedData,
|
|
472
|
+
value: '0x0', // Hex string for value
|
|
473
|
+
gas: gasEstimate ? `0x${gasEstimate.toString(16)}` : undefined,
|
|
474
|
+
nonce,
|
|
475
|
+
chainId,
|
|
476
|
+
};
|
|
477
|
+
// Include EIP-1559 fields if available, otherwise legacy gasPrice
|
|
478
|
+
// All as hex strings for direct Viem compatibility
|
|
479
|
+
if (maxFeePerGas && maxPriorityFeePerGas) {
|
|
480
|
+
txParams.maxFeePerGas = `0x${maxFeePerGas.toString(16)}`;
|
|
481
|
+
txParams.maxPriorityFeePerGas = `0x${maxPriorityFeePerGas.toString(16)}`;
|
|
482
|
+
}
|
|
483
|
+
else if (gasPrice) {
|
|
484
|
+
txParams.gasPrice = `0x${gasPrice.toString(16)}`;
|
|
485
|
+
}
|
|
486
|
+
return txParams;
|
|
487
|
+
}
|
|
488
|
+
async isValidAgentAccount(agentAccount) {
|
|
489
|
+
try {
|
|
490
|
+
// Use AccountProvider's ReadClient interface - check if address has code
|
|
491
|
+
// We can use a simple call to check if it's a contract
|
|
492
|
+
// For now, we'll use publicClient if available, otherwise return null
|
|
493
|
+
if (this.publicClient) {
|
|
494
|
+
const code = await this.publicClient.getBytecode({ address: agentAccount });
|
|
495
|
+
return code ? true : false;
|
|
496
|
+
}
|
|
497
|
+
// AccountProvider doesn't expose getBytecode directly, so we check via isContractSigner
|
|
498
|
+
// This is a workaround - ideally AccountProvider would expose getBytecode
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
catch {
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Extract agentId from a user operation/transaction receipt
|
|
507
|
+
* Public in this SDK to support AA flows explicitly.
|
|
508
|
+
*/
|
|
509
|
+
extractAgentIdFromReceiptPublic(receipt) {
|
|
510
|
+
// Look for parsed events first
|
|
511
|
+
if (receipt?.events) {
|
|
512
|
+
const registeredEvent = receipt.events.find((e) => e.name === 'Registered');
|
|
513
|
+
if (registeredEvent?.args) {
|
|
514
|
+
const val = registeredEvent.args.agentId ?? registeredEvent.args[0];
|
|
515
|
+
if (val !== undefined)
|
|
516
|
+
return BigInt(val);
|
|
517
|
+
}
|
|
518
|
+
const transferEvent = receipt.events.find((e) => e.name === 'Transfer' && (e.args.from === '0x0000000000000000000000000000000000000000' || e.args.from === 0 || e.args.from === 0n));
|
|
519
|
+
if (transferEvent?.args) {
|
|
520
|
+
const val = transferEvent.args.tokenId ?? transferEvent.args[2];
|
|
521
|
+
if (val !== undefined)
|
|
522
|
+
return BigInt(val);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
// Fallback: raw logs array
|
|
526
|
+
if (receipt?.logs && Array.isArray(receipt.logs)) {
|
|
527
|
+
for (const log of receipt.logs) {
|
|
528
|
+
// Transfer(address,address,uint256)
|
|
529
|
+
if (log.topics && log.topics[0] === '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef') {
|
|
530
|
+
const from = log.topics[1];
|
|
531
|
+
if (from === '0x0000000000000000000000000000000000000000000000000000000000000000') {
|
|
532
|
+
const tokenId = BigInt(log.topics[3] || log.data);
|
|
533
|
+
return tokenId;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
throw new Error('Could not extract agentId from transaction receipt - Registered or Transfer event not found');
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Get the owner (EOA) of an account address
|
|
542
|
+
*
|
|
543
|
+
* @param accountAddress - The account address (smart account or contract)
|
|
544
|
+
* @returns The owner address (EOA) or null if not found or error
|
|
545
|
+
*/
|
|
546
|
+
async getAccountOwner(accountAddress) {
|
|
547
|
+
try {
|
|
548
|
+
const owner = await this.accountProvider.call({
|
|
549
|
+
to: accountAddress,
|
|
550
|
+
abi: [{ name: 'owner', type: 'function', stateMutability: 'view', inputs: [], outputs: [{ type: 'address' }] }],
|
|
551
|
+
functionName: 'owner',
|
|
552
|
+
args: [],
|
|
553
|
+
});
|
|
554
|
+
return owner;
|
|
555
|
+
}
|
|
556
|
+
catch {
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* @deprecated Use getAccountOwner instead
|
|
562
|
+
*/
|
|
563
|
+
async getAgentEoaByAgentAccount(agentAccount) {
|
|
564
|
+
return this.getAccountOwner(agentAccount);
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Get agentName from on-chain metadata (string value)
|
|
568
|
+
*/
|
|
569
|
+
async getAgentName(agentId) {
|
|
570
|
+
try {
|
|
571
|
+
const name = await this.getMetadata(agentId, 'agentName');
|
|
572
|
+
if (typeof name === 'string') {
|
|
573
|
+
const trimmed = name.trim();
|
|
574
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
575
|
+
}
|
|
576
|
+
return name ? String(name) : null;
|
|
577
|
+
}
|
|
578
|
+
catch (error) {
|
|
579
|
+
console.info("++++++++++++++++++++++++ getAgentName: error", error);
|
|
580
|
+
return null;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Get agentAccount address from on-chain metadata.
|
|
585
|
+
* Supports CAIP-10 format like "eip155:11155111:0x..." or raw 0x address.
|
|
586
|
+
*/
|
|
587
|
+
async getAgentAccount(agentId) {
|
|
588
|
+
try {
|
|
589
|
+
const value = await this.getMetadata(agentId, 'agentAccount');
|
|
590
|
+
if (!value)
|
|
591
|
+
return null;
|
|
592
|
+
if (typeof value === 'string') {
|
|
593
|
+
const v = value.trim();
|
|
594
|
+
if (v.startsWith('eip155:')) {
|
|
595
|
+
const parts = v.split(':');
|
|
596
|
+
const addr = parts[2];
|
|
597
|
+
if (addr && /^0x[a-fA-F0-9]{40}$/.test(addr))
|
|
598
|
+
return addr;
|
|
599
|
+
}
|
|
600
|
+
if (/^0x[a-fA-F0-9]{40}$/.test(v))
|
|
601
|
+
return v;
|
|
602
|
+
}
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
catch {
|
|
606
|
+
return null;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Get agentCategory from on-chain metadata (string value)
|
|
611
|
+
* Returns one of the standard agent category types from the OAS ecosystem.
|
|
612
|
+
*/
|
|
613
|
+
async getAgentCategory(agentId) {
|
|
614
|
+
try {
|
|
615
|
+
const category = await this.getMetadata(agentId, 'agentCategory');
|
|
616
|
+
if (typeof category === 'string') {
|
|
617
|
+
const trimmed = category.trim();
|
|
618
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
619
|
+
}
|
|
620
|
+
return category ? String(category) : null;
|
|
621
|
+
}
|
|
622
|
+
catch (error) {
|
|
623
|
+
console.info("++++++++++++++++++++++++ getAgentCategory: error", error);
|
|
624
|
+
return null;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Keep compatibility: delegate to receipt extractor.
|
|
629
|
+
*/
|
|
630
|
+
extractAgentIdFromLogs(receipt) {
|
|
631
|
+
return this.extractAgentIdFromReceiptPublic(receipt);
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Get the approved operator address for an agent NFT token
|
|
635
|
+
* Returns the address approved to operate on the token, or null if no operator is set
|
|
636
|
+
*
|
|
637
|
+
* @param agentId - The agent ID (token ID)
|
|
638
|
+
* @returns The approved operator address, or null if no operator is set (zero address)
|
|
639
|
+
*/
|
|
640
|
+
async getNFTOperator(agentId) {
|
|
641
|
+
try {
|
|
642
|
+
const operatorAddress = await this.accountProvider.call({
|
|
643
|
+
to: this.identityRegistryAddress,
|
|
644
|
+
abi: IdentityRegistryABI,
|
|
645
|
+
functionName: 'getApproved',
|
|
646
|
+
args: [agentId],
|
|
647
|
+
});
|
|
648
|
+
// Check if operator is set (not zero address)
|
|
649
|
+
if (operatorAddress && operatorAddress !== '0x0000000000000000000000000000000000000000') {
|
|
650
|
+
return operatorAddress;
|
|
651
|
+
}
|
|
652
|
+
return null;
|
|
653
|
+
}
|
|
654
|
+
catch (error) {
|
|
655
|
+
console.error('Failed to get NFT operator:', error);
|
|
656
|
+
return null;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
//# sourceMappingURL=AIAgentIdentityClient.js.map
|