@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,735 @@
|
|
|
1
|
+
import { createPublicClient, http, encodeFunctionData, zeroAddress, namehash } from 'viem';
|
|
2
|
+
import { AIAgentENSClient } from './AIAgentENSClient';
|
|
3
|
+
export class AIAgentL2ENSDurenClient extends AIAgentENSClient {
|
|
4
|
+
constructor(chain, rpcUrl, adapter, ensRegistryAddress, ensResolverAddress, identityRegistryAddress) {
|
|
5
|
+
super(chain, rpcUrl, adapter, ensRegistryAddress, ensResolverAddress, identityRegistryAddress);
|
|
6
|
+
}
|
|
7
|
+
/** Chain ID for Linea Sepolia (registry uses createSubnode(baseNode, label, owner, [])). */
|
|
8
|
+
static CHAIN_ID_LINEA_SEPOLIA = 59141;
|
|
9
|
+
/** Chain ID for Linea Mainnet (Durin L2Registry acts as registry + resolver). */
|
|
10
|
+
static CHAIN_ID_LINEA_MAINNET = 59144;
|
|
11
|
+
/**
|
|
12
|
+
* L2Registrar address per chain. Base Sepolia uses a separate L2Registrar contract;
|
|
13
|
+
* Linea Sepolia uses the registry's createSubnode(bytes32,string,address,bytes[]) (no separate registrar).
|
|
14
|
+
*/
|
|
15
|
+
getL2RegistrarAddress() {
|
|
16
|
+
const chainId = this.chain?.id;
|
|
17
|
+
if (chainId === 84532)
|
|
18
|
+
return '0x68CAd072571E8bea1DA9e5C071367Aa6ddC8F37F';
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
/** True when this chain uses registry.createSubnode for subdomain registration (e.g. Linea Sepolia). */
|
|
22
|
+
usesRegistryCreateSubnode() {
|
|
23
|
+
const id = this.chain?.id;
|
|
24
|
+
return (id === AIAgentL2ENSDurenClient.CHAIN_ID_LINEA_SEPOLIA ||
|
|
25
|
+
id === AIAgentL2ENSDurenClient.CHAIN_ID_LINEA_MAINNET);
|
|
26
|
+
}
|
|
27
|
+
getEffectiveRpcUrl() {
|
|
28
|
+
const rpc = this.rpcUrl;
|
|
29
|
+
if (typeof rpc === 'string' && rpc.trim())
|
|
30
|
+
return rpc.trim();
|
|
31
|
+
const chain = this.chain;
|
|
32
|
+
const first = chain?.rpcUrls?.default?.http?.[0];
|
|
33
|
+
return typeof first === 'string' ? first : undefined;
|
|
34
|
+
}
|
|
35
|
+
hasValidResolver() {
|
|
36
|
+
const addr = this.getEnsResolverAddress();
|
|
37
|
+
return typeof addr === 'string' && addr.length > 0 && addr !== zeroAddress;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Guard for preparing metadata calls (setAddr/setText/etc).
|
|
41
|
+
*
|
|
42
|
+
* Historically Linea Sepolia used a registry that was not a resolver, so we rejected resolver===registry.
|
|
43
|
+
* If the deployed contract implements both registry+resolver interfaces at the same address, allow it.
|
|
44
|
+
*/
|
|
45
|
+
hasValidResolverForSetInfo() {
|
|
46
|
+
if (!this.hasValidResolver())
|
|
47
|
+
return false;
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Override to ensure L2 client always returns true for isL2()
|
|
52
|
+
*/
|
|
53
|
+
isL2() {
|
|
54
|
+
return true; // This is always an L2 client
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Override to ensure L2 client always returns false for isL1()
|
|
58
|
+
*/
|
|
59
|
+
isL1() {
|
|
60
|
+
return false; // This is never an L1 client
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Override to ensure L2 client always returns 'L2'
|
|
64
|
+
*/
|
|
65
|
+
getChainType() {
|
|
66
|
+
return 'L2';
|
|
67
|
+
}
|
|
68
|
+
async getAgentUrlByName(name) {
|
|
69
|
+
try {
|
|
70
|
+
if (!this.hasValidResolver())
|
|
71
|
+
return null;
|
|
72
|
+
const rpcUrl = this.getEffectiveRpcUrl();
|
|
73
|
+
if (!rpcUrl)
|
|
74
|
+
return null;
|
|
75
|
+
const node = namehash(name);
|
|
76
|
+
const resolverAddress = this.getEnsResolverAddress();
|
|
77
|
+
const resolverAbi = [
|
|
78
|
+
{
|
|
79
|
+
"inputs": [
|
|
80
|
+
{
|
|
81
|
+
"internalType": "bytes32",
|
|
82
|
+
"name": "node",
|
|
83
|
+
"type": "bytes32"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"internalType": "string",
|
|
87
|
+
"name": "key",
|
|
88
|
+
"type": "string"
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
"name": "text",
|
|
92
|
+
"outputs": [
|
|
93
|
+
{
|
|
94
|
+
"internalType": "string",
|
|
95
|
+
"name": "",
|
|
96
|
+
"type": "string"
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
"stateMutability": "view",
|
|
100
|
+
"type": "function"
|
|
101
|
+
}
|
|
102
|
+
];
|
|
103
|
+
const publicClient = createPublicClient({
|
|
104
|
+
chain: this.chain,
|
|
105
|
+
transport: http(rpcUrl),
|
|
106
|
+
});
|
|
107
|
+
const url = await publicClient.readContract({
|
|
108
|
+
address: resolverAddress,
|
|
109
|
+
abi: resolverAbi,
|
|
110
|
+
functionName: 'text',
|
|
111
|
+
args: [node, 'url'],
|
|
112
|
+
});
|
|
113
|
+
// Return null if URL is empty
|
|
114
|
+
if (!url || url.trim() === '') {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
return url;
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
console.error('Error resolving URL for name:', name, error);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async getAgentAccountByName(name) {
|
|
125
|
+
try {
|
|
126
|
+
const node = namehash(name);
|
|
127
|
+
const rpcUrl = this.getEffectiveRpcUrl();
|
|
128
|
+
if (!rpcUrl)
|
|
129
|
+
return null;
|
|
130
|
+
const publicClient = createPublicClient({
|
|
131
|
+
chain: this.chain,
|
|
132
|
+
transport: http(rpcUrl),
|
|
133
|
+
});
|
|
134
|
+
const ensRegistryAddress = this.getEnsRegistryAddress();
|
|
135
|
+
const ensRegistryAbi = [
|
|
136
|
+
{ inputs: [{ internalType: 'bytes32', name: 'node', type: 'bytes32' }], name: 'owner', outputs: [{ internalType: 'address', name: '', type: 'address' }], stateMutability: 'view', type: 'function' },
|
|
137
|
+
];
|
|
138
|
+
const owner = await publicClient.readContract({
|
|
139
|
+
address: ensRegistryAddress,
|
|
140
|
+
abi: ensRegistryAbi,
|
|
141
|
+
functionName: 'owner',
|
|
142
|
+
args: [node],
|
|
143
|
+
});
|
|
144
|
+
if (!owner || owner === zeroAddress)
|
|
145
|
+
return null;
|
|
146
|
+
if (!this.hasValidResolver())
|
|
147
|
+
return null;
|
|
148
|
+
const resolverAddress = this.getEnsResolverAddress();
|
|
149
|
+
// ENS Resolver ABI for addr function
|
|
150
|
+
const resolverAbi = [
|
|
151
|
+
{
|
|
152
|
+
"inputs": [
|
|
153
|
+
{
|
|
154
|
+
"internalType": "bytes32",
|
|
155
|
+
"name": "node",
|
|
156
|
+
"type": "bytes32"
|
|
157
|
+
}
|
|
158
|
+
],
|
|
159
|
+
"name": "addr",
|
|
160
|
+
"outputs": [
|
|
161
|
+
{
|
|
162
|
+
"internalType": "address",
|
|
163
|
+
"name": "",
|
|
164
|
+
"type": "address"
|
|
165
|
+
}
|
|
166
|
+
],
|
|
167
|
+
"stateMutability": "view",
|
|
168
|
+
"type": "function"
|
|
169
|
+
}
|
|
170
|
+
];
|
|
171
|
+
/*
|
|
172
|
+
// TEST: Check resolver status
|
|
173
|
+
console.info("********************* TEST: Checking resolver for name:", name);
|
|
174
|
+
|
|
175
|
+
// Check if resolver is set for this node
|
|
176
|
+
const registryResolverAbi = [
|
|
177
|
+
{
|
|
178
|
+
"inputs": [
|
|
179
|
+
{
|
|
180
|
+
"internalType": "bytes32",
|
|
181
|
+
"name": "node",
|
|
182
|
+
"type": "bytes32"
|
|
183
|
+
}
|
|
184
|
+
],
|
|
185
|
+
"name": "resolver",
|
|
186
|
+
"outputs": [
|
|
187
|
+
{
|
|
188
|
+
"internalType": "address",
|
|
189
|
+
"name": "",
|
|
190
|
+
"type": "address"
|
|
191
|
+
}
|
|
192
|
+
],
|
|
193
|
+
"stateMutability": "view",
|
|
194
|
+
"type": "function"
|
|
195
|
+
}
|
|
196
|
+
] as const;
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
const resolver = await // @ts-ignore - viem version compatibility issue
|
|
200
|
+
publicClient.readContract({
|
|
201
|
+
address: ensRegistryAddress,
|
|
202
|
+
abi: registryResolverAbi,
|
|
203
|
+
functionName: 'resolver',
|
|
204
|
+
args: [node]
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
console.info("********************* TEST: Resolver for", name, ":", resolver);
|
|
208
|
+
|
|
209
|
+
if (resolver && resolver !== '0x0000000000000000000000000000000000000000') {
|
|
210
|
+
console.info("********************* TEST: RESOLVER EXISTS - Name has resolver:", resolver);
|
|
211
|
+
} else {
|
|
212
|
+
console.info("********************* TEST: NO RESOLVER - Name has no resolver set");
|
|
213
|
+
}
|
|
214
|
+
} catch (resolverError) {
|
|
215
|
+
console.error("********************* TEST: Error checking resolver:", resolverError);
|
|
216
|
+
}
|
|
217
|
+
*/
|
|
218
|
+
// Call the resolver directly to get address
|
|
219
|
+
const addressResolverAbi = [
|
|
220
|
+
{
|
|
221
|
+
"inputs": [
|
|
222
|
+
{
|
|
223
|
+
"internalType": "bytes32",
|
|
224
|
+
"name": "node",
|
|
225
|
+
"type": "bytes32"
|
|
226
|
+
}
|
|
227
|
+
],
|
|
228
|
+
"name": "addr",
|
|
229
|
+
"outputs": [
|
|
230
|
+
{
|
|
231
|
+
"internalType": "address",
|
|
232
|
+
"name": "",
|
|
233
|
+
"type": "address"
|
|
234
|
+
}
|
|
235
|
+
],
|
|
236
|
+
"stateMutability": "view",
|
|
237
|
+
"type": "function"
|
|
238
|
+
}
|
|
239
|
+
];
|
|
240
|
+
const address = await publicClient.readContract({
|
|
241
|
+
address: resolverAddress,
|
|
242
|
+
abi: addressResolverAbi,
|
|
243
|
+
functionName: 'addr',
|
|
244
|
+
args: [node],
|
|
245
|
+
});
|
|
246
|
+
// Return null if address is zero address
|
|
247
|
+
if (!address || address === '0x0000000000000000000000000000000000000000') {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
return address;
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
console.error('Error resolving address for name:', name, error);
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Note: getAgentEoaByAgentAccount is not a method of AIAgentENSClient
|
|
259
|
+
* This method is actually in AIAgentIdentityClient, so we don't need to override it here.
|
|
260
|
+
* The ownership detection logic is handled in the UI layer (AddAgentModal.tsx)
|
|
261
|
+
*/
|
|
262
|
+
/**
|
|
263
|
+
* Override hasAgentNameOwner to use L2Registrar available() function
|
|
264
|
+
*/
|
|
265
|
+
async hasAgentNameOwner(orgName, agentName) {
|
|
266
|
+
console.info("AIAgentL2ENSDurenClient.hasAgentNameOwner");
|
|
267
|
+
const clean = (s) => (s || '').trim().toLowerCase();
|
|
268
|
+
const parent = clean(orgName);
|
|
269
|
+
const label = clean(agentName).replace(/\s+/g, '-');
|
|
270
|
+
console.info("AIAgentL2ENSDurenClient.hasAgentNameOwner: label", label);
|
|
271
|
+
console.info("AIAgentL2ENSDurenClient.hasAgentNameOwner: parent", parent);
|
|
272
|
+
const fullSubname = `${label}.${parent}`;
|
|
273
|
+
console.info("AIAgentL2ENSDurenClient.hasAgentNameOwner: fullSubname", fullSubname);
|
|
274
|
+
const l2RegistrarAddress = this.getL2RegistrarAddress();
|
|
275
|
+
if (!l2RegistrarAddress) {
|
|
276
|
+
// No L2Registrar on this chain (e.g. Linea Sepolia); fall back to ENS registry owner(node)
|
|
277
|
+
try {
|
|
278
|
+
const rpcUrl = this.getEffectiveRpcUrl();
|
|
279
|
+
if (!rpcUrl)
|
|
280
|
+
return false;
|
|
281
|
+
const node = namehash(fullSubname);
|
|
282
|
+
const publicClient = createPublicClient({
|
|
283
|
+
chain: this.chain,
|
|
284
|
+
transport: http(rpcUrl),
|
|
285
|
+
});
|
|
286
|
+
const owner = await publicClient.readContract({
|
|
287
|
+
address: this.getEnsRegistryAddress(),
|
|
288
|
+
abi: [{ inputs: [{ name: 'node', type: 'bytes32' }], name: 'owner', outputs: [{ type: 'address' }], stateMutability: 'view', type: 'function' }],
|
|
289
|
+
functionName: 'owner',
|
|
290
|
+
args: [node],
|
|
291
|
+
});
|
|
292
|
+
const hasOwner = owner && owner !== zeroAddress;
|
|
293
|
+
console.info(`AIAgentL2ENSDurenClient.hasAgentNameOwner: "${fullSubname}" (registry) ${hasOwner ? 'HAS owner' : 'has NO owner'}`);
|
|
294
|
+
return hasOwner;
|
|
295
|
+
}
|
|
296
|
+
catch (err) {
|
|
297
|
+
console.error('Error checking agent name owner via registry:', err);
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const l2RegistrarAbi = [
|
|
302
|
+
{
|
|
303
|
+
"inputs": [
|
|
304
|
+
{
|
|
305
|
+
"internalType": "string",
|
|
306
|
+
"name": "label",
|
|
307
|
+
"type": "string"
|
|
308
|
+
}
|
|
309
|
+
],
|
|
310
|
+
"name": "available",
|
|
311
|
+
"outputs": [
|
|
312
|
+
{
|
|
313
|
+
"internalType": "bool",
|
|
314
|
+
"name": "available",
|
|
315
|
+
"type": "bool"
|
|
316
|
+
}
|
|
317
|
+
],
|
|
318
|
+
"stateMutability": "view",
|
|
319
|
+
"type": "function"
|
|
320
|
+
}
|
|
321
|
+
];
|
|
322
|
+
const publicClient = // @ts-ignore - viem version compatibility issue
|
|
323
|
+
createPublicClient({
|
|
324
|
+
chain: this.chain,
|
|
325
|
+
transport: http(this.rpcUrl)
|
|
326
|
+
});
|
|
327
|
+
try {
|
|
328
|
+
// Call registrar.available(label) - returns true if available, false if taken
|
|
329
|
+
const isAvailable = await // @ts-ignore - viem version compatibility issue
|
|
330
|
+
publicClient.readContract({
|
|
331
|
+
address: l2RegistrarAddress,
|
|
332
|
+
abi: l2RegistrarAbi,
|
|
333
|
+
functionName: 'available',
|
|
334
|
+
args: [label]
|
|
335
|
+
});
|
|
336
|
+
// If not available, then it has an owner
|
|
337
|
+
const hasOwner = !isAvailable;
|
|
338
|
+
console.info(`AIAgentL2ENSDurenClient.hasAgentNameOwner: "${fullSubname}" (label: "${label}") ${hasOwner ? 'HAS owner' : 'has NO owner'} (available: ${isAvailable})`);
|
|
339
|
+
return hasOwner;
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
console.error('Error checking agent name owner via registrar:', error);
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
async prepareAddAgentNameToOrgCalls(params) {
|
|
347
|
+
console.log("AIAgentL2ENSDurenClient.prepareAddAgentNameToOrgCalls");
|
|
348
|
+
console.log("orgName: ", params.orgName);
|
|
349
|
+
console.log("agentName: ", params.agentName);
|
|
350
|
+
console.log("agentAddress: ", params.agentAddress);
|
|
351
|
+
console.log("agentUrl: ", params.agentUrl);
|
|
352
|
+
const clean = (s) => (s || '').trim().toLowerCase();
|
|
353
|
+
const parent = clean(params.orgName) + ".eth";
|
|
354
|
+
const label = clean(params.agentName).replace(/\s+/g, '-');
|
|
355
|
+
const fullSubname = `${label}.${parent}`;
|
|
356
|
+
const agentAddress = params.agentAddress;
|
|
357
|
+
const agentUrl = params.agentUrl;
|
|
358
|
+
const chainName = this.chain.name.toLowerCase().replace(/\s+/g, '-');
|
|
359
|
+
console.info("parent: ", parent);
|
|
360
|
+
console.info("label: ", label);
|
|
361
|
+
console.info("fullSubname: ", fullSubname);
|
|
362
|
+
console.info("agentAddress: ", agentAddress);
|
|
363
|
+
console.info("chainName: ", chainName);
|
|
364
|
+
console.info("agentUrl: ", agentUrl);
|
|
365
|
+
const calls = [];
|
|
366
|
+
try {
|
|
367
|
+
// Step 1: Register the subdomain with L2Registrar
|
|
368
|
+
const registrationCall = await this.registerSubdomain(label, agentAddress);
|
|
369
|
+
// Add registration call
|
|
370
|
+
calls.push(...registrationCall.calls);
|
|
371
|
+
/*
|
|
372
|
+
|
|
373
|
+
// Step 2: Set address records for the subdomain
|
|
374
|
+
const chainId = (this as any).chain.id;
|
|
375
|
+
|
|
376
|
+
// Set Base Sepolia address record (coinType 2147568164)
|
|
377
|
+
if (chainId === 84532) { // Base Sepolia
|
|
378
|
+
const baseAddrCall = await this.setResolverAddrRecordDirect(
|
|
379
|
+
fullSubname,
|
|
380
|
+
2147568164, // Base Sepolia coin type
|
|
381
|
+
agentAddress
|
|
382
|
+
);
|
|
383
|
+
calls.push(baseAddrCall);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
// Set Ethereum address record (coinType 60) for cross-chain compatibility
|
|
388
|
+
const ethAddrCall = await this.setResolverAddrRecordDirect(
|
|
389
|
+
fullSubname,
|
|
390
|
+
60, // Ethereum coin type
|
|
391
|
+
agentAddress
|
|
392
|
+
);
|
|
393
|
+
calls.push(ethAddrCall);
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
// Step 3: Set resolver records for the subdomain
|
|
397
|
+
const textRecords = [
|
|
398
|
+
{ key: 'name', value: label },
|
|
399
|
+
{ key: 'url', value: agentUrl },
|
|
400
|
+
{ key: 'description', value: `Agent: ${label}` },
|
|
401
|
+
{ key: 'chain', value: chainName },
|
|
402
|
+
{ key: 'agent-account', value: agentAddress },
|
|
403
|
+
];
|
|
404
|
+
|
|
405
|
+
for (const record of textRecords) {
|
|
406
|
+
const recordCall = await this.setResolverTextRecordDirect(
|
|
407
|
+
fullSubname,
|
|
408
|
+
record.key,
|
|
409
|
+
record.value,
|
|
410
|
+
);
|
|
411
|
+
calls.push(recordCall);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
console.info("Generated calls count: ", calls.length);
|
|
418
|
+
console.info("Calls: ", calls);
|
|
419
|
+
|
|
420
|
+
*/
|
|
421
|
+
}
|
|
422
|
+
catch (error) {
|
|
423
|
+
console.error("Error preparing agent name calls:", error);
|
|
424
|
+
throw error;
|
|
425
|
+
}
|
|
426
|
+
return { calls };
|
|
427
|
+
}
|
|
428
|
+
async prepareAddAgentInfoCalls(params) {
|
|
429
|
+
console.log("AIAgentL2ENSDurenClient.prepareAddAgentNameToOrgCalls");
|
|
430
|
+
console.log("orgName: ", params.orgName);
|
|
431
|
+
console.log("agentName: ", params.agentName);
|
|
432
|
+
console.log("agentAddress: ", params.agentAddress);
|
|
433
|
+
console.log("agentUrl: ", params.agentUrl);
|
|
434
|
+
const clean = (s) => (s || '').trim().toLowerCase();
|
|
435
|
+
const parent = clean(params.orgName) + ".eth";
|
|
436
|
+
const label = clean(params.agentName).replace(/\s+/g, '-');
|
|
437
|
+
const fullSubname = `${label}.${parent}`;
|
|
438
|
+
const agentAddress = params.agentAddress;
|
|
439
|
+
const agentUrl = params.agentUrl;
|
|
440
|
+
const chainName = this.chain.name.toLowerCase().replace(/\s+/g, '-');
|
|
441
|
+
console.info("parent: ", parent);
|
|
442
|
+
console.info("label: ", label);
|
|
443
|
+
console.info("fullSubname: ", fullSubname);
|
|
444
|
+
console.info("agentAddress: ", agentAddress);
|
|
445
|
+
console.info("chainName: ", chainName);
|
|
446
|
+
console.info("agentUrl: ", agentUrl);
|
|
447
|
+
const calls = [];
|
|
448
|
+
try {
|
|
449
|
+
// Step 2: Set address records for the subdomain
|
|
450
|
+
const chainId = this.chain.id;
|
|
451
|
+
// Set Base Sepolia address record (coinType 2147568164)
|
|
452
|
+
if (chainId === 84532) { // Base Sepolia
|
|
453
|
+
const baseAddrCall = await this.setResolverAddrRecordDirect(fullSubname, 2147568164, // Base Sepolia coin type
|
|
454
|
+
agentAddress);
|
|
455
|
+
calls.push(baseAddrCall);
|
|
456
|
+
}
|
|
457
|
+
/*
|
|
458
|
+
// Set Ethereum address record (coinType 60) for cross-chain compatibility
|
|
459
|
+
const ethAddrCall = await this.setResolverAddrRecordDirect(
|
|
460
|
+
fullSubname,
|
|
461
|
+
60, // Ethereum coin type
|
|
462
|
+
agentAddress
|
|
463
|
+
);
|
|
464
|
+
calls.push(ethAddrCall);
|
|
465
|
+
*/
|
|
466
|
+
// Step 3: Set resolver records for the subdomain
|
|
467
|
+
const textRecords = [
|
|
468
|
+
//{ key: 'name', value: label },
|
|
469
|
+
{ key: 'url', value: agentUrl },
|
|
470
|
+
//{ key: 'description', value: `Agent: ${label}` },
|
|
471
|
+
//{ key: 'chain', value: chainName },
|
|
472
|
+
//{ key: 'agent-account', value: agentAddress },
|
|
473
|
+
];
|
|
474
|
+
// Add description if provided
|
|
475
|
+
if (params.agentDescription && params.agentDescription.trim() !== '') {
|
|
476
|
+
textRecords.push({ key: 'description', value: params.agentDescription.trim() });
|
|
477
|
+
}
|
|
478
|
+
for (const record of textRecords) {
|
|
479
|
+
const recordCall = await this.setResolverTextRecordDirect(fullSubname, record.key, record.value);
|
|
480
|
+
calls.push(recordCall);
|
|
481
|
+
}
|
|
482
|
+
console.info("Generated calls count: ", calls.length);
|
|
483
|
+
console.info("Calls: ", calls);
|
|
484
|
+
}
|
|
485
|
+
catch (error) {
|
|
486
|
+
console.error("Error preparing agent name calls:", error);
|
|
487
|
+
throw error;
|
|
488
|
+
}
|
|
489
|
+
return { calls };
|
|
490
|
+
}
|
|
491
|
+
async prepareSetNameUriCalls(name, uri) {
|
|
492
|
+
const calls = [];
|
|
493
|
+
return { calls };
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Register subdomain using L2Registrar contract (Base Sepolia specific)
|
|
497
|
+
*/
|
|
498
|
+
async registerSubdomain(subdomain, owner) {
|
|
499
|
+
console.log("AIAgentL2ENSDurenClient.registerSubdomain", { subdomain, owner });
|
|
500
|
+
const calls = [];
|
|
501
|
+
// Linea Sepolia: registry has baseNode() and createSubnode(bytes32,string,address,bytes[])
|
|
502
|
+
if (this.usesRegistryCreateSubnode()) {
|
|
503
|
+
const rpcUrl = this.getEffectiveRpcUrl();
|
|
504
|
+
if (!rpcUrl)
|
|
505
|
+
return { calls };
|
|
506
|
+
const registryAddress = this.getEnsRegistryAddress();
|
|
507
|
+
const publicClient = createPublicClient({
|
|
508
|
+
chain: this.chain,
|
|
509
|
+
transport: http(rpcUrl),
|
|
510
|
+
});
|
|
511
|
+
const baseNodeAbi = [{ inputs: [], name: 'baseNode', outputs: [{ type: 'bytes32' }], stateMutability: 'view', type: 'function' }];
|
|
512
|
+
const baseNode = await publicClient.readContract({
|
|
513
|
+
address: registryAddress,
|
|
514
|
+
abi: baseNodeAbi,
|
|
515
|
+
functionName: 'baseNode',
|
|
516
|
+
});
|
|
517
|
+
const createSubnodeAbi = [
|
|
518
|
+
{
|
|
519
|
+
inputs: [
|
|
520
|
+
{ internalType: 'bytes32', name: 'baseNode', type: 'bytes32' },
|
|
521
|
+
{ internalType: 'string', name: 'label', type: 'string' },
|
|
522
|
+
{ internalType: 'address', name: 'owner', type: 'address' },
|
|
523
|
+
{ internalType: 'bytes[]', name: 'data', type: 'bytes[]' },
|
|
524
|
+
],
|
|
525
|
+
name: 'createSubnode',
|
|
526
|
+
outputs: [],
|
|
527
|
+
stateMutability: 'nonpayable',
|
|
528
|
+
type: 'function',
|
|
529
|
+
},
|
|
530
|
+
];
|
|
531
|
+
const data = encodeFunctionData({
|
|
532
|
+
abi: createSubnodeAbi,
|
|
533
|
+
functionName: 'createSubnode',
|
|
534
|
+
args: [baseNode, subdomain, owner, []],
|
|
535
|
+
});
|
|
536
|
+
calls.push({ to: registryAddress, data, value: 0n });
|
|
537
|
+
return { calls };
|
|
538
|
+
}
|
|
539
|
+
const l2RegistrarAddress = this.getL2RegistrarAddress();
|
|
540
|
+
if (!l2RegistrarAddress) {
|
|
541
|
+
return { calls };
|
|
542
|
+
}
|
|
543
|
+
// Base Sepolia: separate L2Registrar with register(string, address)
|
|
544
|
+
const l2RegistrarAbi = [
|
|
545
|
+
{
|
|
546
|
+
inputs: [
|
|
547
|
+
{ internalType: 'string', name: 'name', type: 'string' },
|
|
548
|
+
{ internalType: 'address', name: 'owner', type: 'address' },
|
|
549
|
+
],
|
|
550
|
+
name: 'register',
|
|
551
|
+
outputs: [],
|
|
552
|
+
stateMutability: 'nonpayable',
|
|
553
|
+
type: 'function',
|
|
554
|
+
},
|
|
555
|
+
];
|
|
556
|
+
const registerData = encodeFunctionData({
|
|
557
|
+
abi: l2RegistrarAbi,
|
|
558
|
+
functionName: 'register',
|
|
559
|
+
args: [subdomain, owner],
|
|
560
|
+
});
|
|
561
|
+
calls.push({ to: l2RegistrarAddress, data: registerData, value: 0n });
|
|
562
|
+
return { calls };
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Direct chain call for setting resolver records
|
|
566
|
+
*/
|
|
567
|
+
async setResolverTextRecordDirect(name, key, value) {
|
|
568
|
+
console.log("AIAgentL2ENSDurenClient.setResolverTextRecordDirect");
|
|
569
|
+
console.log("name:", name);
|
|
570
|
+
console.log("key:", key);
|
|
571
|
+
console.log("value:", value);
|
|
572
|
+
const resolverAddress = this.getEnsResolverAddress();
|
|
573
|
+
// ENS Resolver ABI for setText
|
|
574
|
+
const resolverAbi = [
|
|
575
|
+
{
|
|
576
|
+
"inputs": [
|
|
577
|
+
{
|
|
578
|
+
"internalType": "bytes32",
|
|
579
|
+
"name": "node",
|
|
580
|
+
"type": "bytes32"
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
"internalType": "string",
|
|
584
|
+
"name": "key",
|
|
585
|
+
"type": "string"
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
"internalType": "string",
|
|
589
|
+
"name": "value",
|
|
590
|
+
"type": "string"
|
|
591
|
+
}
|
|
592
|
+
],
|
|
593
|
+
"name": "setText",
|
|
594
|
+
"outputs": [],
|
|
595
|
+
"stateMutability": "nonpayable",
|
|
596
|
+
"type": "function"
|
|
597
|
+
}
|
|
598
|
+
];
|
|
599
|
+
// Calculate namehash for the subdomain
|
|
600
|
+
const node = namehash(name);
|
|
601
|
+
// Encode the setText function call
|
|
602
|
+
const data = encodeFunctionData({
|
|
603
|
+
abi: resolverAbi,
|
|
604
|
+
functionName: 'setText',
|
|
605
|
+
args: [node, key, value]
|
|
606
|
+
});
|
|
607
|
+
return {
|
|
608
|
+
to: resolverAddress,
|
|
609
|
+
data
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Override for Linea Sepolia (59141): registry does not implement resolver(node).
|
|
614
|
+
* When no resolver is configured return empty; otherwise use configured resolver address and skip registry lookups.
|
|
615
|
+
*/
|
|
616
|
+
async prepareSetAgentNameInfoCalls(params) {
|
|
617
|
+
if (this.usesRegistryCreateSubnode()) {
|
|
618
|
+
const chainId = this.chain?.id;
|
|
619
|
+
// Linea Mainnet Durin L2Registry: registry is also the resolver.
|
|
620
|
+
// Linea Sepolia: requires a separately configured resolver; otherwise skip.
|
|
621
|
+
if (chainId === AIAgentL2ENSDurenClient.CHAIN_ID_LINEA_SEPOLIA && !this.hasValidResolverForSetInfo()) {
|
|
622
|
+
return { calls: [] };
|
|
623
|
+
}
|
|
624
|
+
const resolver = chainId === AIAgentL2ENSDurenClient.CHAIN_ID_LINEA_MAINNET
|
|
625
|
+
? this.getEnsRegistryAddress()
|
|
626
|
+
: this.getEnsResolverAddress();
|
|
627
|
+
const clean = (s) => (s || '').trim().toLowerCase();
|
|
628
|
+
const parent = clean(params.orgName);
|
|
629
|
+
const label = clean(params.agentName).replace(/\s+/g, '-');
|
|
630
|
+
const ensFullName = `${label}.${parent}.eth`;
|
|
631
|
+
const childNode = namehash(ensFullName);
|
|
632
|
+
const calls = [];
|
|
633
|
+
const setAddrData = encodeFunctionData({
|
|
634
|
+
abi: [{ name: 'setAddr', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: 'node', type: 'bytes32' }, { name: 'a', type: 'address' }], outputs: [] }],
|
|
635
|
+
functionName: 'setAddr',
|
|
636
|
+
args: [childNode, params.agentAddress],
|
|
637
|
+
});
|
|
638
|
+
calls.push({ to: resolver, data: setAddrData });
|
|
639
|
+
if (params.agentUrl?.trim()) {
|
|
640
|
+
calls.push({
|
|
641
|
+
to: resolver,
|
|
642
|
+
data: encodeFunctionData({
|
|
643
|
+
abi: [{ name: 'setText', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: 'node', type: 'bytes32' }, { name: 'key', type: 'string' }, { name: 'value', type: 'string' }], outputs: [] }],
|
|
644
|
+
functionName: 'setText',
|
|
645
|
+
args: [childNode, 'url', params.agentUrl.trim()],
|
|
646
|
+
}),
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
if (params.agentDescription?.trim()) {
|
|
650
|
+
calls.push({
|
|
651
|
+
to: resolver,
|
|
652
|
+
data: encodeFunctionData({
|
|
653
|
+
abi: [{ name: 'setText', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: 'node', type: 'bytes32' }, { name: 'key', type: 'string' }, { name: 'value', type: 'string' }], outputs: [] }],
|
|
654
|
+
functionName: 'setText',
|
|
655
|
+
args: [childNode, 'description', params.agentDescription.trim()],
|
|
656
|
+
}),
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
return { calls };
|
|
660
|
+
}
|
|
661
|
+
return super.prepareSetAgentNameInfoCalls(params);
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Direct chain call for setting resolver address records
|
|
665
|
+
* Equivalent to: cast send resolver "setAddr(bytes32,uint256,bytes)" $NODE coinType encodedAddress
|
|
666
|
+
*/
|
|
667
|
+
async setResolverAddrRecordDirect(name, coinType, address) {
|
|
668
|
+
console.log("AIAgentL2ENSDurenClient.setResolverAddrRecordDirect");
|
|
669
|
+
console.log("name:", name);
|
|
670
|
+
console.log("coinType:", coinType);
|
|
671
|
+
console.log("address:", address);
|
|
672
|
+
const resolverAddress = this.getEnsResolverAddress();
|
|
673
|
+
// ENS Resolver ABI for setAddr
|
|
674
|
+
const resolverAbi = [
|
|
675
|
+
{
|
|
676
|
+
"inputs": [
|
|
677
|
+
{
|
|
678
|
+
"internalType": "bytes32",
|
|
679
|
+
"name": "node",
|
|
680
|
+
"type": "bytes32"
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
"internalType": "uint256",
|
|
684
|
+
"name": "coinType",
|
|
685
|
+
"type": "uint256"
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
"internalType": "bytes",
|
|
689
|
+
"name": "a",
|
|
690
|
+
"type": "bytes"
|
|
691
|
+
}
|
|
692
|
+
],
|
|
693
|
+
"name": "setAddr",
|
|
694
|
+
"outputs": [],
|
|
695
|
+
"stateMutability": "nonpayable",
|
|
696
|
+
"type": "function"
|
|
697
|
+
}
|
|
698
|
+
];
|
|
699
|
+
// Calculate namehash for the subdomain
|
|
700
|
+
const node = namehash(name);
|
|
701
|
+
// Encode the address according to the coin type
|
|
702
|
+
// For Base Sepolia (coinType 2147568164), we need to encode as bytes
|
|
703
|
+
let encodedAddress;
|
|
704
|
+
if (coinType === 2147568164) {
|
|
705
|
+
// Base Sepolia coin type - encode as bytes
|
|
706
|
+
// Equivalent to: $(cast abi-encode "f(address)" 0xYourBaseAddress)
|
|
707
|
+
encodedAddress = encodeFunctionData({
|
|
708
|
+
abi: [{ "inputs": [{ "internalType": "address", "name": "addr", "type": "address" }], "name": "f", "outputs": [], "stateMutability": "nonpayable", "type": "function" }],
|
|
709
|
+
functionName: 'f',
|
|
710
|
+
args: [address]
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
else if (coinType === 60) {
|
|
714
|
+
// Ethereum coin type - encode as 20-byte address
|
|
715
|
+
encodedAddress = address.padEnd(66, '0');
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
// Generic encoding for other coin types
|
|
719
|
+
encodedAddress = address.padEnd(66, '0');
|
|
720
|
+
}
|
|
721
|
+
console.log("node:", node);
|
|
722
|
+
console.log("encodedAddress:", encodedAddress);
|
|
723
|
+
// Encode the setAddr function call
|
|
724
|
+
const data = encodeFunctionData({
|
|
725
|
+
abi: resolverAbi,
|
|
726
|
+
functionName: 'setAddr',
|
|
727
|
+
args: [node, BigInt(coinType), encodedAddress]
|
|
728
|
+
});
|
|
729
|
+
return {
|
|
730
|
+
to: resolverAddress,
|
|
731
|
+
data
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
//# sourceMappingURL=AIAgentL2ENSDurenClient.js.map
|