@parity/product-deploy 0.11.0-rc.3 → 0.11.0-rc.5

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.
Files changed (43) hide show
  1. package/assets/environments.json +2 -0
  2. package/dist/auth-config.js +4 -4
  3. package/dist/bug-report.d.ts +10 -1
  4. package/dist/bug-report.js +6 -4
  5. package/dist/{chunk-VZTZEW7C.js → chunk-BIJICTV4.js} +41 -28
  6. package/dist/{chunk-PYGCOK67.js → chunk-ITXKADHO.js} +35 -11
  7. package/dist/{chunk-QRKI6MMK.js → chunk-JSYQ3JQS.js} +4 -1
  8. package/dist/{chunk-RVGCHRLK.js → chunk-KBKS3V2V.js} +2 -2
  9. package/dist/{chunk-PH32GINP.js → chunk-LFRH4HBZ.js} +1 -1
  10. package/dist/{chunk-RFT2I7TB.js → chunk-M6OQTMDB.js} +1 -1
  11. package/dist/{chunk-4IUTMHVB.js → chunk-MRQPJLPS.js} +24 -3
  12. package/dist/{chunk-2NLCZQY4.js → chunk-NEQRG3AY.js} +1 -1
  13. package/dist/{chunk-24KTLZ67.js → chunk-OZEJEDHM.js} +14 -4
  14. package/dist/{chunk-CLM3ODFR.js → chunk-PECJ5FDA.js} +9 -3
  15. package/dist/{chunk-3US5OZWX.js → chunk-RU7Z5Y7Z.js} +1 -1
  16. package/dist/{chunk-5KRARO46.js → chunk-UIDRWZWX.js} +1 -1
  17. package/dist/{chunk-REAHYALJ.js → chunk-UJLQBL7C.js} +3 -3
  18. package/dist/chunk-probe.js +3 -3
  19. package/dist/commands/login.js +12 -12
  20. package/dist/commands/logout.js +5 -5
  21. package/dist/commands/transfer.js +5 -5
  22. package/dist/commands/whoami.js +4 -4
  23. package/dist/deploy-actors.js +7 -7
  24. package/dist/deploy.d.ts +19 -1
  25. package/dist/deploy.js +16 -12
  26. package/dist/dotns.d.ts +15 -2
  27. package/dist/dotns.js +9 -5
  28. package/dist/environments.d.ts +2 -0
  29. package/dist/environments.js +1 -1
  30. package/dist/index.js +13 -13
  31. package/dist/manifest/publish.js +13 -13
  32. package/dist/memory-report.js +2 -2
  33. package/dist/merkle.js +12 -12
  34. package/dist/personhood/bootstrap.js +5 -5
  35. package/dist/personhood/people-client.js +5 -5
  36. package/dist/pool.d.ts +3 -1
  37. package/dist/pool.js +1 -1
  38. package/dist/run-state.js +1 -1
  39. package/dist/sss-allowance-cache.js +5 -5
  40. package/dist/storage-signer.js +12 -12
  41. package/dist/telemetry.js +2 -2
  42. package/dist/version-check.js +3 -3
  43. package/package.json +1 -1
@@ -5,6 +5,7 @@
5
5
  "name": "Preview",
6
6
  "network": "testnet",
7
7
  "description": "Product Preview net, used by Product Teams",
8
+ "bulletinAutoAuthorize": true,
8
9
  "e2eEligible": true,
9
10
  "backend": "https://polkadot-app-stg.parity.io/",
10
11
  "ipfs": "https://previewnet.substrate.dev",
@@ -96,6 +97,7 @@
96
97
  "name": "Paseo Next v2",
97
98
  "network": "testnet",
98
99
  "description": "Next iteration of the Paseo Next testnet",
100
+ "bulletinAutoAuthorize": true,
99
101
  "e2eEligible": true,
100
102
  "backend": "https://identity-backend-next.parity-testnet.parity.io",
101
103
  "ipfs": "https://paseo-bulletin-next-ipfs.polkadot.io",
@@ -9,11 +9,11 @@ import {
9
9
  getPeopleChainEndpoints,
10
10
  hasPersistedSession,
11
11
  resolveBulletinEndpoints
12
- } from "./chunk-RVGCHRLK.js";
12
+ } from "./chunk-KBKS3V2V.js";
13
13
  import "./chunk-TSPERKUS.js";
14
- import "./chunk-5KRARO46.js";
15
- import "./chunk-PH32GINP.js";
16
- import "./chunk-QRKI6MMK.js";
14
+ import "./chunk-UIDRWZWX.js";
15
+ import "./chunk-LFRH4HBZ.js";
16
+ import "./chunk-JSYQ3JQS.js";
17
17
  import "./chunk-ZOC4GITL.js";
