hooddomains 0.1.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/README.md +67 -0
- package/dist/index.cjs +434 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +184 -0
- package/dist/index.d.ts +184 -0
- package/dist/index.js +425 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# hooddomains
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for [Hood Domains](https://www.hood.domains) — resolve, look up, and register
|
|
4
|
+
`.hood` names on Robinhood Chain. Everything is on-chain; the SDK is a thin wrapper over
|
|
5
|
+
[viem](https://viem.sh). No backend, no API keys beyond your own RPC.
|
|
6
|
+
|
|
7
|
+
**Hood Domains** · [www.hood.domains](https://www.hood.domains) · [app](https://app.hood.domains) · [docs](https://app.hood.domains/docs) · [X @hooddomains](https://x.com/hooddomains)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i hooddomains viem
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Read (only needs an RPC)
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { Hood } from "hooddomains";
|
|
17
|
+
|
|
18
|
+
const hood = new Hood({ rpcUrl: "https://robinhood-mainnet.g.alchemy.com/v2/YOUR_KEY" });
|
|
19
|
+
|
|
20
|
+
await hood.resolve("alice.hood"); // -> "0x…" | null (name -> wallet)
|
|
21
|
+
await hood.reverse("0x1234…abcd"); // -> "alice.hood" | null (wallet -> name)
|
|
22
|
+
await hood.text("alice.hood", "twitter"); // -> string | null
|
|
23
|
+
await hood.records("alice.hood"); // -> { address, records: { twitter, url, … }, resolver }
|
|
24
|
+
await hood.available("mynew"); // -> boolean
|
|
25
|
+
await hood.price("mynew", 1); // -> { wei, eth }
|
|
26
|
+
await hood.nameInfo("alice.hood"); // -> { registered, owner, expiry, resolver, tokenId }
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Write (needs a signer)
|
|
30
|
+
|
|
31
|
+
Pass a `privateKey` or a viem `walletClient`:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
const hood = new Hood({ rpcUrl, privateKey: "0x…" });
|
|
35
|
+
|
|
36
|
+
// commit -> wait ~60s -> register, paid in native ETH (overpayment refunded on-chain)
|
|
37
|
+
const res = await hood.register("mynew", { years: 1 });
|
|
38
|
+
// -> { name, owner, tokenId, paid: { eth }, commitTx, registerTx }
|
|
39
|
+
|
|
40
|
+
await hood.setAddr("mynew.hood", "0x…"); // point it at a wallet
|
|
41
|
+
await hood.setText("mynew.hood", "twitter", "hood"); // any key/value
|
|
42
|
+
await hood.setPrimary("mynew.hood"); // make it your primary name
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`setAddr` / `setText` deploy a per-owner resolver (granting addr + text roles) on first use, then
|
|
46
|
+
reuse it.
|
|
47
|
+
|
|
48
|
+
## Options
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
new Hood({
|
|
52
|
+
rpcUrl, // Robinhood Chain (chainId 4663) RPC
|
|
53
|
+
publicClient, // or bring your own viem PublicClient
|
|
54
|
+
walletClient, // or bring your own signer
|
|
55
|
+
privateKey, // or a private key for writes
|
|
56
|
+
contracts, // override addresses (defaults to the live v2 deployment)
|
|
57
|
+
deployBlock, // lower bound for record-key discovery
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Notes
|
|
62
|
+
|
|
63
|
+
- `register` takes ~70s (the ~60s commit-reveal front-run protection).
|
|
64
|
+
- `records()` discovers custom text keys from `TextChanged` events (not just a fixed list).
|
|
65
|
+
- Reading works with any RPC. For a server key, don't use a browser origin-allowlisted key.
|
|
66
|
+
- Contracts default to the live deployment on Robinhood Chain (chainId 4663). Import
|
|
67
|
+
`DEFAULT_CONTRACTS`, `robinhoodChain`, etc. from the package if you need them.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var viem = require('viem');
|
|
4
|
+
var accounts = require('viem/accounts');
|
|
5
|
+
|
|
6
|
+
// src/index.ts
|
|
7
|
+
var REGISTRY_ABI = viem.parseAbi([
|
|
8
|
+
"function getResolver(string label) view returns (address)",
|
|
9
|
+
"function getTokenId(uint256 anyId) view returns (uint256)",
|
|
10
|
+
"function ownerOf(uint256 tokenId) view returns (address)",
|
|
11
|
+
"function getExpiry(uint256 anyId) view returns (uint64)",
|
|
12
|
+
"function setResolver(uint256 anyId, address resolver)",
|
|
13
|
+
"function uri(uint256 id) view returns (string)"
|
|
14
|
+
]);
|
|
15
|
+
var REGISTRAR_ABI = viem.parseAbi([
|
|
16
|
+
"function isAvailable(string label) view returns (bool)",
|
|
17
|
+
"function getRegisterPrice(string label, uint64 duration) view returns (uint256 base, uint256 premium)",
|
|
18
|
+
"function MIN_COMMITMENT_AGE() view returns (uint64)",
|
|
19
|
+
"function MIN_REGISTER_DURATION() view returns (uint64)",
|
|
20
|
+
"function makeCommitment(string label, address owner, bytes32 secret, address subregistry, address resolver, uint64 duration, bytes32 referrer) pure returns (bytes32)",
|
|
21
|
+
"function commit(bytes32 commitment)",
|
|
22
|
+
"function register(string label, address owner, bytes32 secret, address subregistry, address resolver, uint64 duration, bytes32 referrer) payable returns (uint256)",
|
|
23
|
+
"function renew(string label, uint64 duration, bytes32 referrer) payable",
|
|
24
|
+
"event NameRegistered(uint256 indexed tokenId, string label, address owner, address subregistry, address resolver, uint64 duration, bytes32 indexed referrer, uint256 base, uint256 premium)"
|
|
25
|
+
]);
|
|
26
|
+
var RESOLVER_ABI = viem.parseAbi([
|
|
27
|
+
"function initialize(address admin, uint256 roleBitmap, bytes[] setters)",
|
|
28
|
+
"function addr(bytes32 node) view returns (address)",
|
|
29
|
+
"function text(bytes32 node, string key) view returns (string)",
|
|
30
|
+
"function setAddr(bytes32 node, address addr_)",
|
|
31
|
+
"function setText(bytes32 node, string key, string value)"
|
|
32
|
+
]);
|
|
33
|
+
var REVERSE_ABI = viem.parseAbi([
|
|
34
|
+
"function nameForAddr(address addr) view returns (string)",
|
|
35
|
+
"function setName(string name)"
|
|
36
|
+
]);
|
|
37
|
+
var FACTORY_ABI = viem.parseAbi([
|
|
38
|
+
"function deployProxy(address implementation, uint256 salt, bytes data) returns (address)"
|
|
39
|
+
]);
|
|
40
|
+
var TEXT_CHANGED_EVENT = viem.parseAbiItem(
|
|
41
|
+
"event TextChanged(bytes32 indexed node, string indexed indexedKey, string key, string value)"
|
|
42
|
+
);
|
|
43
|
+
var CHAIN_ID = 4663;
|
|
44
|
+
var robinhoodChain = viem.defineChain({
|
|
45
|
+
id: CHAIN_ID,
|
|
46
|
+
name: "Robinhood Chain",
|
|
47
|
+
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
|
48
|
+
rpcUrls: { default: { http: [] } }
|
|
49
|
+
});
|
|
50
|
+
var DEFAULT_CONTRACTS = {
|
|
51
|
+
registry: "0xd1a6f766577a0cd3c0f9f30b4add8623a49c5e41",
|
|
52
|
+
registrar: "0x59ac59d6a06689cc83b68dab07261cb25b8ca03e",
|
|
53
|
+
oracle: "0x0f23b020132f2367cf3218716f9b7e015a173aa3",
|
|
54
|
+
reverseRegistrar: "0x7614e0f829e2c7a0c093005ae887ff117d9e1df4",
|
|
55
|
+
resolverImpl: "0x7ddfb9789646f8633677601e339e578b638c8045",
|
|
56
|
+
verifiableFactory: "0x7ad6be4f4a6fbfdbe36571d69919b1f90ef1008f"
|
|
57
|
+
};
|
|
58
|
+
var RESOLVER_RECORD_ROLES = 1n << 0n | 1n << 128n | 1n << 4n | 1n << 132n;
|
|
59
|
+
var REGISTRY_DEPLOY_BLOCK = 5785243n;
|
|
60
|
+
var SECONDS_PER_YEAR = 31536000n;
|
|
61
|
+
var TEXT_KEYS = [
|
|
62
|
+
"url",
|
|
63
|
+
"com.twitter",
|
|
64
|
+
"twitter",
|
|
65
|
+
"com.github",
|
|
66
|
+
"org.telegram",
|
|
67
|
+
"com.discord",
|
|
68
|
+
"email",
|
|
69
|
+
"avatar",
|
|
70
|
+
"description",
|
|
71
|
+
"location"
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
// src/index.ts
|
|
75
|
+
var ZERO = "0x0000000000000000000000000000000000000000";
|
|
76
|
+
var Z32 = "0x" + "00".repeat(32);
|
|
77
|
+
var toLabel = (s) => String(s ?? "").trim().toLowerCase().replace(/\.hood$/, "");
|
|
78
|
+
function randomSecret() {
|
|
79
|
+
const bytes = new Uint8Array(32);
|
|
80
|
+
const g = globalThis.crypto;
|
|
81
|
+
if (!g?.getRandomValues) throw new Error("crypto.getRandomValues unavailable");
|
|
82
|
+
g.getRandomValues(bytes);
|
|
83
|
+
return "0x" + Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
84
|
+
}
|
|
85
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
86
|
+
var Hood = class {
|
|
87
|
+
constructor(opts = {}) {
|
|
88
|
+
this.contracts = { ...DEFAULT_CONTRACTS, ...opts.contracts };
|
|
89
|
+
this.deployBlock = opts.deployBlock ?? REGISTRY_DEPLOY_BLOCK;
|
|
90
|
+
this.publicClient = opts.publicClient ?? viem.createPublicClient({
|
|
91
|
+
chain: robinhoodChain,
|
|
92
|
+
transport: viem.http(opts.rpcUrl)
|
|
93
|
+
});
|
|
94
|
+
if (opts.walletClient) {
|
|
95
|
+
this.walletClient = opts.walletClient;
|
|
96
|
+
this.acct = opts.walletClient.account;
|
|
97
|
+
} else if (opts.privateKey) {
|
|
98
|
+
const pk = opts.privateKey.startsWith("0x") ? opts.privateKey : "0x" + opts.privateKey;
|
|
99
|
+
const account = accounts.privateKeyToAccount(pk);
|
|
100
|
+
this.acct = account;
|
|
101
|
+
this.walletClient = viem.createWalletClient({
|
|
102
|
+
account,
|
|
103
|
+
chain: robinhoodChain,
|
|
104
|
+
transport: viem.http(opts.rpcUrl)
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// ---- internals ----
|
|
109
|
+
signer() {
|
|
110
|
+
if (!this.walletClient || !this.acct)
|
|
111
|
+
throw new Error(
|
|
112
|
+
"This method needs a signer. Construct Hood({ privateKey }) or Hood({ walletClient })."
|
|
113
|
+
);
|
|
114
|
+
return { wallet: this.walletClient, account: this.acct };
|
|
115
|
+
}
|
|
116
|
+
accountAddress() {
|
|
117
|
+
const { account } = this.signer();
|
|
118
|
+
return typeof account === "string" ? account : account.address;
|
|
119
|
+
}
|
|
120
|
+
node(name) {
|
|
121
|
+
return viem.namehash(toLabel(name) + ".hood");
|
|
122
|
+
}
|
|
123
|
+
labelId(label) {
|
|
124
|
+
return BigInt(viem.keccak256(viem.toBytes(toLabel(label))));
|
|
125
|
+
}
|
|
126
|
+
async resolverOf(label) {
|
|
127
|
+
const r = await this.publicClient.readContract({
|
|
128
|
+
address: this.contracts.registry,
|
|
129
|
+
abi: REGISTRY_ABI,
|
|
130
|
+
functionName: "getResolver",
|
|
131
|
+
args: [toLabel(label)]
|
|
132
|
+
});
|
|
133
|
+
return r && r.toLowerCase() !== ZERO ? viem.getAddress(r) : null;
|
|
134
|
+
}
|
|
135
|
+
// ---- reads ----
|
|
136
|
+
/** Resolve a name to the wallet it points at (forward resolution). */
|
|
137
|
+
async resolve(name) {
|
|
138
|
+
const resolver = await this.resolverOf(name);
|
|
139
|
+
if (!resolver) return null;
|
|
140
|
+
const a = await this.publicClient.readContract({
|
|
141
|
+
address: resolver,
|
|
142
|
+
abi: RESOLVER_ABI,
|
|
143
|
+
functionName: "addr",
|
|
144
|
+
args: [this.node(name)]
|
|
145
|
+
});
|
|
146
|
+
return a && a !== ZERO ? viem.getAddress(a) : null;
|
|
147
|
+
}
|
|
148
|
+
/** Get the primary (reverse) name of a wallet. */
|
|
149
|
+
async reverse(address) {
|
|
150
|
+
const name = await this.publicClient.readContract({
|
|
151
|
+
address: this.contracts.reverseRegistrar,
|
|
152
|
+
abi: REVERSE_ABI,
|
|
153
|
+
functionName: "nameForAddr",
|
|
154
|
+
args: [viem.getAddress(address)]
|
|
155
|
+
});
|
|
156
|
+
return name || null;
|
|
157
|
+
}
|
|
158
|
+
/** Read a single text record. */
|
|
159
|
+
async text(name, key) {
|
|
160
|
+
const resolver = await this.resolverOf(name);
|
|
161
|
+
if (!resolver) return null;
|
|
162
|
+
const v = await this.publicClient.readContract({
|
|
163
|
+
address: resolver,
|
|
164
|
+
abi: RESOLVER_ABI,
|
|
165
|
+
functionName: "text",
|
|
166
|
+
args: [this.node(name), key]
|
|
167
|
+
});
|
|
168
|
+
return v || null;
|
|
169
|
+
}
|
|
170
|
+
/** Read a name's address + all text records (custom keys discovered via TextChanged events). */
|
|
171
|
+
async records(name) {
|
|
172
|
+
const resolver = await this.resolverOf(name);
|
|
173
|
+
if (!resolver) return { address: null, records: {}, resolver: null };
|
|
174
|
+
const node = this.node(name);
|
|
175
|
+
let address = null;
|
|
176
|
+
try {
|
|
177
|
+
const a = await this.publicClient.readContract({
|
|
178
|
+
address: resolver,
|
|
179
|
+
abi: RESOLVER_ABI,
|
|
180
|
+
functionName: "addr",
|
|
181
|
+
args: [node]
|
|
182
|
+
});
|
|
183
|
+
if (a && a !== ZERO) address = viem.getAddress(a);
|
|
184
|
+
} catch {
|
|
185
|
+
}
|
|
186
|
+
const keys = new Set(TEXT_KEYS);
|
|
187
|
+
try {
|
|
188
|
+
const latest = await this.publicClient.getBlockNumber();
|
|
189
|
+
const step = 9000n;
|
|
190
|
+
for (let from = this.deployBlock; from <= latest; from += step) {
|
|
191
|
+
const to = from + step - 1n > latest ? latest : from + step - 1n;
|
|
192
|
+
const logs = await this.publicClient.getLogs({
|
|
193
|
+
address: resolver,
|
|
194
|
+
event: TEXT_CHANGED_EVENT,
|
|
195
|
+
args: { node },
|
|
196
|
+
fromBlock: from,
|
|
197
|
+
toBlock: to
|
|
198
|
+
});
|
|
199
|
+
for (const l of logs) if (l.args.key) keys.add(l.args.key);
|
|
200
|
+
}
|
|
201
|
+
} catch {
|
|
202
|
+
}
|
|
203
|
+
const records = {};
|
|
204
|
+
await Promise.all(
|
|
205
|
+
[...keys].map(async (k) => {
|
|
206
|
+
try {
|
|
207
|
+
const v = await this.publicClient.readContract({
|
|
208
|
+
address: resolver,
|
|
209
|
+
abi: RESOLVER_ABI,
|
|
210
|
+
functionName: "text",
|
|
211
|
+
args: [node, k]
|
|
212
|
+
});
|
|
213
|
+
if (v) records[k] = v;
|
|
214
|
+
} catch {
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
);
|
|
218
|
+
return { address, records, resolver: viem.getAddress(resolver) };
|
|
219
|
+
}
|
|
220
|
+
/** Whether a label is available to register. */
|
|
221
|
+
async available(label) {
|
|
222
|
+
return await this.publicClient.readContract({
|
|
223
|
+
address: this.contracts.registrar,
|
|
224
|
+
abi: REGISTRAR_ABI,
|
|
225
|
+
functionName: "isAvailable",
|
|
226
|
+
args: [toLabel(label)]
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
/** Registration price for a label over `years`. */
|
|
230
|
+
async price(label, years = 1) {
|
|
231
|
+
const [base, premium] = await this.publicClient.readContract({
|
|
232
|
+
address: this.contracts.registrar,
|
|
233
|
+
abi: REGISTRAR_ABI,
|
|
234
|
+
functionName: "getRegisterPrice",
|
|
235
|
+
args: [toLabel(label), BigInt(Math.max(1, years)) * SECONDS_PER_YEAR]
|
|
236
|
+
});
|
|
237
|
+
const wei = base + premium;
|
|
238
|
+
return { wei, eth: viem.formatEther(wei) };
|
|
239
|
+
}
|
|
240
|
+
/** Owner, expiry, resolver, and tokenId of a name. */
|
|
241
|
+
async nameInfo(name) {
|
|
242
|
+
const label = toLabel(name);
|
|
243
|
+
const tokenId = await this.publicClient.readContract({
|
|
244
|
+
address: this.contracts.registry,
|
|
245
|
+
abi: REGISTRY_ABI,
|
|
246
|
+
functionName: "getTokenId",
|
|
247
|
+
args: [this.labelId(label)]
|
|
248
|
+
});
|
|
249
|
+
const [owner, expiry] = await Promise.all([
|
|
250
|
+
this.publicClient.readContract({
|
|
251
|
+
address: this.contracts.registry,
|
|
252
|
+
abi: REGISTRY_ABI,
|
|
253
|
+
functionName: "ownerOf",
|
|
254
|
+
args: [tokenId]
|
|
255
|
+
}).catch(() => ZERO),
|
|
256
|
+
this.publicClient.readContract({
|
|
257
|
+
address: this.contracts.registry,
|
|
258
|
+
abi: REGISTRY_ABI,
|
|
259
|
+
functionName: "getExpiry",
|
|
260
|
+
args: [tokenId]
|
|
261
|
+
}).catch(() => 0n)
|
|
262
|
+
]);
|
|
263
|
+
const resolver = await this.resolverOf(label);
|
|
264
|
+
const registered = owner !== ZERO;
|
|
265
|
+
return {
|
|
266
|
+
name: label + ".hood",
|
|
267
|
+
registered,
|
|
268
|
+
owner: registered ? viem.getAddress(owner) : null,
|
|
269
|
+
expiry: Number(expiry) > 0 ? new Date(Number(expiry) * 1e3) : null,
|
|
270
|
+
resolver,
|
|
271
|
+
tokenId: tokenId.toString()
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
// ---- writes (need a signer) ----
|
|
275
|
+
/** Register a name via commit → wait (~60s) → register. Paid in native ETH. */
|
|
276
|
+
async register(label, opts = {}) {
|
|
277
|
+
const { wallet, account } = this.signer();
|
|
278
|
+
const l = toLabel(label);
|
|
279
|
+
const owner = opts.owner ? viem.getAddress(opts.owner) : this.accountAddress();
|
|
280
|
+
const years = Math.max(1, opts.years ?? 1);
|
|
281
|
+
const duration = BigInt(years) * SECONDS_PER_YEAR;
|
|
282
|
+
const [base, premium] = await this.publicClient.readContract({
|
|
283
|
+
address: this.contracts.registrar,
|
|
284
|
+
abi: REGISTRAR_ABI,
|
|
285
|
+
functionName: "getRegisterPrice",
|
|
286
|
+
args: [l, duration]
|
|
287
|
+
});
|
|
288
|
+
const value = base + premium;
|
|
289
|
+
const secret = randomSecret();
|
|
290
|
+
const commitment = await this.publicClient.readContract({
|
|
291
|
+
address: this.contracts.registrar,
|
|
292
|
+
abi: REGISTRAR_ABI,
|
|
293
|
+
functionName: "makeCommitment",
|
|
294
|
+
args: [l, owner, secret, ZERO, ZERO, duration, Z32]
|
|
295
|
+
});
|
|
296
|
+
const commitTx = await wallet.writeContract({
|
|
297
|
+
address: this.contracts.registrar,
|
|
298
|
+
abi: REGISTRAR_ABI,
|
|
299
|
+
functionName: "commit",
|
|
300
|
+
args: [commitment],
|
|
301
|
+
account,
|
|
302
|
+
chain: robinhoodChain
|
|
303
|
+
});
|
|
304
|
+
await this.publicClient.waitForTransactionReceipt({ hash: commitTx });
|
|
305
|
+
const minAge = await this.publicClient.readContract({
|
|
306
|
+
address: this.contracts.registrar,
|
|
307
|
+
abi: REGISTRAR_ABI,
|
|
308
|
+
functionName: "MIN_COMMITMENT_AGE"
|
|
309
|
+
});
|
|
310
|
+
await sleep((Number(minAge) + 3) * 1e3);
|
|
311
|
+
const registerTx = await wallet.writeContract({
|
|
312
|
+
address: this.contracts.registrar,
|
|
313
|
+
abi: REGISTRAR_ABI,
|
|
314
|
+
functionName: "register",
|
|
315
|
+
args: [l, owner, secret, ZERO, ZERO, duration, Z32],
|
|
316
|
+
value,
|
|
317
|
+
account,
|
|
318
|
+
chain: robinhoodChain
|
|
319
|
+
});
|
|
320
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({
|
|
321
|
+
hash: registerTx
|
|
322
|
+
});
|
|
323
|
+
const evs = viem.parseEventLogs({
|
|
324
|
+
abi: REGISTRAR_ABI,
|
|
325
|
+
eventName: "NameRegistered",
|
|
326
|
+
logs: receipt.logs
|
|
327
|
+
});
|
|
328
|
+
const tokenId = evs.length ? evs[0].args.tokenId.toString() : "";
|
|
329
|
+
return {
|
|
330
|
+
name: l + ".hood",
|
|
331
|
+
owner,
|
|
332
|
+
years,
|
|
333
|
+
paid: { wei: value, eth: viem.formatEther(value) },
|
|
334
|
+
tokenId,
|
|
335
|
+
commitTx,
|
|
336
|
+
registerTx
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
/** Set the caller's primary (reverse) name. */
|
|
340
|
+
async setPrimary(name) {
|
|
341
|
+
const { wallet, account } = this.signer();
|
|
342
|
+
return wallet.writeContract({
|
|
343
|
+
address: this.contracts.reverseRegistrar,
|
|
344
|
+
abi: REVERSE_ABI,
|
|
345
|
+
functionName: "setName",
|
|
346
|
+
args: [toLabel(name) + ".hood"],
|
|
347
|
+
account,
|
|
348
|
+
chain: robinhoodChain
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
/** Set the address record (deploys a per-owner resolver on first use). */
|
|
352
|
+
async setAddr(name, address) {
|
|
353
|
+
const { wallet, account } = this.signer();
|
|
354
|
+
const resolver = await this.ensureResolver(name);
|
|
355
|
+
return wallet.writeContract({
|
|
356
|
+
address: resolver,
|
|
357
|
+
abi: RESOLVER_ABI,
|
|
358
|
+
functionName: "setAddr",
|
|
359
|
+
args: [this.node(name), viem.getAddress(address)],
|
|
360
|
+
account,
|
|
361
|
+
chain: robinhoodChain
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
/** Set a text record (deploys a per-owner resolver on first use). */
|
|
365
|
+
async setText(name, key, value) {
|
|
366
|
+
const { wallet, account } = this.signer();
|
|
367
|
+
const resolver = await this.ensureResolver(name);
|
|
368
|
+
return wallet.writeContract({
|
|
369
|
+
address: resolver,
|
|
370
|
+
abi: RESOLVER_ABI,
|
|
371
|
+
functionName: "setText",
|
|
372
|
+
args: [this.node(name), key, value],
|
|
373
|
+
account,
|
|
374
|
+
chain: robinhoodChain
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
/** Return the name's resolver, deploying + linking a per-owner one (addr+text roles) if needed. */
|
|
378
|
+
async ensureResolver(name) {
|
|
379
|
+
const { wallet, account } = this.signer();
|
|
380
|
+
const label = toLabel(name);
|
|
381
|
+
const existing = await this.resolverOf(label);
|
|
382
|
+
if (existing) return existing;
|
|
383
|
+
const initData = viem.encodeFunctionData({
|
|
384
|
+
abi: RESOLVER_ABI,
|
|
385
|
+
functionName: "initialize",
|
|
386
|
+
args: [this.accountAddress(), RESOLVER_RECORD_ROLES, []]
|
|
387
|
+
});
|
|
388
|
+
const salt = BigInt(Math.floor(Math.random() * 2 ** 48));
|
|
389
|
+
const sim = await this.publicClient.simulateContract({
|
|
390
|
+
address: this.contracts.verifiableFactory,
|
|
391
|
+
abi: FACTORY_ABI,
|
|
392
|
+
functionName: "deployProxy",
|
|
393
|
+
args: [this.contracts.resolverImpl, salt, initData],
|
|
394
|
+
account: this.accountAddress()
|
|
395
|
+
});
|
|
396
|
+
const resolver = sim.result;
|
|
397
|
+
const deployTx = await wallet.writeContract({
|
|
398
|
+
address: this.contracts.verifiableFactory,
|
|
399
|
+
abi: FACTORY_ABI,
|
|
400
|
+
functionName: "deployProxy",
|
|
401
|
+
args: [this.contracts.resolverImpl, salt, initData],
|
|
402
|
+
account,
|
|
403
|
+
chain: robinhoodChain
|
|
404
|
+
});
|
|
405
|
+
await this.publicClient.waitForTransactionReceipt({ hash: deployTx });
|
|
406
|
+
const tokenId = await this.publicClient.readContract({
|
|
407
|
+
address: this.contracts.registry,
|
|
408
|
+
abi: REGISTRY_ABI,
|
|
409
|
+
functionName: "getTokenId",
|
|
410
|
+
args: [this.labelId(label)]
|
|
411
|
+
});
|
|
412
|
+
const setResTx = await wallet.writeContract({
|
|
413
|
+
address: this.contracts.registry,
|
|
414
|
+
abi: REGISTRY_ABI,
|
|
415
|
+
functionName: "setResolver",
|
|
416
|
+
args: [tokenId, resolver],
|
|
417
|
+
account,
|
|
418
|
+
chain: robinhoodChain
|
|
419
|
+
});
|
|
420
|
+
await this.publicClient.waitForTransactionReceipt({ hash: setResTx });
|
|
421
|
+
return resolver;
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
exports.CHAIN_ID = CHAIN_ID;
|
|
426
|
+
exports.DEFAULT_CONTRACTS = DEFAULT_CONTRACTS;
|
|
427
|
+
exports.Hood = Hood;
|
|
428
|
+
exports.REGISTRY_DEPLOY_BLOCK = REGISTRY_DEPLOY_BLOCK;
|
|
429
|
+
exports.RESOLVER_RECORD_ROLES = RESOLVER_RECORD_ROLES;
|
|
430
|
+
exports.SECONDS_PER_YEAR = SECONDS_PER_YEAR;
|
|
431
|
+
exports.TEXT_KEYS = TEXT_KEYS;
|
|
432
|
+
exports.robinhoodChain = robinhoodChain;
|
|
433
|
+
//# sourceMappingURL=index.cjs.map
|
|
434
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/abis.ts","../src/config.ts","../src/index.ts"],"names":["parseAbi","parseAbiItem","defineChain","createPublicClient","http","privateKeyToAccount","createWalletClient","namehash","keccak256","toBytes","getAddress","formatEther","parseEventLogs","encodeFunctionData"],"mappings":"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","file":"index.cjs","sourcesContent":["import { parseAbi, parseAbiItem } from \"viem\";\n\nexport const REGISTRY_ABI = parseAbi([\n \"function getResolver(string label) view returns (address)\",\n \"function getTokenId(uint256 anyId) view returns (uint256)\",\n \"function ownerOf(uint256 tokenId) view returns (address)\",\n \"function getExpiry(uint256 anyId) view returns (uint64)\",\n \"function setResolver(uint256 anyId, address resolver)\",\n \"function uri(uint256 id) view returns (string)\",\n]);\n\nexport const REGISTRAR_ABI = parseAbi([\n \"function isAvailable(string label) view returns (bool)\",\n \"function getRegisterPrice(string label, uint64 duration) view returns (uint256 base, uint256 premium)\",\n \"function MIN_COMMITMENT_AGE() view returns (uint64)\",\n \"function MIN_REGISTER_DURATION() view returns (uint64)\",\n \"function makeCommitment(string label, address owner, bytes32 secret, address subregistry, address resolver, uint64 duration, bytes32 referrer) pure returns (bytes32)\",\n \"function commit(bytes32 commitment)\",\n \"function register(string label, address owner, bytes32 secret, address subregistry, address resolver, uint64 duration, bytes32 referrer) payable returns (uint256)\",\n \"function renew(string label, uint64 duration, bytes32 referrer) payable\",\n \"event NameRegistered(uint256 indexed tokenId, string label, address owner, address subregistry, address resolver, uint64 duration, bytes32 indexed referrer, uint256 base, uint256 premium)\",\n]);\n\nexport const RESOLVER_ABI = parseAbi([\n \"function initialize(address admin, uint256 roleBitmap, bytes[] setters)\",\n \"function addr(bytes32 node) view returns (address)\",\n \"function text(bytes32 node, string key) view returns (string)\",\n \"function setAddr(bytes32 node, address addr_)\",\n \"function setText(bytes32 node, string key, string value)\",\n]);\n\nexport const REVERSE_ABI = parseAbi([\n \"function nameForAddr(address addr) view returns (string)\",\n \"function setName(string name)\",\n]);\n\nexport const FACTORY_ABI = parseAbi([\n \"function deployProxy(address implementation, uint256 salt, bytes data) returns (address)\",\n]);\n\nexport const TEXT_CHANGED_EVENT = parseAbiItem(\n \"event TextChanged(bytes32 indexed node, string indexed indexedKey, string key, string value)\"\n);\n","import { defineChain } from \"viem\";\n\nexport const CHAIN_ID = 4663;\n\n/** Robinhood Chain (Arbitrum Orbit L2). RPC is supplied by the caller. */\nexport const robinhoodChain = defineChain({\n id: CHAIN_ID,\n name: \"Robinhood Chain\",\n nativeCurrency: { name: \"Ether\", symbol: \"ETH\", decimals: 18 },\n rpcUrls: { default: { http: [] } },\n});\n\nexport interface HoodContracts {\n registry: `0x${string}`;\n registrar: `0x${string}`;\n oracle: `0x${string}`;\n reverseRegistrar: `0x${string}`;\n resolverImpl: `0x${string}`;\n verifiableFactory: `0x${string}`;\n}\n\n/** Live Hood Domains deployment on Robinhood Chain (hood-robinhood-v2). */\nexport const DEFAULT_CONTRACTS: HoodContracts = {\n registry: \"0xd1a6f766577a0cd3c0f9f30b4add8623a49c5e41\",\n registrar: \"0x59ac59d6a06689cc83b68dab07261cb25b8ca03e\",\n oracle: \"0x0f23b020132f2367cf3218716f9b7e015a173aa3\",\n reverseRegistrar: \"0x7614e0f829e2c7a0c093005ae887ff117d9e1df4\",\n resolverImpl: \"0x7ddfb9789646f8633677601e339e578b638c8045\",\n verifiableFactory: \"0x7ad6be4f4a6fbfdbe36571d69919b1f90ef1008f\",\n};\n\n/** Roles granted to a per-owner resolver at deploy: ADDR + TEXT (regular + admin). */\nexport const RESOLVER_RECORD_ROLES =\n (1n << 0n) | (1n << 128n) | (1n << 4n) | (1n << 132n);\n\n/** Block the registry was deployed at — lower bound for TextChanged log scans. */\nexport const REGISTRY_DEPLOY_BLOCK = 5785243n;\n\nexport const SECONDS_PER_YEAR = 31536000n;\n\n/** Common text-record keys probed in addition to keys discovered from events. */\nexport const TEXT_KEYS = [\n \"url\",\n \"com.twitter\",\n \"twitter\",\n \"com.github\",\n \"org.telegram\",\n \"com.discord\",\n \"email\",\n \"avatar\",\n \"description\",\n \"location\",\n];\n","/**\n * Hood Domains SDK — resolve, look up, and register `.hood` names on Robinhood Chain.\n * Everything is on-chain; this is a thin wrapper over viem. No backend, no API keys.\n *\n * Website: https://www.hood.domains\n * App: https://app.hood.domains\n * Docs: https://app.hood.domains/docs\n * X: https://x.com/hooddomains (@hooddomains)\n */\nimport {\n createPublicClient,\n createWalletClient,\n encodeFunctionData,\n formatEther,\n getAddress,\n http,\n keccak256,\n namehash,\n parseEventLogs,\n toBytes,\n type Account,\n type Address,\n type Hex,\n type PublicClient,\n type WalletClient,\n} from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\n\nimport {\n FACTORY_ABI,\n REGISTRAR_ABI,\n REGISTRY_ABI,\n RESOLVER_ABI,\n REVERSE_ABI,\n TEXT_CHANGED_EVENT,\n} from \"./abis\";\nimport {\n DEFAULT_CONTRACTS,\n REGISTRY_DEPLOY_BLOCK,\n RESOLVER_RECORD_ROLES,\n SECONDS_PER_YEAR,\n TEXT_KEYS,\n robinhoodChain,\n type HoodContracts,\n} from \"./config\";\n\nexport * from \"./config\";\n\nconst ZERO = \"0x0000000000000000000000000000000000000000\" as const;\nconst Z32 = (\"0x\" + \"00\".repeat(32)) as Hex;\n\nexport interface HoodOptions {\n /** RPC URL for Robinhood Chain (chainId 4663). Ignored if `publicClient` is given. */\n rpcUrl?: string;\n /** Bring your own read client. */\n publicClient?: PublicClient;\n /** Bring your own signer (with account + chain configured) for writes. */\n walletClient?: WalletClient;\n /** Or a private key to sign writes. */\n privateKey?: Hex | string;\n /** Override contract addresses. */\n contracts?: Partial<HoodContracts>;\n /** Lower bound for TextChanged log scans (defaults to the registry deploy block). */\n deployBlock?: bigint;\n}\n\nexport interface NameRecords {\n /** The wallet the name resolves to (its addr record), or null. */\n address: Address | null;\n /** Text records: key -> value (only non-empty). */\n records: Record<string, string>;\n /** The name's resolver, or null if none set. */\n resolver: Address | null;\n}\n\nexport interface NameInfo {\n name: string;\n registered: boolean;\n owner: Address | null;\n expiry: Date | null;\n resolver: Address | null;\n tokenId: string;\n}\n\nexport interface RegisterResult {\n name: string;\n owner: Address;\n years: number;\n paid: { wei: bigint; eth: string };\n tokenId: string;\n commitTx: Hex;\n registerTx: Hex;\n}\n\nconst toLabel = (s: string): string =>\n String(s ?? \"\")\n .trim()\n .toLowerCase()\n .replace(/\\.hood$/, \"\");\n\nfunction randomSecret(): Hex {\n const bytes = new Uint8Array(32);\n const g = (globalThis as { crypto?: Crypto }).crypto;\n if (!g?.getRandomValues) throw new Error(\"crypto.getRandomValues unavailable\");\n g.getRandomValues(bytes);\n return (\"0x\" +\n Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\")) as Hex;\n}\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n/**\n * Client for Hood Domains (.hood names on Robinhood Chain). Read methods need only an RPC;\n * write methods need a signer (pass `privateKey` or `walletClient`).\n *\n * @example\n * const hood = new Hood({ rpcUrl });\n * await hood.resolve(\"alice.hood\"); // -> 0x… | null\n * await hood.reverse(\"0x…\"); // -> \"alice.hood\" | null\n */\nexport class Hood {\n readonly publicClient: PublicClient;\n readonly walletClient?: WalletClient;\n readonly contracts: HoodContracts;\n readonly deployBlock: bigint;\n private readonly acct?: Account | Address;\n\n constructor(opts: HoodOptions = {}) {\n this.contracts = { ...DEFAULT_CONTRACTS, ...opts.contracts };\n this.deployBlock = opts.deployBlock ?? REGISTRY_DEPLOY_BLOCK;\n this.publicClient =\n opts.publicClient ??\n (createPublicClient({\n chain: robinhoodChain,\n transport: http(opts.rpcUrl),\n }) as PublicClient);\n\n if (opts.walletClient) {\n this.walletClient = opts.walletClient;\n this.acct = opts.walletClient.account;\n } else if (opts.privateKey) {\n const pk = (\n opts.privateKey.startsWith(\"0x\") ? opts.privateKey : \"0x\" + opts.privateKey\n ) as Hex;\n const account = privateKeyToAccount(pk);\n this.acct = account;\n this.walletClient = createWalletClient({\n account,\n chain: robinhoodChain,\n transport: http(opts.rpcUrl),\n });\n }\n }\n\n // ---- internals ----\n\n private signer(): { wallet: WalletClient; account: Account | Address } {\n if (!this.walletClient || !this.acct)\n throw new Error(\n \"This method needs a signer. Construct Hood({ privateKey }) or Hood({ walletClient }).\"\n );\n return { wallet: this.walletClient, account: this.acct };\n }\n\n private accountAddress(): Address {\n const { account } = this.signer();\n return typeof account === \"string\" ? account : account.address;\n }\n\n private node(name: string): Hex {\n return namehash(toLabel(name) + \".hood\");\n }\n\n private labelId(label: string): bigint {\n return BigInt(keccak256(toBytes(toLabel(label))));\n }\n\n private async resolverOf(label: string): Promise<Address | null> {\n const r = (await this.publicClient.readContract({\n address: this.contracts.registry,\n abi: REGISTRY_ABI,\n functionName: \"getResolver\",\n args: [toLabel(label)],\n })) as Address;\n return r && r.toLowerCase() !== ZERO ? getAddress(r) : null;\n }\n\n // ---- reads ----\n\n /** Resolve a name to the wallet it points at (forward resolution). */\n async resolve(name: string): Promise<Address | null> {\n const resolver = await this.resolverOf(name);\n if (!resolver) return null;\n const a = (await this.publicClient.readContract({\n address: resolver,\n abi: RESOLVER_ABI,\n functionName: \"addr\",\n args: [this.node(name)],\n })) as Address;\n return a && a !== ZERO ? getAddress(a) : null;\n }\n\n /** Get the primary (reverse) name of a wallet. */\n async reverse(address: Address): Promise<string | null> {\n const name = (await this.publicClient.readContract({\n address: this.contracts.reverseRegistrar,\n abi: REVERSE_ABI,\n functionName: \"nameForAddr\",\n args: [getAddress(address)],\n })) as string;\n return name || null;\n }\n\n /** Read a single text record. */\n async text(name: string, key: string): Promise<string | null> {\n const resolver = await this.resolverOf(name);\n if (!resolver) return null;\n const v = (await this.publicClient.readContract({\n address: resolver,\n abi: RESOLVER_ABI,\n functionName: \"text\",\n args: [this.node(name), key],\n })) as string;\n return v || null;\n }\n\n /** Read a name's address + all text records (custom keys discovered via TextChanged events). */\n async records(name: string): Promise<NameRecords> {\n const resolver = await this.resolverOf(name);\n if (!resolver) return { address: null, records: {}, resolver: null };\n const node = this.node(name);\n\n let address: Address | null = null;\n try {\n const a = (await this.publicClient.readContract({\n address: resolver,\n abi: RESOLVER_ABI,\n functionName: \"addr\",\n args: [node],\n })) as Address;\n if (a && a !== ZERO) address = getAddress(a);\n } catch {\n /* no addr */\n }\n\n const keys = new Set<string>(TEXT_KEYS);\n try {\n const latest = await this.publicClient.getBlockNumber();\n const step = 9000n;\n for (let from = this.deployBlock; from <= latest; from += step) {\n const to = from + step - 1n > latest ? latest : from + step - 1n;\n const logs = await this.publicClient.getLogs({\n address: resolver,\n event: TEXT_CHANGED_EVENT,\n args: { node },\n fromBlock: from,\n toBlock: to,\n });\n for (const l of logs) if (l.args.key) keys.add(l.args.key);\n }\n } catch {\n /* fall back to common keys */\n }\n\n const records: Record<string, string> = {};\n await Promise.all(\n [...keys].map(async (k) => {\n try {\n const v = (await this.publicClient.readContract({\n address: resolver,\n abi: RESOLVER_ABI,\n functionName: \"text\",\n args: [node, k],\n })) as string;\n if (v) records[k] = v;\n } catch {\n /* skip */\n }\n })\n );\n\n return { address, records, resolver: getAddress(resolver) };\n }\n\n /** Whether a label is available to register. */\n async available(label: string): Promise<boolean> {\n return (await this.publicClient.readContract({\n address: this.contracts.registrar,\n abi: REGISTRAR_ABI,\n functionName: \"isAvailable\",\n args: [toLabel(label)],\n })) as boolean;\n }\n\n /** Registration price for a label over `years`. */\n async price(label: string, years = 1): Promise<{ wei: bigint; eth: string }> {\n const [base, premium] = (await this.publicClient.readContract({\n address: this.contracts.registrar,\n abi: REGISTRAR_ABI,\n functionName: \"getRegisterPrice\",\n args: [toLabel(label), BigInt(Math.max(1, years)) * SECONDS_PER_YEAR],\n })) as [bigint, bigint];\n const wei = base + premium;\n return { wei, eth: formatEther(wei) };\n }\n\n /** Owner, expiry, resolver, and tokenId of a name. */\n async nameInfo(name: string): Promise<NameInfo> {\n const label = toLabel(name);\n const tokenId = (await this.publicClient.readContract({\n address: this.contracts.registry,\n abi: REGISTRY_ABI,\n functionName: \"getTokenId\",\n args: [this.labelId(label)],\n })) as bigint;\n const [owner, expiry] = await Promise.all([\n this.publicClient\n .readContract({\n address: this.contracts.registry,\n abi: REGISTRY_ABI,\n functionName: \"ownerOf\",\n args: [tokenId],\n })\n .catch(() => ZERO as Address),\n this.publicClient\n .readContract({\n address: this.contracts.registry,\n abi: REGISTRY_ABI,\n functionName: \"getExpiry\",\n args: [tokenId],\n })\n .catch(() => 0n),\n ]);\n const resolver = await this.resolverOf(label);\n const registered = (owner as Address) !== ZERO;\n return {\n name: label + \".hood\",\n registered,\n owner: registered ? getAddress(owner as Address) : null,\n expiry: Number(expiry) > 0 ? new Date(Number(expiry) * 1000) : null,\n resolver,\n tokenId: tokenId.toString(),\n };\n }\n\n // ---- writes (need a signer) ----\n\n /** Register a name via commit → wait (~60s) → register. Paid in native ETH. */\n async register(\n label: string,\n opts: { years?: number; owner?: Address } = {}\n ): Promise<RegisterResult> {\n const { wallet, account } = this.signer();\n const l = toLabel(label);\n const owner = opts.owner ? getAddress(opts.owner) : this.accountAddress();\n const years = Math.max(1, opts.years ?? 1);\n const duration = BigInt(years) * SECONDS_PER_YEAR;\n\n const [base, premium] = (await this.publicClient.readContract({\n address: this.contracts.registrar,\n abi: REGISTRAR_ABI,\n functionName: \"getRegisterPrice\",\n args: [l, duration],\n })) as [bigint, bigint];\n const value = base + premium;\n\n const secret = randomSecret();\n const commitment = (await this.publicClient.readContract({\n address: this.contracts.registrar,\n abi: REGISTRAR_ABI,\n functionName: \"makeCommitment\",\n args: [l, owner, secret, ZERO, ZERO, duration, Z32],\n })) as Hex;\n\n const commitTx = await wallet.writeContract({\n address: this.contracts.registrar,\n abi: REGISTRAR_ABI,\n functionName: \"commit\",\n args: [commitment],\n account,\n chain: robinhoodChain,\n });\n await this.publicClient.waitForTransactionReceipt({ hash: commitTx });\n\n const minAge = (await this.publicClient.readContract({\n address: this.contracts.registrar,\n abi: REGISTRAR_ABI,\n functionName: \"MIN_COMMITMENT_AGE\",\n })) as bigint;\n await sleep((Number(minAge) + 3) * 1000);\n\n const registerTx = await wallet.writeContract({\n address: this.contracts.registrar,\n abi: REGISTRAR_ABI,\n functionName: \"register\",\n args: [l, owner, secret, ZERO, ZERO, duration, Z32],\n value,\n account,\n chain: robinhoodChain,\n });\n const receipt = await this.publicClient.waitForTransactionReceipt({\n hash: registerTx,\n });\n const evs = parseEventLogs({\n abi: REGISTRAR_ABI,\n eventName: \"NameRegistered\",\n logs: receipt.logs,\n });\n const tokenId = evs.length ? (evs[0].args.tokenId as bigint).toString() : \"\";\n\n return {\n name: l + \".hood\",\n owner,\n years,\n paid: { wei: value, eth: formatEther(value) },\n tokenId,\n commitTx,\n registerTx,\n };\n }\n\n /** Set the caller's primary (reverse) name. */\n async setPrimary(name: string): Promise<Hex> {\n const { wallet, account } = this.signer();\n return wallet.writeContract({\n address: this.contracts.reverseRegistrar,\n abi: REVERSE_ABI,\n functionName: \"setName\",\n args: [toLabel(name) + \".hood\"],\n account,\n chain: robinhoodChain,\n });\n }\n\n /** Set the address record (deploys a per-owner resolver on first use). */\n async setAddr(name: string, address: Address): Promise<Hex> {\n const { wallet, account } = this.signer();\n const resolver = await this.ensureResolver(name);\n return wallet.writeContract({\n address: resolver,\n abi: RESOLVER_ABI,\n functionName: \"setAddr\",\n args: [this.node(name), getAddress(address)],\n account,\n chain: robinhoodChain,\n });\n }\n\n /** Set a text record (deploys a per-owner resolver on first use). */\n async setText(name: string, key: string, value: string): Promise<Hex> {\n const { wallet, account } = this.signer();\n const resolver = await this.ensureResolver(name);\n return wallet.writeContract({\n address: resolver,\n abi: RESOLVER_ABI,\n functionName: \"setText\",\n args: [this.node(name), key, value],\n account,\n chain: robinhoodChain,\n });\n }\n\n /** Return the name's resolver, deploying + linking a per-owner one (addr+text roles) if needed. */\n async ensureResolver(name: string): Promise<Address> {\n const { wallet, account } = this.signer();\n const label = toLabel(name);\n const existing = await this.resolverOf(label);\n if (existing) return existing;\n\n const initData = encodeFunctionData({\n abi: RESOLVER_ABI,\n functionName: \"initialize\",\n args: [this.accountAddress(), RESOLVER_RECORD_ROLES, []],\n });\n const salt = BigInt(Math.floor(Math.random() * 2 ** 48));\n const sim = await this.publicClient.simulateContract({\n address: this.contracts.verifiableFactory,\n abi: FACTORY_ABI,\n functionName: \"deployProxy\",\n args: [this.contracts.resolverImpl, salt, initData],\n account: this.accountAddress(),\n });\n const resolver = sim.result as Address;\n\n const deployTx = await wallet.writeContract({\n address: this.contracts.verifiableFactory,\n abi: FACTORY_ABI,\n functionName: \"deployProxy\",\n args: [this.contracts.resolverImpl, salt, initData],\n account,\n chain: robinhoodChain,\n });\n await this.publicClient.waitForTransactionReceipt({ hash: deployTx });\n\n const tokenId = (await this.publicClient.readContract({\n address: this.contracts.registry,\n abi: REGISTRY_ABI,\n functionName: \"getTokenId\",\n args: [this.labelId(label)],\n })) as bigint;\n const setResTx = await wallet.writeContract({\n address: this.contracts.registry,\n abi: REGISTRY_ABI,\n functionName: \"setResolver\",\n args: [tokenId, resolver],\n account,\n chain: robinhoodChain,\n });\n await this.publicClient.waitForTransactionReceipt({ hash: setResTx });\n return resolver;\n }\n}\n"]}
|