bulletin-deploy 0.7.22 → 0.7.23-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -56,6 +56,13 @@
56
56
  "autoAccountMapping": true,
57
57
  "bulletinAuthorizeV2": true,
58
58
  "nativeToEthRatio": 100000000,
59
+ "popSelfServe": {
60
+ "sudoEnvLabel": "Next V2",
61
+ "faucetUrl": "https://faucet.polkadot.io/?parachain=1500",
62
+ "personhoodFaucetUrl": "https://sudo.personhood.dev/personhood-faucet",
63
+ "dotnsBootstrapUrl": "https://sudo.personhood.dev/dotns-bootstrap",
64
+ "stateAwareGuidance": true
65
+ },
59
66
  "contracts": {
60
67
  "DOTNS_PROTOCOL_REGISTRY": "0x5Caef84563fc980178e28417414aa65bA32f6B4e",
61
68
  "DOTNS_REGISTRAR": "0x885b8085bA92A31c4ef52076f77379E647ECC399",
@@ -9,10 +9,10 @@ import {
9
9
  offerBugReport,
10
10
  scrubSecrets,
11
11
  setDeployContext
12
- } from "./chunk-LMCKLAA5.js";
13
- import "./chunk-V7KIUORT.js";
14
- import "./chunk-PNZCJPI6.js";
15
- import "./chunk-VQPLU5FL.js";
12
+ } from "./chunk-2ZVROLRT.js";
13
+ import "./chunk-TMK6XJTR.js";
14
+ import "./chunk-VAK7N7PX.js";
15
+ import "./chunk-OCI5SFUR.js";
16
16
  export {
17
17
  buildCliFlagsSummary,
18
18
  buildLabels,
@@ -2,11 +2,11 @@ import {
2
2
  classifyErrorArea,
3
3
  isInteractive,
4
4
  promptYesNo
5
- } from "./chunk-V7KIUORT.js";
5
+ } from "./chunk-TMK6XJTR.js";
6
6
  import {
7
7
  VERSION,
8
8
  getCurrentSentryTraceId
9
- } from "./chunk-PNZCJPI6.js";
9
+ } from "./chunk-VAK7N7PX.js";
10
10
 
11
11
  // src/bug-report.ts
12
12
  import { execSync, execFileSync } from "child_process";
@@ -4,8 +4,10 @@ import {
4
4
  import {
5
5
  captureWarning,
6
6
  setDeployAttribute,
7
+ setDeploySentryTag,
8
+ truncateAddress,
7
9
  withSpan
8
- } from "./chunk-PNZCJPI6.js";
10
+ } from "./chunk-VAK7N7PX.js";
9
11
 
10
12
  // src/dotns.ts
11
13
  import crypto from "crypto";
@@ -35,11 +37,20 @@ var FEE_FLOOR_OWNED = ONE_PAS / 100n;
35
37
  var FEE_FLOOR_REGISTER = ONE_PAS / 10n;
36
38
  var TOP_UP_TARGET = ONE_PAS / 2n;
37
39
  var SOURCE_BUFFER = ONE_PAS;
40
+ var REPROVE_FEE_ESTIMATE = ONE_PAS / 100n;
41
+ var REPROVE_FEE_SAFETY_MARGIN_PCT = 110n;
38
42
  var TOP_UP_TRANSFER_TIMEOUT_MS = 6e4;
39
43
  var PASEO_FAUCET_URL = "https://faucet.polkadot.io";
40
44
  function fmtPas(plancks) {
41
45
  return (Number(plancks) / Number(ONE_PAS)).toFixed(4);
42
46
  }
47
+ function resolveNativeTokenSymbol(envId) {
48
+ if (!envId) return "PAS";
49
+ if (envId.includes("paseo")) return "PAS";
50
+ if (envId.includes("westend")) return "WND";
51
+ if (envId.includes("rococo")) return "ROC";
52
+ return "PAS";
53
+ }
43
54
  function feeFloorFor(plannedAction) {
44
55
  return plannedAction === "already-owned-by-us" ? FEE_FLOOR_OWNED : FEE_FLOOR_REGISTER;
45
56
  }