18
18
  export {
19
19
  DOT_DAPP_ID,
@@ -24,6 +24,15 @@ declare function buildReportBody(error: Error): string;
24
24
  declare function buildTitle(error: Error): string;
25
25
  declare function buildLabels(error: Error): string[];
26
26
  declare function createGhIssue(title: string, body: string, labels: string[]): string;
27
+ /**
28
+ * True for deploy failures caused by user input rather than a code defect, so
29
+ * the bug-report prompt is NOT offered for them (#869). Scoped to DotNS name
30
+ * validation: a malformed or reserved label ("Invalid domain label …",
31
+ * "… reserves base names of N chars …") is something the user fixes by choosing
32
+ * a different name — filing it as a bug just creates noise. The actionable error
33
+ * message has already been printed to the user. Exported for unit testing.
34
+ */
35
+ declare function isUserInputError(error: Error): boolean;
27
36
  declare function offerBugReport(error: Error): Promise<void>;
28
37
 
29
- export { buildCliFlagsSummary, buildLabels, buildReportBody, buildTitle, createGhIssue, getCapturedTail, installLogCapture, offerBugReport, scrubSecrets, setDeployContext };
38
+ export { buildCliFlagsSummary, buildLabels, buildReportBody, buildTitle, createGhIssue, getCapturedTail, installLogCapture, isUserInputError, offerBugReport, scrubSecrets, setDeployContext };
@@ -6,13 +6,14 @@ import {
6
6
  createGhIssue,
7
7
  getCapturedTail,
8
8
  installLogCapture,
9
+ isUserInputError,
9
10
  offerBugReport,
10
11
  scrubSecrets,
11
12
  setDeployContext
12
- } from "./chunk-CLM3ODFR.js";
13
- import "./chunk-2NLCZQY4.js";
14
- import "./chunk-5KRARO46.js";
15
- import "./chunk-PH32GINP.js";
13
+ } from "./chunk-PECJ5FDA.js";
14
+ import "./chunk-NEQRG3AY.js";
15
+ import "./chunk-UIDRWZWX.js";
16
+ import "./chunk-LFRH4HBZ.js";
16
17
  export {
17
18
  buildCliFlagsSummary,
18
19
  buildLabels,
@@ -21,6 +22,7 @@ export {
21
22
  createGhIssue,
22
23
  getCapturedTail,
23
24
  installLogCapture,
25
+ isUserInputError,
24
26
  offerBugReport,
25
27
  scrubSecrets,
26
28
  setDeployContext
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  preflightSssAllowance
3
- } from "./chunk-RFT2I7TB.js";
3
+ } from "./chunk-M6OQTMDB.js";
4
4
  import {
5
5
  statementSigningAccount
6
6
  } from "./chunk-GRPLHUYC.js";
@@ -31,16 +31,16 @@ import {
31
31
  } from "./chunk-S7EM5VMW.js";
32
32
  import {
33
33
  setDeployContext
34
- } from "./chunk-CLM3ODFR.js";
34
+ } from "./chunk-PECJ5FDA.js";
35
35
  import {
36
36
  probeChunks
37
- } from "./chunk-3US5OZWX.js";
37
+ } from "./chunk-RU7Z5Y7Z.js";
38
38
  import {
39
39
  packSection
40
40
  } from "./chunk-C2TS5MER.js";
41
41
  import {
42
42
  resolveStorageSigner
43
- } from "./chunk-24KTLZ67.js";
43
+ } from "./chunk-OZEJEDHM.js";
44
44
  import {
45
45
  createSlotAccountSigner,
46
46
  requestResourceAllocation
@@ -49,7 +49,7 @@ import {
49
49
  STALE_SESSION_MESSAGE,
50
50
  getPeopleChainEndpoints,
51
51
  hasPersistedSession
52
- } from "./chunk-RVGCHRLK.js";
52
+ } from "./chunk-KBKS3V2V.js";
53
53
  import {
54
54
  CLI_NAME
55
55
  } from "./chunk-TSPERKUS.js";
@@ -62,7 +62,7 @@ import {
62
62
  parseDomainName,
63
63
  popStatusName,
64
64
  verifyNonceAdvanced
65
- } from "./chunk-PYGCOK67.js";
65
+ } from "./chunk-ITXKADHO.js";
66
66
  import {
67
67
  derivePoolAccounts,
68
68
  detectTestnet,
@@ -70,7 +70,7 @@ import {
70
70
  fetchPoolAuthorizations,
71
71
  isAuthorizationSufficient,
72
72
  selectAccount
73
- } from "./chunk-4IUTMHVB.js";
73
+ } from "./chunk-MRQPJLPS.js";
74
74
  import {
75
75
  VERSION,
76
76
  captureWarning,
@@ -84,13 +84,13 @@ import {
84
84
  truncateAddress,
85
85
  withDeploySpan,
86
86
  withSpan
87
- } from "./chunk-5KRARO46.js";
87
+ } from "./chunk-UIDRWZWX.js";
88
88
  import {
89
89
  DEFAULT_ENV_ID,
90
90
  getPopSelfServeConfig,
91
91
  loadEnvironments,
92
92
  resolveEndpoints
93
- } from "./chunk-QRKI6MMK.js";
93
+ } from "./chunk-JSYQ3JQS.js";
94
94
  import {
95
95
  NonRetryableError
96
96
  } from "./chunk-ZOC4GITL.js";
@@ -373,6 +373,7 @@ var DEFAULT_BULLETIN_RPC = "wss://paseo-bulletin-rpc.polkadot.io";
373
373
  var DEFAULT_POOL_SIZE = 10;
374
374
  var BULLETIN_ENDPOINTS = [DEFAULT_BULLETIN_RPC];
375
375
  var POOL_SIZE = DEFAULT_POOL_SIZE;
376
+ var bulletinAutoAuthorize = false;
376
377
  var _deployRpcFailedOver = false;
377
378
  var _onWsHalt = null;
