bulletin-deploy 0.7.3 → 0.7.4
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 +26 -0
- package/bin/bulletin-deploy +94 -1
- package/dist/bug-report.js +4 -3
- package/dist/{chunk-MDYSENTW.js → chunk-CQ753LDA.js} +1 -1
- package/dist/{chunk-BYIVK52G.js → chunk-DHQ3JGF4.js} +67 -75
- package/dist/{chunk-EPNPPAMS.js → chunk-SVKCVNXD.js} +68 -41
- package/dist/chunk-UJP2PZGU.js +155 -0
- package/dist/chunk-YREZFNCC.js +869 -0
- package/dist/{chunk-4EQERQRG.js → chunk-YYULF2JX.js} +2 -2
- package/dist/deploy.d.ts +4 -2
- package/dist/deploy.js +8 -5
- package/dist/dotns.d.ts +12 -40
- package/dist/dotns.js +9 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +24 -6
- package/dist/memory-report.js +2 -1
- package/dist/run-state.d.ts +22 -0
- package/dist/run-state.js +21 -0
- package/dist/telemetry.d.ts +5 -1
- package/dist/telemetry.js +8 -1
- package/dist/version-check.js +3 -2
- package/package.json +4 -3
- package/dist/chunk-M3H3F4FY.js +0 -1048
package/dist/chunk-M3H3F4FY.js
DELETED
|
@@ -1,1048 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
captureWarning,
|
|
3
|
-
withSpan
|
|
4
|
-
} from "./chunk-BYIVK52G.js";
|
|
5
|
-
import {
|
|
6
|
-
isTestnetSpecName
|
|
7
|
-
} from "./chunk-JHNW2EKY.js";
|
|
8
|
-
|
|
9
|
-
// src/dotns.ts
|
|
10
|
-
import crypto from "crypto";
|
|
11
|
-
import { createClient } from "polkadot-api";
|
|
12
|
-
import { getPolkadotSigner } from "polkadot-api/signer";
|
|
13
|
-
import { getWsProvider } from "polkadot-api/ws-provider";
|
|
14
|
-
import { Keyring } from "@polkadot/keyring";
|
|
15
|
-
import { cryptoWaitReady } from "@polkadot/util-crypto";
|
|
16
|
-
import { Binary } from "polkadot-api";
|
|
17
|
-
import {
|
|
18
|
-
encodeFunctionData,
|
|
19
|
-
decodeFunctionResult,
|
|
20
|
-
keccak256,
|
|
21
|
-
toBytes,
|
|
22
|
-
formatEther,
|
|
23
|
-
isAddress,
|
|
24
|
-
bytesToHex,
|
|
25
|
-
isHex,
|
|
26
|
-
toHex,
|
|
27
|
-
zeroAddress,
|
|
28
|
-
namehash,
|
|
29
|
-
concatHex
|
|
30
|
-
} from "viem";
|
|
31
|
-
import { CID } from "multiformats/cid";
|
|
32
|
-
var RPC_ENDPOINTS = [
|
|
33
|
-
"wss://asset-hub-paseo.dotters.network",
|
|
34
|
-
"wss://sys.ibp.network/asset-hub-paseo",
|
|
35
|
-
"wss://pas-rpc.stakeworld.io/assethub"
|
|
36
|
-
];
|
|
37
|
-
var CONTRACTS = {
|
|
38
|
-
DOTNS_REGISTRAR: "0x329aAA5b6bEa94E750b2dacBa74Bf41291E6c2BD",
|
|
39
|
-
DOTNS_REGISTRAR_CONTROLLER: "0xd09e0F1c1E6CE8Cf40df929ef4FC778629573651",
|
|
40
|
-
DOTNS_REGISTRY: "0x4Da0d37aBe96C06ab19963F31ca2DC0412057a6f",
|
|
41
|
-
DOTNS_RESOLVER: "0x95645C7fD0fF38790647FE13F87Eb11c1DCc8514",
|
|
42
|
-
DOTNS_CONTENT_RESOLVER: "0x7756DF72CBc7f062e7403cD59e45fBc78bed1cD7",
|
|
43
|
-
DOTNS_REVERSE_RESOLVER: "0x95D57363B491CF743970c640fe419541386ac8BF",
|
|
44
|
-
STORE_FACTORY: "0x030296782F4d3046B080BcB017f01837561D9702",
|
|
45
|
-
POP_RULES: "0x4e8920B1E69d0cEA9b23CBFC87A17Ee6fE02d2d3"
|
|
46
|
-
};
|
|
47
|
-
var DECIMALS = 12n;
|
|
48
|
-
var NATIVE_TO_ETH_RATIO = 1000000n;
|
|
49
|
-
var CONNECTION_TIMEOUT_MS = 3e4;
|
|
50
|
-
var OPERATION_TIMEOUT_MS = 3e5;
|
|
51
|
-
var TX_TIMEOUT_MS = 9e4;
|
|
52
|
-
var TX_CHAIN_TIME_BUDGET_MS = 18e4;
|
|
53
|
-
var TX_WALL_CLOCK_CEILING_MS = 10 * 60 * 1e3;
|
|
54
|
-
var WS_HEARTBEAT_TIMEOUT_MS = 3e5;
|
|
55
|
-
var DOTNS_TX_MAX_ATTEMPTS = 3;
|
|
56
|
-
function classifyTxRetryDecision(err) {
|
|
57
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
58
|
-
const lower = msg.toLowerCase();
|
|
59
|
-
if (/\bstale\b/.test(lower)) return "retry";
|
|
60
|
-
if (/"type"\s*:\s*"future"|\binvalid::future\b/.test(lower)) return "retry";
|
|
61
|
-
if (lower.includes("websocket") || lower.includes("connection") || lower.includes("socket closed") || lower.includes("disconnect")) return "retry";
|
|
62
|
-
if (lower.includes("timed out") || lower.includes("timeout")) return "retry";
|
|
63
|
-
return "abort";
|
|
64
|
-
}
|
|
65
|
-
var DEFAULT_MNEMONIC = "bottom drive obey lake curtain smoke basket hold race lonely fit walk";
|
|
66
|
-
var _rpcIdCounter = 0;
|
|
67
|
-
async function fetchNonce(rpc, ss58Address) {
|
|
68
|
-
const WS = globalThis.WebSocket ?? (await import("./wrapper-IFSKR7DG.js")).default;
|
|
69
|
-
return new Promise((resolve, reject) => {
|
|
70
|
-
let done = false;
|
|
71
|
-
const settle = (fn, ...args) => {
|
|
72
|
-
if (done) return;
|
|
73
|
-
done = true;
|
|
74
|
-
clearTimeout(timer);
|
|
75
|
-
try {
|
|
76
|
-
ws.close();
|
|
77
|
-
} catch {
|
|
78
|
-
}
|
|
79
|
-
fn(...args);
|
|
80
|
-
};
|
|
81
|
-
const timer = setTimeout(() => settle(reject, new Error(`fetchNonce timed out after 8s for ${rpc}`)), 8e3);
|
|
82
|
-
const ws = new WS(rpc);
|
|
83
|
-
const id = ++_rpcIdCounter;
|
|
84
|
-
ws.onopen = () => ws.send(JSON.stringify({ jsonrpc: "2.0", id, method: "system_accountNextIndex", params: [ss58Address] }));
|
|
85
|
-
ws.onmessage = (e) => {
|
|
86
|
-
const d = typeof e.data === "string" ? e.data : e.data.toString();
|
|
87
|
-
const r = JSON.parse(d);
|
|
88
|
-
if (r.id === id) {
|
|
89
|
-
r.error ? settle(reject, new Error(r.error.message)) : settle(resolve, r.result);
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
ws.onerror = () => settle(reject, new Error(`WebSocket to ${rpc} failed`));
|
|
93
|
-
ws.onclose = () => settle(reject, new Error(`WebSocket to ${rpc} closed before response`));
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
var ProofOfPersonhoodStatus = {
|
|
97
|
-
NoStatus: 0,
|
|
98
|
-
ProofOfPersonhoodLite: 1,
|
|
99
|
-
ProofOfPersonhoodFull: 2,
|
|
100
|
-
Reserved: 3
|
|
101
|
-
};
|
|
102
|
-
var DOTNS_REGISTRAR_CONTROLLER_ABI = [
|
|
103
|
-
{ inputs: [{ name: "registration", type: "tuple", components: [{ name: "label", type: "string" }, { name: "owner", type: "address" }, { name: "secret", type: "bytes32" }, { name: "reserved", type: "bool" }] }], name: "makeCommitment", outputs: [{ name: "", type: "bytes32" }], stateMutability: "view", type: "function" },
|
|
104
|
-
{ inputs: [{ name: "commitment", type: "bytes32" }], name: "commit", outputs: [], stateMutability: "nonpayable", type: "function" },
|
|
105
|
-
{ inputs: [], name: "minCommitmentAge", outputs: [{ name: "", type: "uint256" }], stateMutability: "view", type: "function" },
|
|
106
|
-
{ inputs: [{ name: "commitment", type: "bytes32" }], name: "commitments", outputs: [{ name: "", type: "uint256" }], stateMutability: "view", type: "function" },
|
|
107
|
-
{ inputs: [{ name: "registration", type: "tuple", components: [{ name: "label", type: "string" }, { name: "owner", type: "address" }, { name: "secret", type: "bytes32" }, { name: "reserved", type: "bool" }] }], name: "register", outputs: [], stateMutability: "payable", type: "function" }
|
|
108
|
-
];
|
|
109
|
-
var DOTNS_REGISTRAR_ABI = [
|
|
110
|
-
{ inputs: [{ name: "tokenId", type: "uint256" }], name: "ownerOf", outputs: [{ name: "", type: "address" }], stateMutability: "view", type: "function" }
|
|
111
|
-
];
|
|
112
|
-
var POP_RULES_ABI = [
|
|
113
|
-
{ inputs: [{ name: "name", type: "string" }], name: "classifyName", outputs: [{ name: "requirement", type: "uint8" }, { name: "message", type: "string" }], stateMutability: "pure", type: "function" },
|
|
114
|
-
{ inputs: [{ name: "name", type: "string" }], name: "price", outputs: [{ name: "", type: "uint256" }], stateMutability: "view", type: "function" },
|
|
115
|
-
{ inputs: [{ name: "", type: "address" }], name: "userPopStatus", outputs: [{ name: "", type: "uint8" }], stateMutability: "view", type: "function" },
|
|
116
|
-
{ inputs: [{ name: "status", type: "uint8" }], name: "setUserPopStatus", outputs: [], stateMutability: "nonpayable", type: "function" },
|
|
117
|
-
{ inputs: [{ name: "baseName", type: "string" }], name: "isBaseNameReserved", outputs: [{ name: "isReserved", type: "bool" }, { name: "reservationOwner", type: "address" }, { name: "expiryTimestamp", type: "uint64" }], stateMutability: "view", type: "function" }
|
|
118
|
-
];
|
|
119
|
-
var DOTNS_REGISTRY_ABI = [
|
|
120
|
-
{ inputs: [{ name: "record", type: "tuple", components: [{ name: "parentNode", type: "bytes32" }, { name: "subLabel", type: "string" }, { name: "parentLabel", type: "string" }, { name: "owner", type: "address" }] }], name: "setSubnodeOwner", outputs: [{ name: "subnode", type: "bytes32" }], stateMutability: "nonpayable", type: "function" },
|
|
121
|
-
{ inputs: [{ name: "node", type: "bytes32" }, { name: "newResolver", type: "address" }], name: "setResolver", outputs: [], stateMutability: "nonpayable", type: "function" },
|
|
122
|
-
{ inputs: [{ name: "node", type: "bytes32" }], name: "owner", outputs: [{ name: "", type: "address" }], stateMutability: "view", type: "function" }
|
|
123
|
-
];
|
|
124
|
-
var DOTNS_CONTENT_RESOLVER_ABI = [
|
|
125
|
-
{ inputs: [{ name: "node", type: "bytes32" }, { name: "hash", type: "bytes" }], name: "setContenthash", outputs: [], stateMutability: "nonpayable", type: "function" },
|
|
126
|
-
{ inputs: [{ name: "node", type: "bytes32" }], name: "contenthash", outputs: [{ name: "", type: "bytes" }], stateMutability: "view", type: "function" }
|
|
127
|
-
];
|
|
128
|
-
function convertToHexString(value) {
|
|
129
|
-
if (!value) return "0x";
|
|
130
|
-
if (typeof value?.asHex === "function") return value.asHex();
|
|
131
|
-
if (typeof value?.toHex === "function") return value.toHex();
|
|
132
|
-
if (typeof value === "string" && isHex(value)) return value;
|
|
133
|
-
if (value instanceof Uint8Array) return bytesToHex(value);
|
|
134
|
-
try {
|
|
135
|
-
return toHex(value);
|
|
136
|
-
} catch {
|
|
137
|
-
return "0x";
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
function convertToBigInt(value, fallback = 0n) {
|
|
141
|
-
try {
|
|
142
|
-
if (typeof value === "bigint") return value;
|
|
143
|
-
if (typeof value === "number") return BigInt(value);
|
|
144
|
-
if (typeof value === "string") return BigInt(value);
|
|
145
|
-
if (value && typeof value.toString === "function") return BigInt(value.toString());
|
|
146
|
-
return fallback;
|
|
147
|
-
} catch {
|
|
148
|
-
return fallback;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
function normalizeWeight(weight) {
|
|
152
|
-
const referenceTime = weight?.ref_time ?? weight?.refTime ?? 0;
|
|
153
|
-
const proofSize = weight?.proof_size ?? weight?.proofSize ?? 0;
|
|
154
|
-
return { referenceTime: convertToBigInt(referenceTime, 0n), proofSize: convertToBigInt(proofSize, 0n) };
|
|
155
|
-
}
|
|
156
|
-
function extractStorageDepositCharge(rawStorageDeposit) {
|
|
157
|
-
if (!rawStorageDeposit) return 0n;
|
|
158
|
-
if (typeof rawStorageDeposit?.isCharge === "boolean") {
|
|
159
|
-
if (rawStorageDeposit.isCharge && rawStorageDeposit.asCharge != null) return convertToBigInt(rawStorageDeposit.asCharge, 0n);
|
|
160
|
-
return 0n;
|
|
161
|
-
}
|
|
162
|
-
if (rawStorageDeposit.charge != null) return convertToBigInt(rawStorageDeposit.charge, 0n);
|
|
163
|
-
if (rawStorageDeposit.Charge != null) return convertToBigInt(rawStorageDeposit.Charge, 0n);
|
|
164
|
-
if (rawStorageDeposit.value != null) return convertToBigInt(rawStorageDeposit.value, 0n);
|
|
165
|
-
return 0n;
|
|
166
|
-
}
|
|
167
|
-
function unwrapExecutionResult(rawResult) {
|
|
168
|
-
if (!rawResult) return { ok: null, err: null, successFlag: null };
|
|
169
|
-
if (typeof rawResult.success === "boolean") {
|
|
170
|
-
return rawResult.success ? { ok: rawResult.value ?? null, err: null, successFlag: true } : { ok: null, err: rawResult.error ?? rawResult.value ?? null, successFlag: false };
|
|
171
|
-
}
|
|
172
|
-
if (typeof rawResult.isOk === "boolean") {
|
|
173
|
-
return rawResult.isOk ? { ok: rawResult.value ?? null, err: null, successFlag: true } : { ok: null, err: rawResult.value ?? null, successFlag: false };
|
|
174
|
-
}
|
|
175
|
-
if (rawResult.ok != null) return { ok: rawResult.ok, err: null, successFlag: true };
|
|
176
|
-
if (rawResult.err != null) return { ok: null, err: rawResult.err, successFlag: false };
|
|
177
|
-
return { ok: null, err: rawResult, successFlag: null };
|
|
178
|
-
}
|
|
179
|
-
function withTimeout(promise, timeoutMs, operationName) {
|
|
180
|
-
let timer;
|
|
181
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
182
|
-
timer = setTimeout(() => reject(new Error(`${operationName} timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
183
|
-
});
|
|
184
|
-
return Promise.race([promise, timeoutPromise]).finally(() => clearTimeout(timer));
|
|
185
|
-
}
|
|
186
|
-
var DOT_NODE = "0x3fce7d1364a893e213bc4212792b517ffc88f5b13b86c8ef9c8d390c3a1370ce";
|
|
187
|
-
function convertWeiToNative(weiValue) {
|
|
188
|
-
return weiValue / NATIVE_TO_ETH_RATIO;
|
|
189
|
-
}
|
|
190
|
-
function computeDomainTokenId(label) {
|
|
191
|
-
const labelhash = keccak256(toBytes(label));
|
|
192
|
-
const node = keccak256(concatHex([DOT_NODE, labelhash]));
|
|
193
|
-
return BigInt(node);
|
|
194
|
-
}
|
|
195
|
-
function countTrailingDigits(label) {
|
|
196
|
-
let count = 0;
|
|
197
|
-
for (let i = label.length - 1; i >= 0; i--) {
|
|
198
|
-
const code = label.charCodeAt(i);
|
|
199
|
-
if (code >= 48 && code <= 57) count++;
|
|
200
|
-
else break;
|
|
201
|
-
}
|
|
202
|
-
return count;
|
|
203
|
-
}
|
|
204
|
-
function stripTrailingDigits(label) {
|
|
205
|
-
return label.replace(/\d+$/, "").replace(/-$/, "");
|
|
206
|
-
}
|
|
207
|
-
function sanitizeDomainLabel(label) {
|
|
208
|
-
const trailingDigitCount = countTrailingDigits(label);
|
|
209
|
-
if (trailingDigitCount > 2) {
|
|
210
|
-
const sanitized = stripTrailingDigits(label) + label.slice(-2);
|
|
211
|
-
console.log(` Domain label sanitized: "${label}" \u2192 "${sanitized}" (stripped excess trailing digits)`);
|
|
212
|
-
return sanitized;
|
|
213
|
-
}
|
|
214
|
-
return label;
|
|
215
|
-
}
|
|
216
|
-
function validateDomainLabel(label) {
|
|
217
|
-
if (!/^[a-z0-9-]{3,}$/.test(label)) throw new Error("Invalid domain label: must contain only lowercase letters, digits, and hyphens, min 3 chars");
|
|
218
|
-
if (label.startsWith("-") || label.endsWith("-")) throw new Error("Invalid domain label: cannot start or end with hyphen");
|
|
219
|
-
return sanitizeDomainLabel(label);
|
|
220
|
-
}
|
|
221
|
-
function isCommitmentMature(chainNowSeconds, commitTimestampSeconds, minimumAgeSeconds) {
|
|
222
|
-
return chainNowSeconds > commitTimestampSeconds + minimumAgeSeconds;
|
|
223
|
-
}
|
|
224
|
-
function classifyDotnsLabel(label) {
|
|
225
|
-
const totalLength = label.length;
|
|
226
|
-
const trailingDigits = countTrailingDigits(label);
|
|
227
|
-
if (trailingDigits > 2) {
|
|
228
|
-
return {
|
|
229
|
-
status: ProofOfPersonhoodStatus.Reserved,
|
|
230
|
-
message: `Name has ${trailingDigits} trailing digits; DotNS allows at most 2 trailing digits. Use a base name with 0-2 trailing digits.`
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
const baselength = totalLength - trailingDigits;
|
|
234
|
-
if (baselength <= 5) {
|
|
235
|
-
return {
|
|
236
|
-
status: ProofOfPersonhoodStatus.Reserved,
|
|
237
|
-
message: `Base name is ${baselength} char${baselength === 1 ? "" : "s"}; DotNS reserves base names of 5 chars or fewer for governance (PopRules). Use a base name of 6+ chars \u2014 role prefixes like 'rc<N>pool' / 'rc<N>dir' / 'nightly-<role>' work well.`
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
if (baselength >= 6 && baselength <= 8) {
|
|
241
|
-
if (trailingDigits === 2) return { status: ProofOfPersonhoodStatus.ProofOfPersonhoodLite, message: "Requires Light personhood verification" };
|
|
242
|
-
return { status: ProofOfPersonhoodStatus.ProofOfPersonhoodFull, message: "Requires Full personhood verification" };
|
|
243
|
-
}
|
|
244
|
-
if (trailingDigits === 2) return { status: ProofOfPersonhoodStatus.NoStatus, message: "Available to all" };
|
|
245
|
-
return { status: ProofOfPersonhoodStatus.ProofOfPersonhoodFull, message: "Requires Full personhood verification" };
|
|
246
|
-
}
|
|
247
|
-
function canRegister(requiredStatus, userStatus, trailingDigits) {
|
|
248
|
-
if (requiredStatus === ProofOfPersonhoodStatus.Reserved) return false;
|
|
249
|
-
if (requiredStatus === ProofOfPersonhoodStatus.ProofOfPersonhoodFull) {
|
|
250
|
-
return userStatus === ProofOfPersonhoodStatus.ProofOfPersonhoodFull;
|
|
251
|
-
}
|
|
252
|
-
if (requiredStatus === ProofOfPersonhoodStatus.ProofOfPersonhoodLite) {
|
|
253
|
-
return userStatus === ProofOfPersonhoodStatus.ProofOfPersonhoodLite || userStatus === ProofOfPersonhoodStatus.ProofOfPersonhoodFull;
|
|
254
|
-
}
|
|
255
|
-
return trailingDigits !== 0 && userStatus !== ProofOfPersonhoodStatus.ProofOfPersonhoodLite;
|
|
256
|
-
}
|
|
257
|
-
function simulateUserStatus(currentStatus, requiredStatus, options) {
|
|
258
|
-
const max = (a, b) => a > b ? a : b;
|
|
259
|
-
if (options.explicitStatus !== void 0) {
|
|
260
|
-
return max(currentStatus, options.explicitStatus);
|
|
261
|
-
}
|
|
262
|
-
if (requiredStatus === ProofOfPersonhoodStatus.NoStatus && currentStatus === ProofOfPersonhoodStatus.ProofOfPersonhoodLite && options.isTestnet) {
|
|
263
|
-
return ProofOfPersonhoodStatus.ProofOfPersonhoodFull;
|
|
264
|
-
}
|
|
265
|
-
if (requiredStatus !== ProofOfPersonhoodStatus.NoStatus) {
|
|
266
|
-
return max(currentStatus, requiredStatus);
|
|
267
|
-
}
|
|
268
|
-
return currentStatus;
|
|
269
|
-
}
|
|
270
|
-
function parseDomainName(input) {
|
|
271
|
-
const name = input.replace(/\.dot$/, "");
|
|
272
|
-
const parts = name.split(".");
|
|
273
|
-
if (parts.length === 1) {
|
|
274
|
-
validateDomainLabel(parts[0]);
|
|
275
|
-
return { isSubdomain: false, label: parts[0], sublabel: null, parentLabel: null, fullName: `${parts[0]}.dot` };
|
|
276
|
-
}
|
|
277
|
-
if (parts.length === 2) {
|
|
278
|
-
validateDomainLabel(parts[0]);
|
|
279
|
-
validateDomainLabel(parts[1]);
|
|
280
|
-
return { isSubdomain: true, label: name, sublabel: parts[0], parentLabel: parts[1], fullName: `${name}.dot` };
|
|
281
|
-
}
|
|
282
|
-
throw new Error(`Invalid domain: only one level of subdomains supported (got ${parts.length} labels)`);
|
|
283
|
-
}
|
|
284
|
-
function parseProofOfPersonhoodStatus(status) {
|
|
285
|
-
const s = (status ?? "none").toLowerCase();
|
|
286
|
-
if (s === "none" || s === "nostatus") return ProofOfPersonhoodStatus.NoStatus;
|
|
287
|
-
if (s === "lite" || s === "poplite") return ProofOfPersonhoodStatus.ProofOfPersonhoodLite;
|
|
288
|
-
if (s === "full" || s === "popfull") return ProofOfPersonhoodStatus.ProofOfPersonhoodFull;
|
|
289
|
-
throw new Error("Invalid status. Use none, lite, or full");
|
|
290
|
-
}
|
|
291
|
-
function popStatusName(status) {
|
|
292
|
-
return Object.keys(ProofOfPersonhoodStatus).find((k) => ProofOfPersonhoodStatus[k] === status) ?? String(status);
|
|
293
|
-
}
|
|
294
|
-
var ReviveClientWrapper = class _ReviveClientWrapper {
|
|
295
|
-
static DRY_RUN_STORAGE_LIMIT = 18446744073709551615n;
|
|
296
|
-
static DRY_RUN_WEIGHT_LIMIT = { ref_time: 18446744073709551615n, proof_size: 18446744073709551615n };
|
|
297
|
-
client;
|
|
298
|
-
mappedAccounts;
|
|
299
|
-
constructor(client) {
|
|
300
|
-
this.client = client;
|
|
301
|
-
this.mappedAccounts = /* @__PURE__ */ new Set();
|
|
302
|
-
}
|
|
303
|
-
async getEvmAddress(substrateAddress) {
|
|
304
|
-
if (isAddress(substrateAddress)) return substrateAddress;
|
|
305
|
-
const address = await this.client.apis.ReviveApi.address(substrateAddress);
|
|
306
|
-
return address.asHex();
|
|
307
|
-
}
|
|
308
|
-
async performDryRunCall(originSubstrateAddress, contractAddress, value, encodedData) {
|
|
309
|
-
if (isAddress(originSubstrateAddress)) throw new Error("performDryRunCall requires SS58 Substrate address, not EVM H160 address");
|
|
310
|
-
const executionResults = await this.client.apis.ReviveApi.call(originSubstrateAddress, Binary.fromHex(contractAddress), value, _ReviveClientWrapper.DRY_RUN_WEIGHT_LIMIT, _ReviveClientWrapper.DRY_RUN_STORAGE_LIMIT, Binary.fromHex(encodedData));
|
|
311
|
-
const { ok, err, successFlag } = unwrapExecutionResult(executionResults.result);
|
|
312
|
-
const flags = ok?.flags ? convertToBigInt(ok.flags, 0n) : 0n;
|
|
313
|
-
const returnData = convertToHexString(ok?.data);
|
|
314
|
-
const didRevert = ok ? (flags & 1n) === 1n : true;
|
|
315
|
-
const gasConsumed = normalizeWeight(executionResults.weight_consumed);
|
|
316
|
-
const gasRequired = normalizeWeight(executionResults.weight_required ?? executionResults.weight_consumed);
|
|
317
|
-
const storageDepositValue = extractStorageDepositCharge(executionResults.storage_deposit);
|
|
318
|
-
const isOk = !!ok && !didRevert;
|
|
319
|
-
const isErr = !ok || didRevert || !!err || (typeof successFlag === "boolean" ? !successFlag : false);
|
|
320
|
-
return { gasConsumed, gasRequired, storageDeposit: { value: storageDepositValue }, result: { isOk, isErr, value: { data: ok ? returnData : "0x", flags: ok ? flags : 1n } } };
|
|
321
|
-
}
|
|
322
|
-
async estimateGasForCall(originSubstrateAddress, contractAddress, value, encodedData) {
|
|
323
|
-
const result = await this.performDryRunCall(originSubstrateAddress, contractAddress, value, encodedData);
|
|
324
|
-
if (!result.result.isOk) return { success: false, gasConsumed: result.gasConsumed, storageDeposit: result.storageDeposit.value, gasRequired: result.gasRequired, revertData: result.result.value.data, revertFlags: result.result.value.flags };
|
|
325
|
-
return { success: true, gasConsumed: result.gasConsumed, storageDeposit: result.storageDeposit.value, gasRequired: result.gasRequired };
|
|
326
|
-
}
|
|
327
|
-
async checkIfAccountMapped(substrateAddress) {
|
|
328
|
-
try {
|
|
329
|
-
const evmAddress = await this.getEvmAddress(substrateAddress);
|
|
330
|
-
const key = Binary.fromHex(evmAddress);
|
|
331
|
-
const mappedAccount = await this.client.query.Revive.OriginalAccount.getValue(key);
|
|
332
|
-
return mappedAccount !== null && mappedAccount !== void 0;
|
|
333
|
-
} catch {
|
|
334
|
-
return false;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
async ensureAccountMapped(substrateAddress, signer) {
|
|
338
|
-
if (isAddress(substrateAddress)) throw new Error("ensureAccountMapped requires SS58 Substrate address, not EVM H160 address");
|
|
339
|
-
if (this.mappedAccounts.has(substrateAddress)) return;
|
|
340
|
-
const isMapped = await this.checkIfAccountMapped(substrateAddress);
|
|
341
|
-
if (isMapped) {
|
|
342
|
-
this.mappedAccounts.add(substrateAddress);
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
try {
|
|
346
|
-
await this.signAndSubmitWithRetry(() => this.client.tx.Revive.map_account(), signer, () => {
|
|
347
|
-
}, "Revive.map_account");
|
|
348
|
-
this.mappedAccounts.add(substrateAddress);
|
|
349
|
-
} catch (error) {
|
|
350
|
-
const errorMessage = error?.message || String(error);
|
|
351
|
-
if (errorMessage.includes("AccountAlreadyMapped")) {
|
|
352
|
-
this.mappedAccounts.add(substrateAddress);
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
throw error;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
// `nonceFallback` is accepted for call-site compatibility but no longer
|
|
359
|
-
// used: a nonce-increment shortcut can't distinguish our tx from a
|
|
360
|
-
// concurrent/zombie tx on the same account (Alice's default mnemonic is
|
|
361
|
-
// widely shared on testnets), producing false-positive resolution. The
|
|
362
|
-
// chain-time deadline below is the reliability knob instead.
|
|
363
|
-
signAndSubmitExtrinsic(extrinsic, signer, statusCallback, _opts = {}) {
|
|
364
|
-
return new Promise((resolve, reject) => {
|
|
365
|
-
let settled = false;
|
|
366
|
-
let deadlinePoller = null;
|
|
367
|
-
let sub;
|
|
368
|
-
const finish = (fn) => (...args) => {
|
|
369
|
-
if (!settled) {
|
|
370
|
-
settled = true;
|
|
371
|
-
if (deadlinePoller) clearInterval(deadlinePoller);
|
|
372
|
-
try {
|
|
373
|
-
sub?.unsubscribe();
|
|
374
|
-
} catch {
|
|
375
|
-
}
|
|
376
|
-
fn(...args);
|
|
377
|
-
}
|
|
378
|
-
};
|
|
379
|
-
const startWallClockMs = Date.now();
|
|
380
|
-
let startChainTimeMs = null;
|
|
381
|
-
deadlinePoller = setInterval(async () => {
|
|
382
|
-
if (settled) return;
|
|
383
|
-
try {
|
|
384
|
-
if (Date.now() - startWallClockMs > TX_WALL_CLOCK_CEILING_MS) {
|
|
385
|
-
statusCallback("failed");
|
|
386
|
-
finish(reject)(new Error(`Transaction did not settle within ${TX_WALL_CLOCK_CEILING_MS / 1e3}s wall-clock (chain may be stalled)`));
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
const chainNowMs = Number(await this.client.query.Timestamp.Now.getValue());
|
|
390
|
-
if (startChainTimeMs === null) startChainTimeMs = chainNowMs;
|
|
391
|
-
const chainElapsedMs = chainNowMs - startChainTimeMs;
|
|
392
|
-
if (chainElapsedMs > TX_CHAIN_TIME_BUDGET_MS) {
|
|
393
|
-
statusCallback("failed");
|
|
394
|
-
finish(reject)(new Error(`Transaction not included after ${Math.floor(chainElapsedMs / 1e3)}s of chain progress (budget=${TX_CHAIN_TIME_BUDGET_MS / 1e3}s)`));
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
} catch {
|
|
398
|
-
}
|
|
399
|
-
}, 6e3);
|
|
400
|
-
try {
|
|
401
|
-
sub = extrinsic.signSubmitAndWatch(signer, { mortality: { mortal: true, period: 256 } }).subscribe({
|
|
402
|
-
next: (event) => {
|
|
403
|
-
const transactionHash = event.txHash?.toString();
|
|
404
|
-
switch (event.type) {
|
|
405
|
-
case "signed":
|
|
406
|
-
statusCallback("signing");
|
|
407
|
-
break;
|
|
408
|
-
case "broadcasted":
|
|
409
|
-
statusCallback("broadcasting");
|
|
410
|
-
break;
|
|
411
|
-
case "txBestBlocksState":
|
|
412
|
-
if (event.found) statusCallback("included");
|
|
413
|
-
break;
|
|
414
|
-
case "finalized":
|
|
415
|
-
if (event.dispatchError) {
|
|
416
|
-
statusCallback("failed");
|
|
417
|
-
finish(reject)(new Error(`Transaction failed: ${event.dispatchError.toString()}`));
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
statusCallback("finalized");
|
|
421
|
-
finish(resolve)(transactionHash);
|
|
422
|
-
return;
|
|
423
|
-
case "invalid":
|
|
424
|
-
case "dropped":
|
|
425
|
-
statusCallback("failed");
|
|
426
|
-
finish(reject)(new Error(`Transaction ${event.type}`));
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
},
|
|
430
|
-
error: (error) => {
|
|
431
|
-
statusCallback("failed");
|
|
432
|
-
finish(reject)(error);
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
|
-
} catch (error) {
|
|
436
|
-
statusCallback("failed");
|
|
437
|
-
finish(reject)(error);
|
|
438
|
-
}
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
// Wrapper around signAndSubmitExtrinsic that retries on transient errors
|
|
442
|
-
// (Stale, Future, connection, timeout — see classifyTxRetryDecision). The
|
|
443
|
-
// extrinsic is rebuilt per attempt via `buildExtrinsic` so polkadot-api
|
|
444
|
-
// assigns a fresh nonce + mortality window — same pattern as
|
|
445
|
-
// pool.ts:submitAliceTxWithRetry. `label` is the human-readable op name
|
|
446
|
-
// used in retry log lines.
|
|
447
|
-
async signAndSubmitWithRetry(buildExtrinsic, signer, statusCallback, label, opts = {}) {
|
|
448
|
-
let lastError;
|
|
449
|
-
for (let attempt = 1; attempt <= DOTNS_TX_MAX_ATTEMPTS; attempt++) {
|
|
450
|
-
try {
|
|
451
|
-
return await this.signAndSubmitExtrinsic(buildExtrinsic(), signer, statusCallback, opts);
|
|
452
|
-
} catch (e) {
|
|
453
|
-
lastError = e;
|
|
454
|
-
const decision = classifyTxRetryDecision(e);
|
|
455
|
-
if (decision === "abort" || attempt === DOTNS_TX_MAX_ATTEMPTS) break;
|
|
456
|
-
const short = (e?.message ?? String(e)).slice(0, 80);
|
|
457
|
-
console.log(` ${label}: attempt ${attempt}/${DOTNS_TX_MAX_ATTEMPTS} failed (${short}), retrying...`);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
461
|
-
}
|
|
462
|
-
async submitTransaction(contractAddress, value, encodedData, signerSubstrateAddress, signer, statusCallback, { rpc, useNoncePolling } = {}) {
|
|
463
|
-
await this.ensureAccountMapped(signerSubstrateAddress, signer);
|
|
464
|
-
const gasEstimate = await this.estimateGasForCall(signerSubstrateAddress, contractAddress, value, encodedData);
|
|
465
|
-
if (!gasEstimate.success) throw new Error(`Contract execution would revert: ${gasEstimate.revertData ?? "0x"}`);
|
|
466
|
-
const weightLimit = { proof_size: gasEstimate.gasRequired.proofSize, ref_time: gasEstimate.gasRequired.referenceTime };
|
|
467
|
-
const minimumStorageDeposit = 2000000000000n;
|
|
468
|
-
let storageDepositLimit = gasEstimate.storageDeposit === 0n ? minimumStorageDeposit : gasEstimate.storageDeposit * 120n / 100n;
|
|
469
|
-
if (storageDepositLimit < minimumStorageDeposit) storageDepositLimit = minimumStorageDeposit;
|
|
470
|
-
const buildExtrinsic = () => this.client.tx.Revive.call({ dest: Binary.fromHex(contractAddress), value, weight_limit: weightLimit, storage_deposit_limit: storageDepositLimit, data: Binary.fromHex(encodedData) });
|
|
471
|
-
let nonceFallback;
|
|
472
|
-
if (useNoncePolling && rpc) {
|
|
473
|
-
try {
|
|
474
|
-
const nonce = await fetchNonce(rpc, signerSubstrateAddress);
|
|
475
|
-
nonceFallback = { rpc, senderSS58: signerSubstrateAddress, expectedNonce: nonce };
|
|
476
|
-
} catch {
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
return await this.signAndSubmitWithRetry(buildExtrinsic, signer, statusCallback, "Revive.call", { nonceFallback });
|
|
480
|
-
}
|
|
481
|
-
};
|
|
482
|
-
var DotNS = class {
|
|
483
|
-
client;
|
|
484
|
-
clientWrapper;
|
|
485
|
-
rpc;
|
|
486
|
-
substrateAddress;
|
|
487
|
-
evmAddress;
|
|
488
|
-
signer;
|
|
489
|
-
connected;
|
|
490
|
-
constructor() {
|
|
491
|
-
this.client = null;
|
|
492
|
-
this.clientWrapper = null;
|
|
493
|
-
this.rpc = null;
|
|
494
|
-
this.substrateAddress = null;
|
|
495
|
-
this.evmAddress = null;
|
|
496
|
-
this.signer = null;
|
|
497
|
-
this.connected = false;
|
|
498
|
-
}
|
|
499
|
-
async connect(options = {}) {
|
|
500
|
-
const endpoints = options.rpc ? [options.rpc] : process.env.DOTNS_RPC ? [process.env.DOTNS_RPC] : RPC_ENDPOINTS;
|
|
501
|
-
let lastError;
|
|
502
|
-
for (const rpc of endpoints) {
|
|
503
|
-
try {
|
|
504
|
-
console.log(` Connecting to: ${rpc}`);
|
|
505
|
-
this.rpc = rpc;
|
|
506
|
-
this.client = createClient(getWsProvider(rpc, { heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS }));
|
|
507
|
-
const unsafeApi = this.client.getUnsafeApi();
|
|
508
|
-
this.clientWrapper = new ReviveClientWrapper(unsafeApi);
|
|
509
|
-
if (options.signer && options.signerAddress) {
|
|
510
|
-
this.signer = options.signer;
|
|
511
|
-
this.substrateAddress = options.signerAddress;
|
|
512
|
-
} else {
|
|
513
|
-
const source = options.keyUri || options.mnemonic || process.env.DOTNS_KEY_URI || process.env.DOTNS_MNEMONIC || process.env.MNEMONIC || DEFAULT_MNEMONIC;
|
|
514
|
-
const isKeyUri = Boolean(options.keyUri || process.env.DOTNS_KEY_URI);
|
|
515
|
-
await cryptoWaitReady();
|
|
516
|
-
const keyring = new Keyring({ type: "sr25519" });
|
|
517
|
-
const uri = options.derivationPath && !isKeyUri ? `${source}${options.derivationPath}` : source;
|
|
518
|
-
const account = isKeyUri || options.derivationPath ? keyring.addFromUri(uri) : keyring.addFromMnemonic(source);
|
|
519
|
-
this.substrateAddress = account.address;
|
|
520
|
-
this.signer = getPolkadotSigner(account.publicKey, "Sr25519", async (input) => account.sign(input));
|
|
521
|
-
}
|
|
522
|
-
this.evmAddress = await withTimeout(
|
|
523
|
-
this.clientWrapper.getEvmAddress(this.substrateAddress),
|
|
524
|
-
CONNECTION_TIMEOUT_MS,
|
|
525
|
-
`Connect to ${rpc}`
|
|
526
|
-
);
|
|
527
|
-
console.log(` SS58 Address: ${this.substrateAddress}`);
|
|
528
|
-
console.log(` H160 Address: ${this.evmAddress}`);
|
|
529
|
-
await this.clientWrapper.ensureAccountMapped(this.substrateAddress, this.signer);
|
|
530
|
-
this.connected = true;
|
|
531
|
-
return this;
|
|
532
|
-
} catch (e) {
|
|
533
|
-
lastError = e;
|
|
534
|
-
captureWarning("DotNS RPC endpoint failed, trying next", { endpoint: rpc, error: e.message?.slice(0, 200), remainingEndpoints: endpoints.length - endpoints.indexOf(rpc) - 1 });
|
|
535
|
-
console.log(` Failed to connect to ${rpc}: ${e.message}`);
|
|
536
|
-
if (this.client) {
|
|
537
|
-
try {
|
|
538
|
-
this.client.destroy();
|
|
539
|
-
} catch {
|
|
540
|
-
}
|
|
541
|
-
this.client = null;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
throw new Error(`All RPC endpoints failed. Last error: ${lastError?.message}`);
|
|
546
|
-
}
|
|
547
|
-
ensureConnected() {
|
|
548
|
-
if (!this.connected) throw new Error("Not connected. Call connect() first.");
|
|
549
|
-
}
|
|
550
|
-
// Returns true when the DotNS chain (Asset Hub) reports a testnet spec_name.
|
|
551
|
-
// Used to gate test-only behaviors like self-granting Full PoP on a Lite
|
|
552
|
-
// signer for a NoStatus label. Mainnet must never self-grant — Personhood
|
|
553
|
-
// is attested, not self-declared, there.
|
|
554
|
-
_testnetCache = null;
|
|
555
|
-
async isTestnet() {
|
|
556
|
-
if (this._testnetCache !== null) return this._testnetCache;
|
|
557
|
-
this.ensureConnected();
|
|
558
|
-
try {
|
|
559
|
-
const version = await this.clientWrapper.client.constants.System.Version();
|
|
560
|
-
const raw = version?.spec_name ?? version?.specName;
|
|
561
|
-
const specName = typeof raw === "string" ? raw : raw?.asText?.() ?? String(raw ?? "");
|
|
562
|
-
this._testnetCache = isTestnetSpecName(specName);
|
|
563
|
-
} catch {
|
|
564
|
-
this._testnetCache = false;
|
|
565
|
-
}
|
|
566
|
-
return this._testnetCache;
|
|
567
|
-
}
|
|
568
|
-
async contractCall(contractAddress, contractAbi, functionName, args = []) {
|
|
569
|
-
this.ensureConnected();
|
|
570
|
-
const encodedCallData = encodeFunctionData({ abi: contractAbi, functionName, args });
|
|
571
|
-
const callResult = await this.clientWrapper.performDryRunCall(this.substrateAddress, contractAddress, 0n, encodedCallData);
|
|
572
|
-
if (!callResult.result.isOk) {
|
|
573
|
-
const errorData = callResult.result.value;
|
|
574
|
-
const flags = errorData?.flags ?? 0n;
|
|
575
|
-
const revertData = errorData?.data ?? "0x";
|
|
576
|
-
const isRevert = (flags & 1n) === 1n;
|
|
577
|
-
if (isRevert) throw new Error(`Contract reverted (flags=${flags}) with data: ${revertData}`);
|
|
578
|
-
throw new Error(`Contract call failed (flags=${flags}) with data: ${revertData}`);
|
|
579
|
-
}
|
|
580
|
-
return decodeFunctionResult({ abi: contractAbi, functionName, data: callResult.result.value.data });
|
|
581
|
-
}
|
|
582
|
-
async contractTransaction(contractAddress, value, contractAbi, functionName, args = [], statusCallback = () => {
|
|
583
|
-
}, { useNoncePolling } = {}) {
|
|
584
|
-
this.ensureConnected();
|
|
585
|
-
const encodedCallData = encodeFunctionData({ abi: contractAbi, functionName, args });
|
|
586
|
-
return await withTimeout(this.clientWrapper.submitTransaction(contractAddress, value, encodedCallData, this.substrateAddress, this.signer, statusCallback, { rpc: this.rpc, useNoncePolling }), OPERATION_TIMEOUT_MS, functionName);
|
|
587
|
-
}
|
|
588
|
-
async checkOwnership(label, ownerAddress = null) {
|
|
589
|
-
this.ensureConnected();
|
|
590
|
-
const checkAddress = ownerAddress || this.evmAddress;
|
|
591
|
-
const tokenId = computeDomainTokenId(label);
|
|
592
|
-
try {
|
|
593
|
-
const owner = await withTimeout(this.contractCall(CONTRACTS.DOTNS_REGISTRAR, DOTNS_REGISTRAR_ABI, "ownerOf", [tokenId]), 3e4, "ownerOf");
|
|
594
|
-
const owned = owner.toLowerCase() === checkAddress.toLowerCase();
|
|
595
|
-
return { owned, owner };
|
|
596
|
-
} catch {
|
|
597
|
-
return { owned: false, owner: null };
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
async classifyName(label) {
|
|
601
|
-
this.ensureConnected();
|
|
602
|
-
console.log(`
|
|
603
|
-
Classifying name via PopOracle...`);
|
|
604
|
-
const result = await withTimeout(this.contractCall(CONTRACTS.POP_RULES, POP_RULES_ABI, "classifyName", [label]), 3e4, "classifyName");
|
|
605
|
-
const requiredStatus = typeof result[0] === "bigint" ? Number(result[0]) : result[0];
|
|
606
|
-
const message = result[1];
|
|
607
|
-
console.log(` Required status: ${popStatusName(requiredStatus)}`);
|
|
608
|
-
console.log(` Message: ${message}`);
|
|
609
|
-
return { requiredStatus, message };
|
|
610
|
-
}
|
|
611
|
-
async getUserPopStatus(ownerAddress = null) {
|
|
612
|
-
this.ensureConnected();
|
|
613
|
-
const checkAddress = ownerAddress || this.evmAddress;
|
|
614
|
-
const result = await withTimeout(this.contractCall(CONTRACTS.POP_RULES, POP_RULES_ABI, "userPopStatus", [checkAddress]), 3e4, "userPopStatus");
|
|
615
|
-
return typeof result === "bigint" ? Number(result) : result;
|
|
616
|
-
}
|
|
617
|
-
async setUserPopStatus(status) {
|
|
618
|
-
this.ensureConnected();
|
|
619
|
-
console.log(`
|
|
620
|
-
Checking current PoP status...`);
|
|
621
|
-
const currentStatus = await this.getUserPopStatus();
|
|
622
|
-
const currentStatusName = popStatusName(currentStatus);
|
|
623
|
-
const desiredStatusName = popStatusName(status);
|
|
624
|
-
console.log(` Current: ${currentStatusName}`);
|
|
625
|
-
console.log(` Desired: ${desiredStatusName}`);
|
|
626
|
-
if (currentStatus === status) {
|
|
627
|
-
console.log(` Status already set, skipping update`);
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
if (currentStatus > status) {
|
|
631
|
-
console.log(` Current status already satisfies desired, skipping downgrade`);
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
console.log(` Setting PoP status to ${desiredStatusName}...`);
|
|
635
|
-
const txHash = await this.contractTransaction(CONTRACTS.POP_RULES, 0n, POP_RULES_ABI, "setUserPopStatus", [status], (s) => console.log(` ${s}`));
|
|
636
|
-
console.log(` Tx: ${txHash}`);
|
|
637
|
-
}
|
|
638
|
-
async ensureNotRegistered(label) {
|
|
639
|
-
this.ensureConnected();
|
|
640
|
-
console.log(`
|
|
641
|
-
Checking availability of ${label}.dot...`);
|
|
642
|
-
const tokenId = computeDomainTokenId(label);
|
|
643
|
-
try {
|
|
644
|
-
const owner = await withTimeout(this.contractCall(CONTRACTS.DOTNS_REGISTRAR, DOTNS_REGISTRAR_ABI, "ownerOf", [tokenId]), 3e4, "Availability check");
|
|
645
|
-
if (owner !== zeroAddress) throw new Error(`Domain ${label}.dot already owned by ${owner}`);
|
|
646
|
-
} catch (error) {
|
|
647
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
648
|
-
if (errorMessage.includes("already owned")) throw error;
|
|
649
|
-
}
|
|
650
|
-
console.log(` ${label}.dot is available`);
|
|
651
|
-
}
|
|
652
|
-
async generateCommitment(label, includeReverse = false) {
|
|
653
|
-
this.ensureConnected();
|
|
654
|
-
console.log(`
|
|
655
|
-
Generating commitment hash...`);
|
|
656
|
-
label = validateDomainLabel(label);
|
|
657
|
-
const secret = `0x${crypto.randomBytes(32).toString("hex")}`;
|
|
658
|
-
const registration = { label, owner: this.evmAddress, secret, reserved: includeReverse };
|
|
659
|
-
const commitment = await withTimeout(this.contractCall(CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, DOTNS_REGISTRAR_CONTROLLER_ABI, "makeCommitment", [registration]), 3e4, "Commitment generation");
|
|
660
|
-
console.log(` Commitment: ${commitment}`);
|
|
661
|
-
return { commitment, registration };
|
|
662
|
-
}
|
|
663
|
-
async submitCommitment(commitment) {
|
|
664
|
-
this.ensureConnected();
|
|
665
|
-
console.log(`
|
|
666
|
-
Submitting commitment...`);
|
|
667
|
-
const txHash = await this.contractTransaction(CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, 0n, DOTNS_REGISTRAR_CONTROLLER_ABI, "commit", [commitment], (s) => console.log(` ${s}`));
|
|
668
|
-
console.log(` Tx: ${txHash}`);
|
|
669
|
-
console.log(` Committed at: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
670
|
-
}
|
|
671
|
-
async waitForCommitmentAge(commitment) {
|
|
672
|
-
this.ensureConnected();
|
|
673
|
-
const POLL_TIMEOUT_MS = 9e4;
|
|
674
|
-
const POLL_INTERVAL_MS = 3e3;
|
|
675
|
-
console.log(`
|
|
676
|
-
Reading minimum commitment age...`);
|
|
677
|
-
const [minimumAge, initialCommitTimestamp] = await Promise.all([
|
|
678
|
-
withTimeout(this.contractCall(CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, DOTNS_REGISTRAR_CONTROLLER_ABI, "minCommitmentAge", []), 3e4, "minCommitmentAge"),
|
|
679
|
-
withTimeout(this.contractCall(CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, DOTNS_REGISTRAR_CONTROLLER_ABI, "commitments", [commitment]), 3e4, "commitments")
|
|
680
|
-
]);
|
|
681
|
-
const minimumAgeSeconds = typeof minimumAge === "bigint" ? Number(minimumAge) : minimumAge;
|
|
682
|
-
const commitTimestamp = typeof initialCommitTimestamp === "bigint" ? Number(initialCommitTimestamp) : initialCommitTimestamp;
|
|
683
|
-
if (commitTimestamp === 0) {
|
|
684
|
-
throw new Error("Commitment not found on-chain. It may not have been included in a block yet.");
|
|
685
|
-
}
|
|
686
|
-
console.log(` Minimum commitment age: ${minimumAgeSeconds}s`);
|
|
687
|
-
console.log(` Commitment stored on-chain (timestamp: ${commitTimestamp})`);
|
|
688
|
-
console.log(` Waiting for on-chain block.timestamp > ${commitTimestamp + minimumAgeSeconds}...`);
|
|
689
|
-
const pollDeadline = Date.now() + POLL_TIMEOUT_MS;
|
|
690
|
-
while (Date.now() < pollDeadline) {
|
|
691
|
-
const nowMs = await this.clientWrapper.client.query.Timestamp.Now.getValue();
|
|
692
|
-
const chainNowSeconds = Math.floor(Number(nowMs) / 1e3);
|
|
693
|
-
if (isCommitmentMature(chainNowSeconds, commitTimestamp, minimumAgeSeconds)) {
|
|
694
|
-
console.log(` Commitment age requirement met (chain.now=${chainNowSeconds}, target>${commitTimestamp + minimumAgeSeconds})`);
|
|
695
|
-
return;
|
|
696
|
-
}
|
|
697
|
-
const remaining = Math.ceil((pollDeadline - Date.now()) / 1e3);
|
|
698
|
-
console.log(` Chain time ${chainNowSeconds}, waiting for > ${commitTimestamp + minimumAgeSeconds} (${remaining}s left)...`);
|
|
699
|
-
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
700
|
-
}
|
|
701
|
-
throw new Error(`Commitment still too new after ${POLL_TIMEOUT_MS / 1e3}s of polling chain time. The chain may be stalled.`);
|
|
702
|
-
}
|
|
703
|
-
async getPriceAndValidate(label) {
|
|
704
|
-
this.ensureConnected();
|
|
705
|
-
console.log(`
|
|
706
|
-
Checking price and eligibility...`);
|
|
707
|
-
label = validateDomainLabel(label);
|
|
708
|
-
const baseName = stripTrailingDigits(label);
|
|
709
|
-
const reservationInfo = await withTimeout(this.contractCall(CONTRACTS.POP_RULES, POP_RULES_ABI, "isBaseNameReserved", [baseName]), 3e4, "isBaseNameReserved");
|
|
710
|
-
const [isReserved, reservationOwner] = reservationInfo;
|
|
711
|
-
if (isReserved && reservationOwner.toLowerCase() !== this.evmAddress.toLowerCase()) throw new Error("Base name reserved for original Lite registrant");
|
|
712
|
-
const classificationResult = await withTimeout(this.contractCall(CONTRACTS.POP_RULES, POP_RULES_ABI, "classifyName", [label]), 3e4, "classifyName");
|
|
713
|
-
const requiredStatus = typeof classificationResult[0] === "bigint" ? Number(classificationResult[0]) : classificationResult[0];
|
|
714
|
-
const message = classificationResult[1];
|
|
715
|
-
const userStatus = await this.getUserPopStatus();
|
|
716
|
-
if (requiredStatus === ProofOfPersonhoodStatus.Reserved) throw new Error(message);
|
|
717
|
-
if (requiredStatus === ProofOfPersonhoodStatus.ProofOfPersonhoodFull) {
|
|
718
|
-
if (userStatus !== ProofOfPersonhoodStatus.ProofOfPersonhoodFull) throw new Error("Requires Full Personhood verification");
|
|
719
|
-
} else if (requiredStatus === ProofOfPersonhoodStatus.ProofOfPersonhoodLite) {
|
|
720
|
-
if (userStatus !== ProofOfPersonhoodStatus.ProofOfPersonhoodLite && userStatus !== ProofOfPersonhoodStatus.ProofOfPersonhoodFull) throw new Error("Requires Personhood Lite verification");
|
|
721
|
-
} else {
|
|
722
|
-
const trailingDigitCount = countTrailingDigits(label);
|
|
723
|
-
if (trailingDigitCount === 0 || userStatus === ProofOfPersonhoodStatus.ProofOfPersonhoodLite) {
|
|
724
|
-
throw new Error("Personhood Lite cannot register base names \u2014 this name class requires a Full or NoStatus signer");
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
const priceRaw = await withTimeout(this.contractCall(CONTRACTS.POP_RULES, POP_RULES_ABI, "price", [label]), 3e4, "price");
|
|
728
|
-
const priceWei = typeof priceRaw === "bigint" ? priceRaw : BigInt(priceRaw);
|
|
729
|
-
const requiredStatusName = popStatusName(requiredStatus);
|
|
730
|
-
const userStatusName = popStatusName(userStatus);
|
|
731
|
-
console.log(` Required status: ${requiredStatusName}`);
|
|
732
|
-
console.log(` User status: ${userStatusName}`);
|
|
733
|
-
console.log(` Price: ${formatEther(priceWei)} PAS`);
|
|
734
|
-
return { priceWei, requiredStatus, userStatus, message };
|
|
735
|
-
}
|
|
736
|
-
async finalizeRegistration(registration, priceWei) {
|
|
737
|
-
this.ensureConnected();
|
|
738
|
-
console.log(`
|
|
739
|
-
Finalizing registration for ${registration.label}.dot...`);
|
|
740
|
-
const bufferedPaymentWei = priceWei * 110n / 100n;
|
|
741
|
-
const bufferedPaymentNative = convertWeiToNative(bufferedPaymentWei);
|
|
742
|
-
console.log(` Oracle price: ${formatEther(priceWei)} PAS`);
|
|
743
|
-
console.log(` Paying: ${formatEther(bufferedPaymentWei)} PAS`);
|
|
744
|
-
const txHash = await this.contractTransaction(CONTRACTS.DOTNS_REGISTRAR_CONTROLLER, bufferedPaymentNative, DOTNS_REGISTRAR_CONTROLLER_ABI, "register", [registration], (s) => console.log(` ${s}`));
|
|
745
|
-
console.log(` Tx: ${txHash}`);
|
|
746
|
-
}
|
|
747
|
-
async verifyOwnership(label) {
|
|
748
|
-
this.ensureConnected();
|
|
749
|
-
console.log(`
|
|
750
|
-
Verifying ownership...`);
|
|
751
|
-
const tokenId = computeDomainTokenId(label);
|
|
752
|
-
const actualOwner = await withTimeout(this.contractCall(CONTRACTS.DOTNS_REGISTRAR, DOTNS_REGISTRAR_ABI, "ownerOf", [tokenId]), 3e4, "ownerOf");
|
|
753
|
-
if (actualOwner.toLowerCase() !== this.evmAddress.toLowerCase()) {
|
|
754
|
-
console.log(` Expected: ${this.evmAddress}`);
|
|
755
|
-
console.log(` Actual: ${actualOwner}`);
|
|
756
|
-
throw new Error(`Owner mismatch for ${label}.dot`);
|
|
757
|
-
}
|
|
758
|
-
console.log(` Owner: ${actualOwner}`);
|
|
759
|
-
}
|
|
760
|
-
async getContenthash(domainName) {
|
|
761
|
-
this.ensureConnected();
|
|
762
|
-
const node = namehash(`${domainName}.dot`);
|
|
763
|
-
const result = await withTimeout(
|
|
764
|
-
this.contractCall(CONTRACTS.DOTNS_CONTENT_RESOLVER, DOTNS_CONTENT_RESOLVER_ABI, "contenthash", [node]),
|
|
765
|
-
3e4,
|
|
766
|
-
"contenthash"
|
|
767
|
-
);
|
|
768
|
-
return typeof result === "string" ? result : result?.toString?.() ?? String(result);
|
|
769
|
-
}
|
|
770
|
-
async checkSubdomainOwnership(sublabel, parentLabel) {
|
|
771
|
-
this.ensureConnected();
|
|
772
|
-
const node = namehash(`${sublabel}.${parentLabel}.dot`);
|
|
773
|
-
try {
|
|
774
|
-
const owner = await withTimeout(this.contractCall(CONTRACTS.DOTNS_REGISTRY, DOTNS_REGISTRY_ABI, "owner", [node]), 3e4, "owner");
|
|
775
|
-
if (!owner || owner === zeroAddress) return { owned: false, owner: null };
|
|
776
|
-
const owned = owner.toLowerCase() === this.evmAddress.toLowerCase();
|
|
777
|
-
return { owned, owner };
|
|
778
|
-
} catch {
|
|
779
|
-
return { owned: false, owner: null };
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
async registerSubdomain(sublabel, parentLabel) {
|
|
783
|
-
return withSpan("deploy.dotns.register-subdomain", `2a. register ${sublabel}.${parentLabel}.dot`, {}, async () => {
|
|
784
|
-
this.ensureConnected();
|
|
785
|
-
console.log(`
|
|
786
|
-
Registering subdomain ${sublabel}.${parentLabel}.dot...`);
|
|
787
|
-
const parentNode = namehash(`${parentLabel}.dot`);
|
|
788
|
-
const subnodeRecord = { parentNode, subLabel: sublabel, parentLabel, owner: this.evmAddress };
|
|
789
|
-
const txHash = await this.contractTransaction(CONTRACTS.DOTNS_REGISTRY, 0n, DOTNS_REGISTRY_ABI, "setSubnodeOwner", [subnodeRecord], (s) => console.log(` ${s}`), { useNoncePolling: true });
|
|
790
|
-
console.log(` Tx: ${txHash}`);
|
|
791
|
-
console.log(` Setting resolver...`);
|
|
792
|
-
const subnodeNode = namehash(`${sublabel}.${parentLabel}.dot`);
|
|
793
|
-
const resolverTxHash = await this.contractTransaction(CONTRACTS.DOTNS_REGISTRY, 0n, DOTNS_REGISTRY_ABI, "setResolver", [subnodeNode, CONTRACTS.DOTNS_CONTENT_RESOLVER], (s) => console.log(` ${s}`), { useNoncePolling: true });
|
|
794
|
-
console.log(` Tx: ${resolverTxHash}`);
|
|
795
|
-
console.log(` Subdomain registered!`);
|
|
796
|
-
return { sublabel, parentLabel, owner: this.evmAddress };
|
|
797
|
-
});
|
|
798
|
-
}
|
|
799
|
-
async setContenthash(domainName, contenthashHex) {
|
|
800
|
-
return withSpan("deploy.dotns.set-contenthash", "2b. set-contenthash", {}, async () => {
|
|
801
|
-
this.ensureConnected();
|
|
802
|
-
const node = namehash(`${domainName}.dot`);
|
|
803
|
-
let ipfsCid = null;
|
|
804
|
-
if (contenthashHex && contenthashHex !== "0x") {
|
|
805
|
-
const bytes = Buffer.from(contenthashHex.slice(2), "hex");
|
|
806
|
-
if (bytes[0] === 227 && bytes.length >= 4) {
|
|
807
|
-
const cidBytes = bytes.slice(2);
|
|
808
|
-
ipfsCid = CID.decode(cidBytes).toString();
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
console.log(` Setting contenthash: ${ipfsCid || contenthashHex}`);
|
|
812
|
-
const txHash = await this.contractTransaction(CONTRACTS.DOTNS_CONTENT_RESOLVER, 0n, DOTNS_CONTENT_RESOLVER_ABI, "setContenthash", [node, contenthashHex], (s) => console.log(` ${s}`), { useNoncePolling: true });
|
|
813
|
-
console.log(` Tx: ${txHash}`);
|
|
814
|
-
const onChainRaw = await this.getContenthash(domainName);
|
|
815
|
-
const onChain = (onChainRaw || "0x").toLowerCase();
|
|
816
|
-
const expected = contenthashHex.toLowerCase();
|
|
817
|
-
if (onChain !== expected) {
|
|
818
|
-
throw new Error(
|
|
819
|
-
`Post-deploy verification failed for ${domainName}.dot: on-chain contenthash is ${onChain}, not the ${expected} we just wrote. The setContenthash tx may have silently failed, or another party overwrote the domain. Re-run the deploy to retry.`
|
|
820
|
-
);
|
|
821
|
-
}
|
|
822
|
-
console.log(` Verified on-chain: ${ipfsCid || contenthashHex}
|
|
823
|
-
`);
|
|
824
|
-
return { node };
|
|
825
|
-
});
|
|
826
|
-
}
|
|
827
|
-
// View-only readiness check. Runs every chain read needed to predict whether
|
|
828
|
-
// `register(label)` will succeed, so the caller can fail-fast BEFORE the
|
|
829
|
-
// Bulletin chunk upload. Never writes to chain. See issue #100.
|
|
830
|
-
async preflight(label, explicitStatusOverride) {
|
|
831
|
-
return withSpan("deploy.dotns.preflight", `preflight ${label}.dot`, {}, async () => {
|
|
832
|
-
this.ensureConnected();
|
|
833
|
-
const validated = validateDomainLabel(label);
|
|
834
|
-
const trailingDigits = countTrailingDigits(validated);
|
|
835
|
-
const baselength = validated.length - trailingDigits;
|
|
836
|
-
const classification = classifyDotnsLabel(validated);
|
|
837
|
-
if (classification.status === ProofOfPersonhoodStatus.Reserved) {
|
|
838
|
-
const sanitizeTrail = label !== validated ? `Input "${label}" was sanitized to "${validated}" (excess trailing digits trimmed). ` : "";
|
|
839
|
-
return {
|
|
840
|
-
label: validated,
|
|
841
|
-
classification,
|
|
842
|
-
userStatus: 0,
|
|
843
|
-
trailingDigits,
|
|
844
|
-
baselength,
|
|
845
|
-
isAvailable: false,
|
|
846
|
-
existingOwner: null,
|
|
847
|
-
isBaseNameReserved: false,
|
|
848
|
-
reservationOwner: null,
|
|
849
|
-
isTestnet: false,
|
|
850
|
-
canProceed: false,
|
|
851
|
-
reason: `${sanitizeTrail}${classification.message}`,
|
|
852
|
-
plannedAction: "abort",
|
|
853
|
-
needsPopUpgrade: false
|
|
854
|
-
};
|
|
855
|
-
}
|
|
856
|
-
const baseName = stripTrailingDigits(validated);
|
|
857
|
-
const [userStatus, baseReservation, ownership, isTestnet] = await Promise.all([
|
|
858
|
-
this.getUserPopStatus(),
|
|
859
|
-
withTimeout(this.contractCall(CONTRACTS.POP_RULES, POP_RULES_ABI, "isBaseNameReserved", [baseName]), 3e4, "isBaseNameReserved"),
|
|
860
|
-
this.checkOwnership(validated),
|
|
861
|
-
this.isTestnet()
|
|
862
|
-
]);
|
|
863
|
-
const [isReserved, reservationOwnerRaw] = baseReservation;
|
|
864
|
-
const reservationOwner = isReserved ? reservationOwnerRaw.toLowerCase() : null;
|
|
865
|
-
const ownerRaw = ownership.owner?.toLowerCase() ?? null;
|
|
866
|
-
const existingOwner = ownerRaw && ownerRaw !== zeroAddress ? ownerRaw : null;
|
|
867
|
-
const selfAddress = this.evmAddress.toLowerCase();
|
|
868
|
-
if (existingOwner !== null && existingOwner !== selfAddress) {
|
|
869
|
-
return {
|
|
870
|
-
label: validated,
|
|
871
|
-
classification,
|
|
872
|
-
userStatus,
|
|
873
|
-
trailingDigits,
|
|
874
|
-
baselength,
|
|
875
|
-
isAvailable: false,
|
|
876
|
-
existingOwner,
|
|
877
|
-
isBaseNameReserved: isReserved,
|
|
878
|
-
reservationOwner,
|
|
879
|
-
isTestnet,
|
|
880
|
-
canProceed: false,
|
|
881
|
-
reason: `Domain ${validated}.dot is already owned by ${existingOwner}.`,
|
|
882
|
-
plannedAction: "abort",
|
|
883
|
-
needsPopUpgrade: false
|
|
884
|
-
};
|
|
885
|
-
}
|
|
886
|
-
if (existingOwner !== null && existingOwner === selfAddress) {
|
|
887
|
-
return {
|
|
888
|
-
label: validated,
|
|
889
|
-
classification,
|
|
890
|
-
userStatus,
|
|
891
|
-
trailingDigits,
|
|
892
|
-
baselength,
|
|
893
|
-
isAvailable: true,
|
|
894
|
-
existingOwner,
|
|
895
|
-
isBaseNameReserved: isReserved,
|
|
896
|
-
reservationOwner,
|
|
897
|
-
isTestnet,
|
|
898
|
-
canProceed: true,
|
|
899
|
-
plannedAction: "already-owned-by-us",
|
|
900
|
-
needsPopUpgrade: false
|
|
901
|
-
};
|
|
902
|
-
}
|
|
903
|
-
if (isReserved && reservationOwner !== selfAddress) {
|
|
904
|
-
return {
|
|
905
|
-
label: validated,
|
|
906
|
-
classification,
|
|
907
|
-
userStatus,
|
|
908
|
-
trailingDigits,
|
|
909
|
-
baselength,
|
|
910
|
-
isAvailable: true,
|
|
911
|
-
existingOwner: null,
|
|
912
|
-
isBaseNameReserved: true,
|
|
913
|
-
reservationOwner,
|
|
914
|
-
isTestnet,
|
|
915
|
-
canProceed: false,
|
|
916
|
-
reason: `Base name ${baseName} is reserved for ${reservationOwner}.`,
|
|
917
|
-
plannedAction: "abort",
|
|
918
|
-
needsPopUpgrade: false
|
|
919
|
-
};
|
|
920
|
-
}
|
|
921
|
-
const explicitStatus = explicitStatusOverride ?? process.env.DOTNS_STATUS;
|
|
922
|
-
const explicitStatusNum = explicitStatus ? parseProofOfPersonhoodStatus(explicitStatus) : void 0;
|
|
923
|
-
const targetPopStatus = simulateUserStatus(userStatus, classification.status, { explicitStatus: explicitStatusNum, isTestnet });
|
|
924
|
-
if (!canRegister(classification.status, targetPopStatus, trailingDigits)) {
|
|
925
|
-
const className = popStatusName(classification.status);
|
|
926
|
-
return {
|
|
927
|
-
label: validated,
|
|
928
|
-
classification,
|
|
929
|
-
userStatus,
|
|
930
|
-
trailingDigits,
|
|
931
|
-
baselength,
|
|
932
|
-
isAvailable: true,
|
|
933
|
-
existingOwner: null,
|
|
934
|
-
isBaseNameReserved: isReserved,
|
|
935
|
-
reservationOwner,
|
|
936
|
-
isTestnet,
|
|
937
|
-
canProceed: false,
|
|
938
|
-
reason: `${validated}.dot classifies as ${className}; this signer cannot register it (PopRules.priceWithCheck gate). Remediations: use a signer with Full PoP, or pick a base name of 6-8 chars + trailing 00 (which classifies as PopLite).`,
|
|
939
|
-
plannedAction: "abort",
|
|
940
|
-
needsPopUpgrade: false,
|
|
941
|
-
targetPopStatus
|
|
942
|
-
};
|
|
943
|
-
}
|
|
944
|
-
return {
|
|
945
|
-
label: validated,
|
|
946
|
-
classification,
|
|
947
|
-
userStatus,
|
|
948
|
-
trailingDigits,
|
|
949
|
-
baselength,
|
|
950
|
-
isAvailable: true,
|
|
951
|
-
existingOwner: null,
|
|
952
|
-
isBaseNameReserved: isReserved,
|
|
953
|
-
reservationOwner,
|
|
954
|
-
isTestnet,
|
|
955
|
-
canProceed: true,
|
|
956
|
-
plannedAction: "register",
|
|
957
|
-
needsPopUpgrade: targetPopStatus !== userStatus,
|
|
958
|
-
targetPopStatus
|
|
959
|
-
};
|
|
960
|
-
});
|
|
961
|
-
}
|
|
962
|
-
async register(label, options = {}) {
|
|
963
|
-
return withSpan("deploy.dotns.register", `2a. register ${label}.dot`, {}, async () => {
|
|
964
|
-
const explicitStatus = options.status || process.env.DOTNS_STATUS;
|
|
965
|
-
const reverse = options.reverse ?? (process.env.DOTNS_REVERSE ?? "false").toLowerCase() === "true";
|
|
966
|
-
if (!this.connected) await this.connect(options);
|
|
967
|
-
label = validateDomainLabel(label);
|
|
968
|
-
const [classification] = await Promise.all([
|
|
969
|
-
this.classifyName(label),
|
|
970
|
-
this.ensureNotRegistered(label)
|
|
971
|
-
]);
|
|
972
|
-
const requiredStatus = classification.requiredStatus;
|
|
973
|
-
if (requiredStatus === ProofOfPersonhoodStatus.Reserved) {
|
|
974
|
-
throw new Error(classification.message);
|
|
975
|
-
}
|
|
976
|
-
const initialUserStatus = await this.getUserPopStatus();
|
|
977
|
-
const isTestnet = await this.isTestnet();
|
|
978
|
-
const explicitStatusNum = explicitStatus ? parseProofOfPersonhoodStatus(explicitStatus) : void 0;
|
|
979
|
-
const targetUserStatus = simulateUserStatus(initialUserStatus, requiredStatus, { explicitStatus: explicitStatusNum, isTestnet });
|
|
980
|
-
if (!canRegister(requiredStatus, targetUserStatus, countTrailingDigits(label))) {
|
|
981
|
-
throw new Error(
|
|
982
|
-
`This label classifies as ${popStatusName(requiredStatus)}; the registrar will reject this signer (PopRules.priceWithCheck). Remediations: use a signer with Full PoP, or pick a shorter base name (6-8 chars + trailing 00) that classifies as PopLite.`
|
|
983
|
-
);
|
|
984
|
-
}
|
|
985
|
-
if (targetUserStatus !== initialUserStatus && targetUserStatus !== ProofOfPersonhoodStatus.NoStatus) {
|
|
986
|
-
await this.setUserPopStatus(targetUserStatus);
|
|
987
|
-
const userStatus = await this.getUserPopStatus();
|
|
988
|
-
if (userStatus !== targetUserStatus) {
|
|
989
|
-
throw new Error(
|
|
990
|
-
"setUserPopStatus did not land as expected (wanted " + targetUserStatus + ", got " + userStatus + "). Check RPC connectivity and POP_RULES contract."
|
|
991
|
-
);
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
const { commitment, registration } = await this.generateCommitment(label, reverse);
|
|
995
|
-
await withSpan("deploy.dotns.submit-commitment", "2a-i. submit-commitment", {}, () => this.submitCommitment(commitment));
|
|
996
|
-
await withSpan("deploy.dotns.wait-commitment-age", "2a-ii. wait-commitment-age", {}, () => this.waitForCommitmentAge(commitment));
|
|
997
|
-
const pricing = await withSpan("deploy.dotns.price-validation", "2a-iii. price-validation", {}, () => this.getPriceAndValidate(label));
|
|
998
|
-
await withSpan("deploy.dotns.finalize-registration", "2a-iv. finalize-registration", {}, () => this.finalizeRegistration(registration, pricing.priceWei));
|
|
999
|
-
await this.verifyOwnership(label);
|
|
1000
|
-
console.log(`
|
|
1001
|
-
Registration complete!`);
|
|
1002
|
-
return { label, owner: this.evmAddress };
|
|
1003
|
-
});
|
|
1004
|
-
}
|
|
1005
|
-
disconnect() {
|
|
1006
|
-
if (this.client) {
|
|
1007
|
-
this.client.destroy();
|
|
1008
|
-
this.client = null;
|
|
1009
|
-
this.clientWrapper = null;
|
|
1010
|
-
this.connected = false;
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
};
|
|
1014
|
-
var dotns = new DotNS();
|
|
1015
|
-
|
|
1016
|
-
export {
|
|
1017
|
-
RPC_ENDPOINTS,
|
|
1018
|
-
CONTRACTS,
|
|
1019
|
-
DECIMALS,
|
|
1020
|
-
NATIVE_TO_ETH_RATIO,
|
|
1021
|
-
CONNECTION_TIMEOUT_MS,
|
|
1022
|
-
OPERATION_TIMEOUT_MS,
|
|
1023
|
-
TX_TIMEOUT_MS,
|
|
1024
|
-
TX_CHAIN_TIME_BUDGET_MS,
|
|
1025
|
-
TX_WALL_CLOCK_CEILING_MS,
|
|
1026
|
-
WS_HEARTBEAT_TIMEOUT_MS,
|
|
1027
|
-
DOTNS_TX_MAX_ATTEMPTS,
|
|
1028
|
-
classifyTxRetryDecision,
|
|
1029
|
-
DEFAULT_MNEMONIC,
|
|
1030
|
-
fetchNonce,
|
|
1031
|
-
ProofOfPersonhoodStatus,
|
|
1032
|
-
DOT_NODE,
|
|
1033
|
-
convertWeiToNative,
|
|
1034
|
-
computeDomainTokenId,
|
|
1035
|
-
countTrailingDigits,
|
|
1036
|
-
stripTrailingDigits,
|
|
1037
|
-
sanitizeDomainLabel,
|
|
1038
|
-
validateDomainLabel,
|
|
1039
|
-
isCommitmentMature,
|
|
1040
|
-
classifyDotnsLabel,
|
|
1041
|
-
canRegister,
|
|
1042
|
-
simulateUserStatus,
|
|
1043
|
-
parseDomainName,
|
|
1044
|
-
parseProofOfPersonhoodStatus,
|
|
1045
|
-
popStatusName,
|
|
1046
|
-
DotNS,
|
|
1047
|
-
dotns
|
|
1048
|
-
};
|