@@ -258,12 +269,12 @@ function formatContractDryRunFailure(gasEstimate, context) {
258
269
  const isBareRevert = (revertData === void 0 || revertData.trim() === "0x") && gasEstimate.revertFlags === 1n;
259
270
  if (isBareRevert && BARE_REVERT_DIAGNOSTIC_FUNCTIONS.has(functionName)) {
260
271
  lines.push(
261
- ` diagnostic: bare-revert (empty 0x). Most common causes:`,
262
- ` 1. Origin not actually mapped on-chain. Try BULLETIN_DEPLOY_DEBUG_DOTNS_TX=1 to log calldata, then run`,
263
- ` \`node tools/dotns-dry-run.mjs <label>\` against the env's Asset Hub RPC to reproduce in isolation.`,
264
- ` 2. Contract precondition (e.g. commitment too new). dotns-dry-run.mjs will surface the contract-side state.`,
265
- ` hint: prepend --fresh to dotns-dry-run.mjs to use a brand-new unmapped substrate origin \u2014`,
266
- ` if the revert ONLY reproduces with --fresh, it's the account-mapping bug.`
272
+ ` diagnostic: bare-revert (empty 0x). Account mapping was verified at connect time, so the cause is likely:`,
273
+ ` 1. PoP status changed between preflight and registration (race condition).`,
274
+ ` 2. Commitment timing: the revealed commitment is still too new or already expired.`,
275
+ ` 3. Label was registered by someone else between preflight and register.`,
276
+ ` To reproduce in isolation: \`node tools/dotns-dry-run.mjs <label>\``,
277
+ ` To rule out a mapping issue: add --fresh (a brand-new unmapped origin) \u2014 if --fresh reverts but the mapped one doesn't, it's a mapping bug.`
267
278
  );
268
279
  }
269
280
  return lines.join("\n");
@@ -636,21 +647,45 @@ var ReviveClientWrapper = class _ReviveClientWrapper {
636
647
  }
637
648
  };
638
649
  var DOTNS_CONTEXT_HEX_LOWER = "0x646f746e73000000000000000000000000000000000000000000000000000000";