378
379
  function setWsHaltCallback(cb) {
@@ -511,7 +512,7 @@ async function getProvider() {
511
512
  const selectionResult = selectAccount(authorizations, Math.random, pinnedPoolIndex);
512
513
  const selectedAccount = selectionResult.account;
513
514
  const eligibleCount = selectionResult.eligibleCount;
514
- await ensureAuthorized(unsafeApi, selectedAccount.address, `pool account ${selectedAccount.index}`);
515
+ await ensureAuthorized(unsafeApi, selectedAccount.address, `pool account ${selectedAccount.index}`, { autoAuthorize: bulletinAutoAuthorize });
515
516
  console.log(` Using pool account ${selectedAccount.index}: ${selectedAccount.address}`);
516
517
  setDeployAttribute("deploy.signer.mode", "pool");
517
518
  setDeployAttribute("deploy.pool.account", truncateAddress(selectedAccount.address));
@@ -540,7 +541,7 @@ async function getDirectProvider(mnemonic, derivationPath = "") {
540
541
  let now = currentBlock.number;
541
542
  if (!auth || Number(auth.expiration ?? 0) <= now) {
542
543
  try {
543
- await ensureAuthorized(unsafeApi, ss58, "direct signer");
544
+ await ensureAuthorized(unsafeApi, ss58, "direct signer", { autoAuthorize: bulletinAutoAuthorize });
544
545
  [auth, currentBlock] = await Promise.all([
545
546
  unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum2("Account", ss58)),
546
547
  client.getFinalizedBlock()
@@ -572,7 +573,7 @@ async function getSignerProvider(signer, ss58) {
572
573
  let now = currentBlock.number;
573
574
  if (!auth || Number(auth.expiration ?? 0) <= now) {
574
575
  try {
575
- await ensureAuthorized(unsafeApi, ss58, "external signer");
576
+ await ensureAuthorized(unsafeApi, ss58, "external signer", { autoAuthorize: bulletinAutoAuthorize });
576
577
  [auth, currentBlock] = await Promise.all([
577
578
  unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum2("Account", ss58)),
578
579
  client.getFinalizedBlock()
@@ -614,6 +615,12 @@ function formatStorageSignerLine(slotAddress, failReason, owned) {
614
615
  }
615
616
  return ` Storage signer: pool fallback (${failReason ?? "no session"})`;
616
617
  }
618
+ function formatTransferModeStorageSignerLine(workerAddress) {
619
+ return ` Storage signer: worker ${workerAddress} (transfer mode)`;
620
+ }
621
+ function formatTransferModeDotnsLine(alreadyOwned, dotName, recipient) {
622
+ return alreadyOwned ? ` DotNS: you already own ${dotName} \u2014 content update needs your phone signature (no transfer)` : ` DotNS: will register ${dotName} and transfer it to your account ${recipient}`;
623
+ }
617
624
  function selectStorageReconnect(options) {
618
625
  if (options.storageSigner && options.storageSignerAddress) {
619
626
  let useSlot = true;
@@ -965,7 +972,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
965
972
  while (b < chunks.length) {
966
973
  if (wsHaltDetected && reconnect && reconnectionsUsed < MAX_RECONNECTIONS) {
967
974
  wsHaltDetected = false;
968
- await doReconnect();
975
+ await doReconnectAndRebase();
969
976
  }
970
977
  const batchSize = reconnectionsUsed > 0 ? BATCH_SIZE_RECOVERY : BATCH_SIZE_INITIAL;
971
978
  const batchIndices = [];
@@ -1009,7 +1016,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
1009
1016
  for (const idx of batchIndices) {
1010
1017
  const chunkNonce = assignedNonces.get(idx);
1011
1018
  if (chunkNonce !== void 0 && chunkNonce < currentNonce && stored[idx] === null) {
1012
- console.log(` Chunk ${idx + 1}: nonce ${chunkNonce} consumed (current=${currentNonce}), treating as included`);
1019
+ console.log(` Chunk ${idx}: nonce ${chunkNonce} consumed (current=${currentNonce}), treating as included`);
1013
1020
  stored[idx] = { cid: createCID(chunks[idx], CID_CONFIG.codec, 18), len: chunks[idx].length, viaFallback: true };
1014
1021
  nonceAdvanceIndices.add(idx);
1015
1022
  assignedNonces.delete(idx);
@@ -1027,7 +1034,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
1027
1034
  }
1028
1035
  const failCid = createCID(fail.chunkData, CID_CONFIG.codec, 18);
1029
1036
  if (probeFailedCids && probeFailedCids.has(failCid.toString()) && fail.error?.message?.includes("isValid:false")) {
1030
- console.log(` Chunk ${fail.index + 1}: isValid:false but CID was probe-failed \u2014 treating as already on chain`);
1037
+ console.log(` Chunk ${fail.index}: isValid:false but CID was probe-failed \u2014 treating as already on chain`);
1031
1038
  captureWarning("isValid:false treated as success (probe-failed backstop)", { chunkIndex: fail.index + 1, cid: failCid.toString() });
1032
1039
  stored[fail.index] = { cid: failCid, len: fail.chunkData.length, viaFallback: true };
1033
1040
  continue;
@@ -1035,13 +1042,13 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
1035
1042
  captureWarning("Chunk upload failed, retrying", { chunkIndex: fail.index + 1, maxRetries: MAX_CHUNK_RETRIES, error: fail.error?.message?.slice(0, 200) });
1036
1043
  const isExpiryFailure = fail.error?.message?.includes("isValid:false");
1037
1044
  if (isExpiryFailure) {
1038
- console.log(` Chunk ${fail.index + 1}: tx rejected (isValid:false), likely mortal era expiry \u2014 reissuing with fresh nonce`);
1045
+ console.log(` Chunk ${fail.index}: tx rejected (isValid:false), likely mortal era expiry \u2014 reissuing with fresh nonce`);
1039
1046
  }
1040
1047
  let retried = false;
1041
1048
  for (let attempt = 1; attempt <= MAX_CHUNK_RETRIES; attempt++) {
1042
1049
  recordRecoveryAndCheckBudget("chunk_retry");
1043
1050
  const retryDelay = Math.min(RETRY_BASE_DELAY_MS * Math.pow(2, attempt - 1), RETRY_MAX_DELAY_MS);
1044
- console.log(` Retrying chunk ${fail.index + 1} (attempt ${attempt}/${MAX_CHUNK_RETRIES}) in ${(retryDelay / 1e3).toFixed(0)}s...`);
1051
+ console.log(` Retrying chunk ${fail.index} (attempt ${attempt}/${MAX_CHUNK_RETRIES}) in ${(retryDelay / 1e3).toFixed(0)}s...`);
1045
1052
  await new Promise((r) => setTimeout(r, retryDelay));
1046
1053
  let perRetryChanged = false;
1047
1054
  if (isConnectionError(fail.error) && reconnect && reconnectionsUsed < MAX_RECONNECTIONS) {
@@ -1056,7 +1063,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
1056
1063
  const currentNonce = await _fetchNonce(BULLETIN_ENDPOINTS, ss58);
1057
1064
  const originalNonce = assignedNonces.get(fail.index);
1058
1065
  if (!perRetryChanged && originalNonce !== void 0 && originalNonce < currentNonce) {
1059
- console.log(` Chunk ${fail.index + 1}: nonce ${originalNonce} consumed (current=${currentNonce}), treating as included`);
1066
+ console.log(` Chunk ${fail.index}: nonce ${originalNonce} consumed (current=${currentNonce}), treating as included`);
1060
1067
  stored[fail.index] = { cid: createCID(fail.chunkData, CID_CONFIG.codec, 18), len: fail.chunkData.length, viaFallback: true };
1061
1068
  nonceAdvanceIndices.add(fail.index);
1062
1069
  assignedNonces.delete(fail.index);
@@ -1073,7 +1080,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
1073
1080
  break;
1074
1081
  } catch (e) {
1075
1082
  if (probeFailedCids && probeFailedCids.has(failCid.toString()) && e?.message?.includes("isValid:false")) {
1076
- console.log(` Chunk ${fail.index + 1}: retry isValid:false but CID was probe-failed \u2014 treating as already on chain`);
1083
+ console.log(` Chunk ${fail.index}: retry isValid:false but CID was probe-failed \u2014 treating as already on chain`);
1077
1084
  captureWarning("isValid:false retry treated as success (probe-failed backstop)", { chunkIndex: fail.index + 1, cid: failCid.toString(), attempt });
1078
1085
  stored[fail.index] = { cid: failCid, len: fail.chunkData.length, viaFallback: true };
1079
1086
  assignedNonces.delete(fail.index);
@@ -1094,7 +1101,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
1094
1101
  if (isConnectionError(fail.error) && reconnectionsUsed >= MAX_RECONNECTIONS) {
1095
1102
  throw new Error(`Connection lost and max reconnections (${MAX_RECONNECTIONS}) exhausted`);
1096
1103
  }
1097
- throw new Error(`Chunk ${fail.index + 1} failed after ${MAX_CHUNK_RETRIES} retries: ${fail.error?.message?.slice(0, 100)}`);
1104
+ throw new Error(`Chunk ${fail.index} failed after ${MAX_CHUNK_RETRIES} retries: ${fail.error?.message?.slice(0, 100)}`);
1098
1105
  }
1099
1106
  }
1100
1107
  b += batchSize;
@@ -1118,7 +1125,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
1118
1125
  for (const m of missingResults) {
1119
1126
  const idx = cidToIndex.get(m.cid);
1120
1127
  for (let attempt = 1; attempt <= MAX_REPROBE_RETRIES; attempt++) {
1121
- console.log(` Nonce-collision re-upload: chunk ${idx + 1} (attempt ${attempt}/${MAX_REPROBE_RETRIES})`);
1128
+ console.log(` Nonce-collision re-upload: chunk ${idx} (attempt ${attempt}/${MAX_REPROBE_RETRIES})`);
1122
1129
  try {
1123
1130
  const freshNonce = await _fetchNonce(BULLETIN_ENDPOINTS, ss58);
1124
1131
  const result2 = await storeChunk(unsafeApi, signer, chunks[idx], freshNonce, ss58, { fetchNonce: fetchNonceOverride });
@@ -1133,7 +1140,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
1133
1140
  }
1134
1141
  }
1135
1142
  if (attempt === MAX_REPROBE_RETRIES) {
1136
- throw new Error(`Nonce-collision re-upload of chunk ${idx + 1} failed after ${MAX_REPROBE_RETRIES} attempts: ${e.message?.slice(0, 100)}`);
1143
+ throw new Error(`Nonce-collision re-upload of chunk ${idx} failed after ${MAX_REPROBE_RETRIES} attempts: ${e.message?.slice(0, 100)}`);
1137
1144
  }
1138
1145
  }
1139
1146
  }
@@ -1154,7 +1161,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
1154
1161
  for (let i = 0; i < chunks.length; i++) {
1155
1162
  const expectedCid = createCID(chunks[i], CID_CONFIG.codec, 18);
1156
1163
  if (verifiedStored[i].cid.toString() !== expectedCid.toString()) {
1157
- throw new Error(`Chunk verification failed: chunk ${i + 1} CID mismatch (expected ${expectedCid}, got ${verifiedStored[i].cid})`);
1164
+ throw new Error(`Chunk verification failed: chunk ${i} CID mismatch (expected ${expectedCid}, got ${verifiedStored[i].cid})`);
1158
1165
  }
1159
1166
  }
1160
1167
  console.log(` All ${chunks.length} chunks verified \u2713`);
@@ -1993,6 +2000,7 @@ async function deploy(content, domainName = null, options = {}) {
1993
2000
  let envNativeToEthRatio;
1994
2001
  let envRegisterStorageDeposit;
1995
2002
  let envPopSelfServe = null;
2003
+ bulletinAutoAuthorize = false;
1996
2004
  if (options.bulletinEndpoints && options.bulletinEndpoints.length > 0) {
1997
2005
  envBulletin = options.bulletinEndpoints;
1998
2006
  envAssetHub = options.assetHubEndpoints;
@@ -2011,6 +2019,7 @@ async function deploy(content, domainName = null, options = {}) {
2011
2019
  envNativeToEthRatio = resolved.nativeToEthRatio;
2012
2020
  envRegisterStorageDeposit = resolved.registerStorageDeposit;
2013
2021
  envPopSelfServe = getPopSelfServeConfig(doc, envId);
2022
+ bulletinAutoAuthorize = resolved.bulletinAutoAuthorize;
2014
2023
  } catch (e) {
2015
2024
  if (e instanceof NonRetryableError) throw e;
2016
2025
  if (options.env !== void 0) throw e;
@@ -2056,7 +2065,7 @@ async function deploy(content, domainName = null, options = {}) {
2056
2065
  sessionCleanup = actors.worker.destroy.bind(actors.worker);
2057
2066
  if (actors.worker.source === "session") resolvedUserSession = actors.worker;
2058
2067
  if (actors.recipientH160) {
2059
- console.log(` Worker: ${actors.worker.source} signer ${actors.worker.address} \u2192 will transfer ${domainName ?? "the name"} to ${actors.recipientH160}`);
2068
+ console.log(` Worker: ${actors.worker.source} signer ${actors.worker.address} (signs Bulletin storage)`);
2060
2069
  } else {
2061
2070
  console.log(` Using ${actors.worker.source} signer: ${actors.worker.address}`);
2062
2071
  }
@@ -2107,8 +2116,10 @@ async function deploy(content, domainName = null, options = {}) {
2107
2116
  if (slotResult) {
2108
2117
  options = { ...options, storageSigner: slotResult.signer, storageSignerAddress: slotResult.slotAddress };
2109
2118
  console.log(formatStorageSignerLine(slotResult.slotAddress, void 0, slotResult.owned));
2119
+ } else if (options.transferTo && options.signerAddress) {
2120
+ console.log(formatTransferModeStorageSignerLine(options.signerAddress));
2110
2121
  } else {
2111
- const storageFailReason = resolvedUserSession ? "no allowance" : options.transferTo ? "transfer mode \u2014 worker signs storage" : void 0;
2122
+ const storageFailReason = resolvedUserSession ? "no allowance" : void 0;
2112
2123
  console.log(formatStorageSignerLine(null, storageFailReason));
2113
2124
  }
2114
2125
  }
@@ -2214,8 +2225,8 @@ async function deploy(content, domainName = null, options = {}) {
2214
2225
  const fromName = popStatusName(dotnsPreflight.userStatus);
2215
2226
  console.log(` Your PoP: ${fromName}`);
2216
2227
  console.log(` Domain: ${alreadyOwned ? "owned by you" : "available"}`);
2217
- if (dotnsPreflight.plannedAction === "already-owned-by-recipient") {
2218
- console.log(` DotNS signer: owner identity (phone signature required for content update)`);
2228
+ if (options.transferTo) {
2229
+ console.log(formatTransferModeDotnsLine(alreadyOwned, `${name}.dot`, options.transferTo));
2219
2230
  }
2220
2231
  }
2221
2232
  if (!dotnsPreflight.canProceed) {
@@ -2570,7 +2581,7 @@ async function deploy(content, domainName = null, options = {}) {
2570
2581
  console.log("=".repeat(60));
2571
2582
  console.log("\nCheck it out here:");
2572
2583
  console.log(` ${browserUrlFor(name, envId)}`);
2573
- console.log(` ${name}.dot (in a Polkadot-aware browser)`);
2584
+ console.log(` ${name}.dot (in a Polkadot app: mobile or desktop)`);
2574
2585
  console.log("\n" + "=".repeat(60) + "\n");
2575
2586
  return { domainName: name, fullDomain: `${name}.dot`, cid, ipfsCid };
2576
2587
  } finally {
@@ -3026,6 +3037,8 @@ export {
3026
3037
  isPhoneSignerActive,
3027
3038
  shouldHandoverName,
3028
3039
  formatStorageSignerLine,
3040
+ formatTransferModeStorageSignerLine,
3041
+ formatTransferModeDotnsLine,
3029
3042
  storeFile,
3030
3043
  __assignDenseNoncesForTest,
3031
3044
  storeChunkedContent,
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-SI2ZUOYD.js";
4
4
  import {
5
5
  isTestnetSpecName
6
- } from "./chunk-4IUTMHVB.js";
6
+ } from "./chunk-MRQPJLPS.js";
7
7
  import {
8
8
  captureWarning,
9
9
  markCodePath,
@@ -11,10 +11,10 @@ import {
11
11
  setDeploySentryTag,
12
12
  truncateAddress,
13
13
  withSpan
14
- } from "./chunk-5KRARO46.js";
14
+ } from "./chunk-UIDRWZWX.js";
15
15
  import {
16
16
  validateContractAddresses
17
- } from "./chunk-QRKI6MMK.js";
17
+ } from "./chunk-JSYQ3JQS.js";
18
18
  import {
19
19
  NonRetryableError
20
20
  } from "./chunk-ZOC4GITL.js";
@@ -126,6 +126,7 @@ var TX_WALL_CLOCK_CEILING_MS = 24e4;
126
126
  var TX_NO_PROGRESS_MS = 9e4;
127
127
  var WS_HEARTBEAT_TIMEOUT_MS = 3e5;
128
128
  var DOTNS_TX_MAX_ATTEMPTS = 3;
129
+ var VERIFY_EFFECT_CHAIN_SECONDS = 60;
129
130
  function classifyTxRetryDecision(err) {
130
131
  const msg = err instanceof Error ? err.message : String(err);
131
132
  const lower = msg.toLowerCase();
@@ -146,6 +147,9 @@ function dotnsRetryBackoffMs(attempt, rand = Math.random) {
146
147
  function shouldRetryTxAttempt(attempt, maxAttempts, decision) {
147
148
  return decision === "retry" && attempt < maxAttempts;
148
149
  }
150
+ function shouldRegateBeforeResign(attempt, isPhoneSigner) {
151
+ return attempt >= 2 && isPhoneSigner === true;
152
+ }
149
153
  function makeRetryStatusFilter(sink) {
150
154
  let buffered = false;
151
155
  let includedSeen = false;
@@ -783,6 +787,9 @@ var ReviveClientWrapper = class _ReviveClientWrapper {
783
787
  let lastError;
784
788
  for (let attempt = 1; attempt <= DOTNS_TX_MAX_ATTEMPTS; attempt++) {
785
789
  filter.reset();
790
+ if (shouldRegateBeforeResign(attempt, opts.isPhoneSigner)) {
791
+ await opts.onResign?.(attempt);
792
+ }
786
793
  try {
787
794
  return await this.signAndSubmitExtrinsic(buildExtrinsic(), signer, filter.callback, opts);
788
795
  } catch (e) {
@@ -830,7 +837,7 @@ var ReviveClientWrapper = class _ReviveClientWrapper {
830
837
  storage_deposit_limit: storageDepositLimit
831
838
  };
832
839
  }
833
- async submitTransaction(contractAddress, value, encodedData, signerSubstrateAddress, signer, statusCallback, { rpcs, useNoncePolling, functionName, args, contracts, verifyEffect, feeAsset }) {
840
+ async submitTransaction(contractAddress, value, encodedData, signerSubstrateAddress, signer, statusCallback, { rpcs, useNoncePolling, functionName, args, contracts, verifyEffect, feeAsset, isPhoneSigner, onResign }) {
834
841
  await this.ensureAccountMapped(signerSubstrateAddress, signer);
835
842
  if (functionName === "register") {
836
843
  try {
@@ -862,7 +869,7 @@ var ReviveClientWrapper = class _ReviveClientWrapper {
862
869
  "chain.tx.submit",
863
870
  `sign+submit ${functionName ?? "Revive.call"}`,
864
871
  { "chain.function_name": functionName ?? "Revive.call", "chain.use_nonce_polling": Boolean(useNoncePolling) },
865
- () => this.signAndSubmitWithRetry(buildExtrinsic, signer, statusCallback, "Revive.call", { nonceFallback, verifyEffect, feeAsset })
872
+ () => this.signAndSubmitWithRetry(buildExtrinsic, signer, statusCallback, "Revive.call", { nonceFallback, verifyEffect, feeAsset, isPhoneSigner, onResign })
866
873
  );
867
874
  }
868
875
  // Dry-runs each call individually, then wraps them in a single
@@ -1484,7 +1491,22 @@ var DotNS = class {
1484
1491
  await this._awaitPhoneReady(phoneLabel);
1485
1492
  }
1486
1493
  return await withTimeout(
1487
- this.clientWrapper.submitTransaction(contractAddress, value, encodedCallData, this.substrateAddress, this.signer, statusCallback, { rpcs, useNoncePolling, functionName, args, contracts: this._contracts, verifyEffect, feeAsset }),
1494
+ this.clientWrapper.submitTransaction(contractAddress, value, encodedCallData, this.substrateAddress, this.signer, statusCallback, {
1495
+ rpcs,
1496
+ useNoncePolling,
1497
+ functionName,
1498
+ args,
1499
+ contracts: this._contracts,
1500
+ verifyEffect,
1501
+ feeAsset,
1502
+ // Re-gate phone-signer retries (#971): a verifyEffect false-negative
1503
+ // makes signAndSubmitWithRetry re-sign; for a phone signer that needs
1504
+ // another tap, so pause via _awaitPhoneReady ("Re-sign needed … Check
1505
+ // your phone / Press Y") before re-signing. Both _awaitPhoneReady and the
1506
+ // retry-loop guard no-op for non-phone signers (local/dev workers).
1507
+ isPhoneSigner: this._isPhoneSigner,
1508
+ onResign: phoneLabel !== void 0 ? () => this._awaitPhoneReady(phoneLabel) : void 0
1509
+ }),
1488
1510
  OPERATION_TIMEOUT_MS,
1489
1511
  functionName
1490
1512
  );
@@ -1587,7 +1609,7 @@ var DotNS = class {
1587
1609
  const parentNode = namehash(`${parentLabel}.dot`);
1588
1610
  const subnodeNode = namehash(`${sublabel}.${parentLabel}.dot`);
1589
1611
  const subnodeRecord = { parentNode, subLabel: sublabel, parentLabel, owner: this.evmAddress };
1590
- const MAX_VERIFY_CHAIN_SECONDS = 30;
1612
+ const MAX_VERIFY_CHAIN_SECONDS = VERIFY_EFFECT_CHAIN_SECONDS;
1591
1613
  const POLL_INTERVAL_MS = 2e3;
1592
1614
  const verifyEffect = async () => {
1593
1615
  const wrapper = this.clientWrapper;
@@ -1727,7 +1749,7 @@ var DotNS = class {
1727
1749
  } catch (_) {
1728
1750
  }
1729
1751
  setDeployAttribute("deploy.dotns.contenthash_unchanged", "false");
1730
- const MAX_VERIFY_CHAIN_SECONDS = 30;
1752
+ const MAX_VERIFY_CHAIN_SECONDS = VERIFY_EFFECT_CHAIN_SECONDS;
1731
1753
  const POLL_INTERVAL_MS = 2e3;
1732
1754
  const verifyEffect = async () => {
1733
1755
  const wrapper = this.clientWrapper;
@@ -1824,7 +1846,7 @@ var DotNS = class {
1824
1846
  this.ensureConnected();
1825
1847
  console.log(` Setting text[${key}]: ${value}`);
1826
1848
  const node = namehash(`${domainName}.dot`);
1827
- const MAX_VERIFY_CHAIN_SECONDS = 30;
1849
+ const MAX_VERIFY_CHAIN_SECONDS = VERIFY_EFFECT_CHAIN_SECONDS;
1828
1850
  const TEXT_POLL_INTERVAL_MS = 2e3;
1829
1851
  const verifyEffect = async () => {
1830
1852
  const wrapper = this.clientWrapper;
@@ -1956,7 +1978,7 @@ var DotNS = class {
1956
1978
  console.log(` Already published \u2014 skipping`);
1957
1979
  return { status: "already-published" };
1958
1980
  }
1959
- const MAX_VERIFY_CHAIN_SECONDS = 30;
1981
+ const MAX_VERIFY_CHAIN_SECONDS = VERIFY_EFFECT_CHAIN_SECONDS;
1960
1982
  const PUBLISH_POLL_INTERVAL_MS = 2e3;
1961
1983
  const verifyEffect = async () => {
1962
1984
  const wrapper = this.clientWrapper;
@@ -2029,7 +2051,7 @@ var DotNS = class {
2029
2051
  console.log(` Not currently published \u2014 skipping`);
2030
2052
  return { status: "already-unpublished" };
2031
2053
  }
2032
- const MAX_VERIFY_CHAIN_SECONDS = 30;
2054
+ const MAX_VERIFY_CHAIN_SECONDS = VERIFY_EFFECT_CHAIN_SECONDS;
2033
2055
  const UNPUBLISH_POLL_INTERVAL_MS = 2e3;
2034
2056
  const verifyEffect = async () => {
2035
2057
  const wrapper = this.clientWrapper;
@@ -2737,9 +2759,11 @@ export {
2737
2759
  TX_NO_PROGRESS_MS,
2738
2760
  WS_HEARTBEAT_TIMEOUT_MS,
2739
2761
  DOTNS_TX_MAX_ATTEMPTS,
2762
+ VERIFY_EFFECT_CHAIN_SECONDS,
2740
2763
  classifyTxRetryDecision,
2741
2764
  dotnsRetryBackoffMs,
2742
2765
  shouldRetryTxAttempt,
2766
+ shouldRegateBeforeResign,
2743
2767
  makeRetryStatusFilter,
2744
2768
  DEFAULT_MNEMONIC,
2745
2769
  fetchNonce,
@@ -14,6 +14,7 @@ var environments_default = {
14
14
  name: "Preview",
15
15
  network: "testnet",
16
16
  description: "Product Preview net, used by Product Teams",
17
+ bulletinAutoAuthorize: true,
17
18
  e2eEligible: true,
18
19
  backend: "https://polkadot-app-stg.parity.io/",
19
20
  ipfs: "https://previewnet.substrate.dev",
@@ -105,6 +106,7 @@ var environments_default = {
105
106
  name: "Paseo Next v2",
106
107
  network: "testnet",
107
108
  description: "Next iteration of the Paseo Next testnet",
109
+ bulletinAutoAuthorize: true,
108
110
  e2eEligible: true,
109
111
  backend: "https://identity-backend-next.parity-testnet.parity.io",
110
112
  ipfs: "https://paseo-bulletin-next-ipfs.polkadot.io",
@@ -652,7 +654,8 @@ function resolveEndpoints(doc, envId) {
652
654
  autoAccountMapping: env.autoAccountMapping ?? false,
653
655
  contracts: env.contracts ?? {},
654
656
  nativeToEthRatio: BigInt(env.nativeToEthRatio ?? 1e6),
655
- ...env.registerStorageDeposit !== void 0 ? { registerStorageDeposit: BigInt(env.registerStorageDeposit) } : {}
657
+ ...env.registerStorageDeposit !== void 0 ? { registerStorageDeposit: BigInt(env.registerStorageDeposit) } : {},
658
+ bulletinAutoAuthorize: env.bulletinAutoAuthorize ?? false
656
659
  };
657
660
  }
658
661
  function getPopSelfServeConfig(doc, envId) {
@@ -3,10 +3,10 @@ import {
3
3
  } from "./chunk-TSPERKUS.js";
4
4
  import {
5
5
  VERSION
6
- } from "./chunk-5KRARO46.js";
6
+ } from "./chunk-UIDRWZWX.js";
7
7
  import {
8
8
  loadEnvironments
9
- } from "./chunk-QRKI6MMK.js";
9
+ } from "./chunk-JSYQ3JQS.js";
10
10
 
11
11
  // src/auth-config.ts
12
12
  import { existsSync, readdirSync } from "fs";
@@ -6,7 +6,7 @@ import * as path from "path";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@parity/product-deploy",
9
- version: "0.11.0-rc.3",
9
+ version: "0.11.0-rc.5",
10
10
  private: false,
11
11
  repository: {
12
12
  type: "git",
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-GRPLHUYC.js";
4
4
  import {
5
5
  DOT_DAPP_ID
6
- } from "./chunk-RVGCHRLK.js";
6
+ } from "./chunk-KBKS3V2V.js";
7
7
 
8
8
  // src/sss-allowance-cache.ts
9
9
  import { mkdir, readFile, writeFile, unlink } from "fs/promises";
@@ -100,17 +100,38 @@ function clampU32(n, name) {
100
100
  if (n > U32_MAX) throw new Error(`${name} (${n}) exceeds u32 max \u2014 split the deploy into smaller batches`);
101
101
  return Number(n);
102
102
  }
103
- async function ensureAuthorized(api, address, label) {
103
+ async function ensureAuthorized(api, address, label, opts = {}) {
104
104
  const [auth, currentBlock] = await Promise.all([
105
105
  api.query.TransactionStorage.Authorizations.getValue(Enum("Account", address)),
106
106
  api.query.System.Number.getValue()
107
107
  ]);
108
108
  if (isAuthorizationSufficient(auth, currentBlock)) return;
109
- const isTestnet = await detectTestnet(api);
110
109
  const who = `${label ?? "account"} (${address.slice(0, 8)}...)`;
110
+ if (opts.autoAuthorize) {
111
+ await cryptoWaitReady();
112
+ const keyring = new Keyring({ type: "sr25519" });
113
+ const alice = keyring.addFromUri("//Alice");
114
+ const signer = getPolkadotSigner(alice.publicKey, "Sr25519", (data) => alice.sign(data));
115
+ const grant = `${TOPUP_TRANSACTIONS} txs / ${Number(TOPUP_BYTES) / 1e6}MB`;
116
+ console.log(` Auto-authorizing ${who} on Bulletin (${grant})...`);
117
+ const tx = api.tx.TransactionStorage.authorize_account({
118
+ who: address,
119
+ transactions: clampU32(BigInt(TOPUP_TRANSACTIONS), "transactions"),
120
+ bytes: TOPUP_BYTES
121
+ });
122
+ const result = await tx.signAndSubmit(signer);
123
+ if (!result?.ok) {
124
+ throw new Error(
125
+ `Auto-authorization of Bulletin storage account ${who} failed: authorize_account was rejected. This environment is flagged bulletinAutoAuthorize, but //Alice may not hold authorizer rights on this chain.`
126
+ );
127
+ }
128
+ console.log(` Authorized: ${grant}`);
129
+ return;
130
+ }
131
+ const isTestnet = await detectTestnet(api);
111
132
  if (isTestnet) {
112
133
  throw new Error(
113
- `Bulletin storage account ${who} is not authorized (or its authorization expired). bulletin-deploy no longer self-authorizes on the Bulletin chain \u2014 request authorization for this account from the chain's authorizer (testnet faucet / personhood / pool bootstrap), then retry.`
134
+ `Bulletin storage account ${who} is not authorized (or its authorization expired). bulletin-deploy self-authorizes only on allow-listed testnets (environments.json bulletinAutoAuthorize) \u2014 request authorization for this account from the chain's authorizer (testnet faucet / personhood / pool bootstrap), then retry.`
114
135
  );
115
136
  }
116
137
  throw new Error(
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-5KRARO46.js";
3
+ } from "./chunk-UIDRWZWX.js";
4
4
 
5
5
  // src/version-check.ts
6
6
  import { execSync, execFileSync } from "child_process";
@@ -4,10 +4,10 @@ import {
4
4
  } from "./chunk-5FLTDWWP.js";
5
5
  import {
6
6
  DOT_PRODUCT_ID
7
- } from "./chunk-RVGCHRLK.js";
7
+ } from "./chunk-KBKS3V2V.js";
8
8
  import {
9
9
  DEFAULT_MNEMONIC
10
- } from "./chunk-PYGCOK67.js";
10
+ } from "./chunk-ITXKADHO.js";
11
11
 
12
12
  // src/deploy-actors.ts
13
13
  var DEFAULT_WORKER_SURI = DEFAULT_MNEMONIC;
@@ -23,8 +23,18 @@ async function resolveDeployActors(authClient, { suri, transferEnabled, isTestne
23
23
  if (sessionPresent && transferEnabled) {
24
24
  if (!suri && !isTestnet) throw new MainnetDefaultWorkerError();
25
25
  const worker2 = await resolveSigner(authClient, { suri: suri ?? DEFAULT_WORKER_SURI });
26
- const handle = await authClient.getSessionSigner();
27
- if (!handle) throw new Error("transfer mode active but no session resolved; pass --no-transfer-to-signedin-user.");
26
+ let handle = null;
27
+ try {
28
+ handle = await authClient.getSessionSigner();
29
+ } catch {
30
+ handle = null;
31
+ }
32
+ if (!handle) {
33
+ console.error(
34
+ "\u26A0 Found a login session on disk but couldn't load it \u2014 deploying without transfer (the worker signs directly). Log in again if you meant to transfer to your account."
35
+ );
36
+ return { worker: worker2 };
37
+ }
28
38
  try {
29
39
  return { worker: worker2, recipientH160: handle.addresses.productH160 };
30
40
  } finally {