639
- function formatPersonhoodRemediation(state, environmentId) {
640
- if (environmentId !== "paseo-next-v2") {
650
+ function formatPersonhoodRemediation(state, popSelfServe, environmentId) {
651
+ if (!popSelfServe?.stateAwareGuidance) {
641
652
  return "Self-attestation is no longer available. Contact the DotNS team for whitelisting / Personhood status help.";
642
653
  }
643
654
  switch (state.state) {
644
655
  case "not-bound":
645
- return "Your account has no DotNS alias binding on paseo-next-v2. Visit https://sudo.personhood.dev/dotns-bootstrap to complete the recognition and binding steps, or run `tools/personhood-bootstrap.mjs --mnemonic <your-mnemonic>` once Stage 2 of the bootstrap tooling lands.";
656
+ return `Your account has no DotNS alias binding on ${environmentId ?? "this environment"}. Visit ${popSelfServe.dotnsBootstrapUrl} to complete the recognition and binding steps, or run \`tools/personhood-bootstrap.mjs --mnemonic <your-mnemonic>\` once Stage 2 of the bootstrap tooling lands.`;
646
657
  case "bound-likely-stale":
647
- return "Your alias binding exists but may have a stale ring revision. Run `node tools/reprove-alias.mjs --mnemonic <your-mnemonic> --env paseo-next-v2` to refresh the proof, then retry the registration.";
658
+ return `Your alias binding exists but may have a stale ring revision. Run \`node tools/reprove-alias.mjs --mnemonic <your-mnemonic> --env ${environmentId ?? "this environment"}\` to refresh the proof, then retry the registration.`;
648
659
  case "wrong-context":
649
- return "Your alias binding exists but is for a different application context" + (state.storedContextHex ? ` (context: ${state.storedContextHex})` : "") + ". Re-bind under the 'dotns' context using the bootstrap flow: https://sudo.personhood.dev/dotns-bootstrap";
660
+ return "Your alias binding exists but is for a different application context" + (state.storedContextHex ? ` (context: ${state.storedContextHex})` : "") + ". Re-bind under the 'dotns' context using the bootstrap flow: " + popSelfServe.dotnsBootstrapUrl;
650
661
  case "bound-fresh":
651
662
  return "Self-attestation is no longer available. Contact the DotNS team for whitelisting / Personhood status help.";
652
663
  }
653
664
  }
665
+ function formatPopShortfallReason(opts) {
666
+ const { label, requiredName, currentName, isTestnet, environmentId, popSelfServe, aliasState, exampleNoStatusLabel: noStatusEx } = opts;
667
+ const leadIn = `${label}.dot requires ${requiredName}, but this signer is ${currentName}.`;
668
+ let testnetBlock = "";
669
+ if (isTestnet && popSelfServe != null) {
670
+ if (popSelfServe.stateAwareGuidance) {
671
+ const state = aliasState ?? { state: "not-bound" };
672
+ testnetBlock = "\n\n" + formatPersonhoodRemediation(state, popSelfServe, environmentId);
673
+ } else {
674
+ testnetBlock = `
675
+
676
+ On testnets you can self-serve:
677
+ 1. Fund the service account mnemonic via ${popSelfServe.faucetUrl}
678
+ 2. Go to ${popSelfServe.personhoodFaucetUrl}, pick your env (e.g. ${popSelfServe.sudoEnvLabel}), and paste the mnemonic
679
+ 3. Go to ${popSelfServe.dotnsBootstrapUrl} and follow each step (first and last can probably be skipped)`;
680
+ }
681
+ }
682
+ const alternativesBlock = `
683
+
684
+ Alternatively:
685
+ - Use a NoStatus-compatible label (base length >= 9 with exactly two trailing digits, e.g. ${noStatusEx})
686
+ - Raise a whitelist issue at https://github.com/paritytech/dotns/`;
687
+ return leadIn + testnetBlock + alternativesBlock;
688
+ }
654
689
  var DotNS = class {
655
690
  client;
656
691
  clientWrapper;
@@ -664,9 +699,18 @@ var DotNS = class {
664
699
  // back to the legacy paseo-only RPC_ENDPOINTS for direct library callers.
665
700
  assetHubEndpoints;
666
701
  _usesExternalSigner = false;
702
+ _localMnemonic = null;
667
703
  _contracts = CONTRACTS;
668
704
  _nativeToEthRatio = NATIVE_TO_ETH_RATIO;
669
705
  _environmentId = null;
706
+ _popSelfServe = null;
707
+ // Test-only seam: consumed once by classifyAliasAccountState() then cleared.
708
+ // Mirrors the __setDeployRootSpanForTest / __setSentryForTest pattern.
709
+ _classifyOverrideForTest = null;
710
+ /** Test-only: inject a fixed classifyAliasAccountState return value for the next call. Consumed once. */
711
+ __setClassifyOverrideForTest(state) {
712
+ this._classifyOverrideForTest = { state, revision: 0 };
713
+ }
670
714
  constructor() {
671
715
  this.client = null;
672
716
  this.clientWrapper = null;
@@ -687,6 +731,9 @@ var DotNS = class {
687
731
  if (options.environmentId) {
688
732
  this._environmentId = options.environmentId;
689
733
  }
734
+ if (options.popSelfServe !== void 0) {
735
+ this._popSelfServe = options.popSelfServe ?? null;
736
+ }
690
737
  const rpc = options.rpc || process.env.DOTNS_RPC || this.assetHubEndpoints[0];
691
738
  this.rpc = rpc;
692
739
  this._usesExternalSigner = Boolean(options.signer && options.signerAddress);
@@ -698,6 +745,9 @@ var DotNS = class {
698
745
  const keyUriArg = options.keyUri || process.env.DOTNS_KEY_URI;
699
746
  let source = keyUriArg || mnemonicArg || DEFAULT_MNEMONIC;
700
747
  const isKeyUri = Boolean(keyUriArg);
748
+ if (!isKeyUri && !options.derivationPath) {
749
+ this._localMnemonic = mnemonicArg || DEFAULT_MNEMONIC;
750
+ }
701
751
  if (options.derivationPath && !isKeyUri && source) {
702
752
  source = `${source}${options.derivationPath}`;
703
753
  }
@@ -708,28 +758,34 @@ var DotNS = class {
708
758
  this.substrateAddress = account.address;
709
759
  }
710
760
  console.log(` SS58 Address: ${this.substrateAddress}`);
711
- try {
712
- this.client = createClient(getWsProvider(rpc, { heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS }));
713
- const unsafeApi = this.client.getUnsafeApi();
714
- this.clientWrapper = new ReviveClientWrapper(unsafeApi);
715
- this.evmAddress = await withTimeout(
716
- this.clientWrapper.getEvmAddress(this.substrateAddress),
717
- CONNECTION_TIMEOUT_MS,
718
- "ReviveApi.address"
719
- );
720
- console.log(` H160 Address: ${this.evmAddress}`);
721
- } catch (e) {
722
- throw new Error(`DotNS connect: failed to resolve EVM address from ${this.substrateAddress} via ReviveApi.address (${e.message?.slice(0, 200)})`);
723
- }
724
- this.connected = true;
725
- if (options.nativeToEthRatio) this._nativeToEthRatio = options.nativeToEthRatio;
726
- try {
727
- await this.ensureMappedAccountReady(options.autoAccountMapping ?? false);
728
- } catch (e) {
729
- this.connected = false;
730
- throw e;
731
- }
732
- return this;
761
+ setDeployAttribute("deploy.dotns.signer", truncateAddress(this.substrateAddress));
762
+ setDeploySentryTag("deploy.dotns.signer", truncateAddress(this.substrateAddress));
763
+ return withSpan("deploy.dotns.connect", "dotns connect", {}, async () => {
764
+ try {
765
+ this.client = createClient(getWsProvider(rpc, { heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS }));
766
+ const unsafeApi = this.client.getUnsafeApi();
767
+ this.clientWrapper = new ReviveClientWrapper(unsafeApi);
768
+ this.evmAddress = await withTimeout(
769
+ this.clientWrapper.getEvmAddress(this.substrateAddress),
770
+ CONNECTION_TIMEOUT_MS,
771
+ "ReviveApi.address"
772
+ );
773
+ console.log(` H160 Address: ${this.evmAddress}`);
774
+ } catch (e) {
775
+ throw new Error(`DotNS connect: failed to resolve EVM address from ${this.substrateAddress} via ReviveApi.address (${e.message?.slice(0, 200)})`);
776
+ }
777
+ setDeployAttribute("deploy.dotns.rpc_used", rpc);
778
+ setDeployAttribute("deploy.dotns.evm_address", this.evmAddress);
779
+ this.connected = true;
780
+ if (options.nativeToEthRatio) this._nativeToEthRatio = options.nativeToEthRatio;
781
+ try {
782
+ await this.ensureMappedAccountReady(options.autoAccountMapping ?? false);
783
+ } catch (e) {
784
+ this.connected = false;
785
+ throw e;
786
+ }
787
+ return this;
788
+ });
733
789
  }
734
790
  async ensureMappedAccountReady(autoAccountMapping = false) {
735
791
  this.ensureConnected();
@@ -737,10 +793,12 @@ var DotNS = class {
737
793
  throw new Error("Account mapping unavailable before DotNS signer is initialized");
738
794
  }
739
795
  if (autoAccountMapping) {
796
+ setDeployAttribute("deploy.dotns.mapping_source", "auto-account-mapping");
740
797
  await this.ensureAutoMappedAccountReady();
741
798
  return;
742
799
  }
743
800
  if (await this.clientWrapper.checkIfAccountMapped(this.substrateAddress)) {
801
+ setDeployAttribute("deploy.dotns.mapping_source", "already-mapped");
744
802
  console.log(` Account: mapped`);
745
803
  return;
746
804
  }
@@ -749,6 +807,7 @@ var DotNS = class {
749
807
  await this.clientWrapper.ensureAccountMapped(this.substrateAddress, this.signer);
750
808
  } catch (e) {
751
809
  if (await this.clientWrapper.checkIfAccountMapped(this.substrateAddress)) {
810
+ setDeployAttribute("deploy.dotns.mapping_source", "direct-mapped");
752
811
  console.log(` Account: mapped`);
753
812
  return;
754
813
  }
@@ -756,9 +815,11 @@ var DotNS = class {
756
815
  signer: this.substrateAddress,
757
816
  error: e?.message?.slice?.(0, 200) ?? String(e).slice(0, 200)
758
817
  });
818
+ setDeployAttribute("deploy.dotns.mapping_source", "auto-map-fallback");
759
819
  await this.ensureAutoMappedAccountReady();
760
820
  return;
761
821
  }
822
+ setDeployAttribute("deploy.dotns.mapping_source", "direct-mapped");
762
823
  console.log(` Account: mapped`);
763
824
  }
764
825
  async ensureAutoMappedAccountReady() {
@@ -841,6 +902,11 @@ var DotNS = class {
841
902
  * Returns "not-bound" if the chain is unreachable (safe fallback to generic advice).
842
903
  */
843
904
  async classifyAliasAccountState(ss58) {
905
+ if (this._classifyOverrideForTest !== null) {
906
+ const result = this._classifyOverrideForTest;
907
+ this._classifyOverrideForTest = null;
908
+ return result;
909
+ }
844
910
  if (!this.clientWrapper) return { state: "not-bound" };
845
911
  try {
846
912
  const api = this.clientWrapper.client;
@@ -1264,7 +1330,12 @@ var DotNS = class {
1264
1330
  }
1265
1331
  }
1266
1332
  const priceMeta = await withTimeout(this.contractCall(this._contracts.POP_RULES, POP_RULES_ABI, "priceWithCheck", [label, this.evmAddress]), 3e4, "priceWithCheck");
1267
- const priceRaw = priceMeta.price ?? (Array.isArray(priceMeta) ? priceMeta[0] : 0n);
1333
+ const priceRaw = priceMeta?.price;
1334
+ if (priceRaw == null) {
1335
+ throw new Error(
1336
+ `priceWithCheck returned unexpected shape (expected object with .price): ` + JSON.stringify(priceMeta, (_, v) => typeof v === "bigint" ? v.toString() : v)
1337
+ );
1338
+ }
1268
1339
  const priceWei = typeof priceRaw === "bigint" ? priceRaw : BigInt(priceRaw);
1269
1340
  console.log(` Required status: ${popStatusName(requiredStatus)}`);
1270
1341
  console.log(` User status: ${popStatusName(userStatus)}`);
@@ -1277,6 +1348,12 @@ var DotNS = class {
1277
1348
  Finalizing registration for ${registration.label}.dot...`);
1278
1349
  const bufferedPaymentWei = priceWei * 110n / 100n;
1279
1350
  const bufferedPaymentNative = bufferedPaymentWei / this._nativeToEthRatio;
1351
+ if (priceWei > 0n && bufferedPaymentNative === 0n) {
1352
+ throw new Error(
1353
+ `Payment conversion underflow: priceWei=${priceWei} rounds to 0 native units (nativeToEthRatio=${this._nativeToEthRatio}). Cannot call register with zero payment.`
1354
+ );
1355
+ }
1356
+ setDeployAttribute("deploy.payment_wei", priceWei.toString());
1280
1357
  console.log(` Oracle price: ${formatEther(priceWei)} PAS`);
1281
1358
  console.log(` Paying: ${formatEther(bufferedPaymentWei)} PAS`);
1282
1359
  const txHash = await this.contractTransaction(this._contracts.DOTNS_REGISTRAR_CONTROLLER, bufferedPaymentNative, DOTNS_REGISTRAR_CONTROLLER_ABI, "register", [registration], (s) => console.log(` ${s}`));
@@ -1299,7 +1376,11 @@ var DotNS = class {
1299
1376
  // `register(label)` will succeed, so the caller can fail-fast BEFORE the
1300
1377
  // Bulletin chunk upload. Never writes to chain. See issue #100.
1301
1378
  async preflight(label) {
1379
+ return this._preflightInternal(label, false);
1380
+ }
1381
+ async _preflightInternal(label, reproveAttempted) {
1302
1382
  return withSpan("deploy.dotns.preflight", `preflight ${label}.dot`, {}, async () => {
1383
+ setDeployAttribute("deploy.dotns.reprove.auto", "false");
1303
1384
  this.ensureConnected();
1304
1385
  const validated = validateDomainLabel(label);
1305
1386
  const trailingDigits = countTrailingDigits(validated);
@@ -1414,9 +1495,65 @@ var DotNS = class {
1414
1495
  signerFreeBalance
1415
1496
  };
1416
1497
  }
1417
- if (userStatus === ProofOfPersonhoodStatus.NoStatus && isTestnet && this._environmentId === "paseo-next-v2" && this.substrateAddress) {
1498
+ if (userStatus === ProofOfPersonhoodStatus.NoStatus && isTestnet && this._popSelfServe?.stateAwareGuidance === true && this.substrateAddress) {
1418
1499
  const aliasState = await this.classifyAliasAccountState(this.substrateAddress);
1419
- const remediationMessage = formatPersonhoodRemediation(aliasState, this._environmentId);
1500
+ if (aliasState.state === "bound-likely-stale" && !this._usesExternalSigner && this._localMnemonic && !reproveAttempted) {
1501
+ const minBalance = REPROVE_FEE_ESTIMATE * REPROVE_FEE_SAFETY_MARGIN_PCT / 100n;
1502
+ const symbol = resolveNativeTokenSymbol(this._environmentId);
1503
+ if (signerFreeBalance < minBalance) {
1504
+ setDeployAttribute("deploy.dotns.reprove.auto", "true");
1505
+ setDeployAttribute("deploy.dotns.reprove.outcome", "insufficient_funds");
1506
+ return {
1507
+ label: validated,
1508
+ classification,
1509
+ userStatus,
1510
+ trailingDigits,
1511
+ baselength,
1512
+ isAvailable: true,
1513
+ existingOwner: null,
1514
+ isBaseNameReserved: isReserved,
1515
+ reservationOwner,
1516
+ isTestnet,
1517
+ canProceed: false,
1518
+ reason: `Cannot auto-refresh: signer balance ${fmtPas(signerFreeBalance)} ${symbol} < estimated fee ${fmtPas(REPROVE_FEE_ESTIMATE)} ${symbol}. Top up via the testnet faucet or run \`tools/reprove-alias.mjs --mnemonic <your-mnemonic> --env ${this._environmentId}\` manually.`,
1519
+ plannedAction: "abort",
1520
+ needsPopUpgrade: false,
1521
+ targetPopStatus,
1522
+ signerFreeBalance
1523
+ };
1524
+ }
1525
+ console.log(`
1526
+ [stage] Personhood preflight \u2014 auto-refresh`);
1527
+ console.log(` Alias binding exists but ring revision is stale (stored=${aliasState.revision ?? "unknown"}).`);
1528
+ console.log(` Refreshing automatically on testnet\u2026`);
1529
+ console.log(` Estimated reprove fee: ${fmtPas(REPROVE_FEE_ESTIMATE)} ${symbol}. Signer balance: ${fmtPas(signerFreeBalance)} ${symbol} \u2014 proceeding.`);
1530
+ setDeployAttribute("deploy.dotns.reprove.auto", "true");
1531
+ let reproveSucceeded = false;
1532
+ try {
1533
+ console.log(` Submitting reprove_alias_account\u2026`);
1534
+ const reproveResult = await this.reprove(this._localMnemonic);
1535
+ console.log(` Refresh complete (old_revision=${reproveResult.oldRevision}, new_revision=${reproveResult.newRevision}, block=${reproveResult.blockHash}).`);
1536
+ setDeployAttribute("deploy.dotns.reprove.outcome", "success");
1537
+ setDeployAttribute("deploy.dotns.reprove.old_revision", String(reproveResult.oldRevision));
1538
+ setDeployAttribute("deploy.dotns.reprove.new_revision", String(reproveResult.newRevision));
1539
+ const refreshedState = await this.classifyAliasAccountState(this.substrateAddress);
1540
+ if (refreshedState.state !== "bound-fresh") {
1541
+ console.log(` Re-classification after reprove returned '${refreshedState.state}' \u2014 falling through to manual remediation.`);
1542
+ setDeployAttribute("deploy.dotns.reprove.outcome", "failed_reverify");
1543
+ } else {
1544
+ reproveSucceeded = true;
1545
+ }
1546
+ } catch (e) {
1547
+ const msg = e?.message ?? String(e);
1548
+ console.log(` Auto-reprove failed: ${msg}`);
1549
+ setDeployAttribute("deploy.dotns.reprove.outcome", "failed_submission");
1550
+ }
1551
+ if (reproveSucceeded) {
1552
+ console.log(` Continuing with registration of ${validated}.dot.`);
1553
+ return this._preflightInternal(label, true);
1554
+ }
1555
+ }
1556
+ const remediationMessage = formatPersonhoodRemediation(aliasState, this._popSelfServe, this._environmentId);
1420
1557
  const currentName2 = popStatusName(userStatus);
1421
1558
  const requiredName2 = popStatusName(classification.status);
1422
1559
  return {
@@ -1452,7 +1589,16 @@ var DotNS = class {
1452
1589
  reservationOwner,
1453
1590
  isTestnet,
1454
1591
  canProceed: false,
1455
- reason: `${validated}.dot requires ${requiredName}, but this signer is ${currentName}. Self-attestation is no longer available. Choose a NoStatus-compatible name (base length >= 9 with exactly two trailing digits, for example ${exampleNoStatusLabel(validated)}) or contact the DotNS team for whitelisting / Personhood status.`,
1592
+ reason: formatPopShortfallReason({
1593
+ label: validated,
1594
+ requiredName,
1595
+ currentName,
1596
+ isTestnet,
1597
+ environmentId: this._environmentId,
1598
+ popSelfServe: this._popSelfServe,
1599
+ aliasState: null,
1600
+ exampleNoStatusLabel: exampleNoStatusLabel(validated)
1601
+ }),
1456
1602
  plannedAction: "abort",
1457
1603
  needsPopUpgrade: false,
1458
1604
  targetPopStatus,
@@ -1533,6 +1679,8 @@ var DotNS = class {
1533
1679
  if (preRequiredStatus === ProofOfPersonhoodStatus.Reserved) {
1534
1680
  throw new Error(preClassification.message);
1535
1681
  }
1682
+ const isTestnet = await this.isTestnet();
1683
+ const registerAliasState = isTestnet && this._popSelfServe?.stateAwareGuidance === true && this.substrateAddress ? await this.classifyAliasAccountState(this.substrateAddress) : null;
1536
1684
  const rejectIneligible = (statusRequired, userStatus2) => {
1537
1685
  if (statusRequired === ProofOfPersonhoodStatus.NoStatus && userStatus2 === ProofOfPersonhoodStatus.ProofOfPersonhoodLite) {
1538
1686
  throw new Error(
@@ -1540,7 +1688,16 @@ var DotNS = class {
1540
1688
  );
1541
1689
  }
1542
1690
  throw new Error(
1543
- `${label}.dot requires ${popStatusName(statusRequired)}, but this signer is ${popStatusName(userStatus2)}. Self-attestation is no longer available. Choose a NoStatus-compatible name (base length >= 9 with exactly two trailing digits, for example ${exampleNoStatusLabel(label)}) or contact the DotNS team for whitelisting / Personhood status.`
1691
+ formatPopShortfallReason({
1692
+ label,
1693
+ requiredName: popStatusName(statusRequired),
1694
+ currentName: popStatusName(userStatus2),
1695
+ isTestnet,
1696
+ environmentId: this._environmentId,
1697
+ popSelfServe: this._popSelfServe,
1698
+ aliasState: registerAliasState,
1699
+ exampleNoStatusLabel: exampleNoStatusLabel(label)
1700
+ })
1544
1701
  );
1545
1702
  };
1546
1703
  const reverse = options.reverse ?? (process.env.DOTNS_REVERSE ?? "false").toLowerCase() === "true";
@@ -1666,6 +1823,7 @@ export {
1666
1823
  parseProofOfPersonhoodStatus,
1667
1824
  popStatusName,
1668
1825
  formatPersonhoodRemediation,
1826
+ formatPopShortfallReason,
1669
1827
  DotNS,
1670
1828
  dotns
1671
1829
  };
@@ -65,6 +65,13 @@ var environments_default = {
65
65
  autoAccountMapping: true,
66
66
  bulletinAuthorizeV2: true,
67
67
  nativeToEthRatio: 1e8,
68
+ popSelfServe: {
69
+ sudoEnvLabel: "Next V2",
70
+ faucetUrl: "https://faucet.polkadot.io/?parachain=1500",
71
+ personhoodFaucetUrl: "https://sudo.personhood.dev/personhood-faucet",
72
+ dotnsBootstrapUrl: "https://sudo.personhood.dev/dotns-bootstrap",
73
+ stateAwareGuidance: true
74
+ },
68
75
  contracts: {
69
76
  DOTNS_PROTOCOL_REGISTRY: "0x5Caef84563fc980178e28417414aa65bA32f6B4e",
70
77
  DOTNS_REGISTRAR: "0x885b8085bA92A31c4ef52076f77379E647ECC399",
@@ -319,7 +326,14 @@ var HARDCODED_FALLBACK = {
319
326
  id: "paseo-next-v2",
320
327
  name: "Paseo Next v2",
321
328
  network: "testnet",
322
- description: "Next iteration of the Paseo Next testnet (hardcoded fallback)"
329
+ description: "Next iteration of the Paseo Next testnet (hardcoded fallback)",
330
+ popSelfServe: {
331
+ sudoEnvLabel: "Next V2",
332
+ faucetUrl: "https://faucet.polkadot.io/?parachain=1500",
333
+ personhoodFaucetUrl: "https://sudo.personhood.dev/personhood-faucet",
334
+ dotnsBootstrapUrl: "https://sudo.personhood.dev/dotns-bootstrap",
335
+ stateAwareGuidance: true
336
+ }
323
337
  }
324
338
  ],
325
339
  chains: [
@@ -444,6 +458,9 @@ function resolveEndpoints(doc, envId) {
444
458
  nativeToEthRatio: BigInt(env.nativeToEthRatio ?? 1e6)
445
459
  };
446
460
  }
461
+ function getPopSelfServeConfig(doc, envId) {
462
+ return doc.environments.find((e) => e.id === envId)?.popSelfServe ?? null;
463
+ }
447
464
  function listEnvironments(doc) {
448
465
  const bulletinChain = doc.chains.find((c) => c.id === "bulletin");
449
466
  return doc.environments.map((env) => {
@@ -480,6 +497,7 @@ export {
480
497
  defaultBundledPath,
481
498
  loadEnvironments,
482
499
  resolveEndpoints,
500
+ getPopSelfServeConfig,
483
501
  listEnvironments,
484
502
  formatEnvironmentTable
485
503
  };