bulletin-deploy 0.6.11 → 0.6.12

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.
@@ -2,10 +2,10 @@ import {
2
2
  classifyErrorArea,
3
3
  isInteractive,
4
4
  promptYesNo
5
- } from "./chunk-OKLWNC3M.js";
5
+ } from "./chunk-ELKNME32.js";
6
6
  import {
7
7
  VERSION
8
- } from "./chunk-2WNEX6D2.js";
8
+ } from "./chunk-VL2Q4CNZ.js";
9
9
  import "./chunk-QGM4M3NI.js";
10
10
 
11
11
  // src/bug-report.ts
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-2WNEX6D2.js";
3
+ } from "./chunk-VL2Q4CNZ.js";
4
4
 
5
5
  // src/version-check.ts
6
6
  import { execSync, execFileSync } from "child_process";
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  captureWarning,
6
6
  withSpan
7
- } from "./chunk-2WNEX6D2.js";
7
+ } from "./chunk-VL2Q4CNZ.js";
8
8
 
9
9
  // src/dotns.ts
10
10
  import crypto from "crypto";
@@ -49,6 +49,19 @@ var NATIVE_TO_ETH_RATIO = 1000000n;
49
49
  var CONNECTION_TIMEOUT_MS = 3e4;
50
50
  var OPERATION_TIMEOUT_MS = 3e5;
51
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
+ }
52
65
  var DEFAULT_MNEMONIC = "bottom drive obey lake curtain smoke basket hold race lonely fit walk";
53
66
  var _rpcIdCounter = 0;
54
67
  async function fetchNonce(rpc, ss58Address) {
@@ -302,10 +315,9 @@ var ReviveClientWrapper = class _ReviveClientWrapper {
302
315
  this.mappedAccounts.add(substrateAddress);
303
316
  return;
304
317
  }
305
- const mappingExtrinsic = this.client.tx.Revive.map_account();
306
318
  try {
307
- await this.signAndSubmitExtrinsic(mappingExtrinsic, signer, () => {
308
- });
319
+ await this.signAndSubmitWithRetry(() => this.client.tx.Revive.map_account(), signer, () => {
320
+ }, "Revive.map_account");
309
321
  this.mappedAccounts.add(substrateAddress);
310
322
  } catch (error) {
311
323
  const errorMessage = error?.message || String(error);
@@ -316,30 +328,50 @@ var ReviveClientWrapper = class _ReviveClientWrapper {
316
328
  throw error;
317
329
  }
318
330
  }
319
- signAndSubmitExtrinsic(extrinsic, signer, statusCallback, { nonceFallback } = {}) {
331
+ // `nonceFallback` is accepted for call-site compatibility but no longer
332
+ // used: a nonce-increment shortcut can't distinguish our tx from a
333
+ // concurrent/zombie tx on the same account (Alice's default mnemonic is
334
+ // widely shared on testnets), producing false-positive resolution. The
335
+ // chain-time deadline below is the reliability knob instead.
336
+ signAndSubmitExtrinsic(extrinsic, signer, statusCallback, _opts = {}) {
320
337
  return new Promise((resolve, reject) => {
321
338
  let settled = false;
322
- let noncePoller = null;
339
+ let deadlinePoller = null;
340
+ let sub;
323
341
  const finish = (fn) => (...args) => {
324
342
  if (!settled) {
325
343
  settled = true;
326
- clearTimeout(timer);
327
- if (noncePoller) clearInterval(noncePoller);
344
+ if (deadlinePoller) clearInterval(deadlinePoller);
328
345
  try {
329
- sub.unsubscribe();
346
+ sub?.unsubscribe();
330
347
  } catch {
331
348
  }
332
349
  fn(...args);
333
350
  }
334
351
  };
335
- const timer = setTimeout(() => {
336
- if (!settled) {
337
- statusCallback("failed");
338
- finish(reject)(new Error(`Transaction timed out after ${TX_TIMEOUT_MS / 1e3}s waiting for finalization`));
352
+ const startWallClockMs = Date.now();
353
+ let startChainTimeMs = null;
354
+ deadlinePoller = setInterval(async () => {
355
+ if (settled) return;
356
+ try {
357
+ if (Date.now() - startWallClockMs > TX_WALL_CLOCK_CEILING_MS) {
358
+ statusCallback("failed");
359
+ finish(reject)(new Error(`Transaction did not settle within ${TX_WALL_CLOCK_CEILING_MS / 1e3}s wall-clock (chain may be stalled)`));
360
+ return;
361
+ }
362
+ const chainNowMs = Number(await this.client.query.Timestamp.Now.getValue());
363
+ if (startChainTimeMs === null) startChainTimeMs = chainNowMs;
364
+ const chainElapsedMs = chainNowMs - startChainTimeMs;
365
+ if (chainElapsedMs > TX_CHAIN_TIME_BUDGET_MS) {
366
+ statusCallback("failed");
367
+ finish(reject)(new Error(`Transaction not included after ${Math.floor(chainElapsedMs / 1e3)}s of chain progress (budget=${TX_CHAIN_TIME_BUDGET_MS / 1e3}s)`));
368
+ return;
369
+ }
370
+ } catch {
339
371
  }
340
- }, TX_TIMEOUT_MS);
372
+ }, 6e3);
341
373
  try {
342
- var sub = extrinsic.signSubmitAndWatch(signer, { mortality: { mortal: true, period: 256 } }).subscribe({
374
+ sub = extrinsic.signSubmitAndWatch(signer, { mortality: { mortal: true, period: 256 } }).subscribe({
343
375
  next: (event) => {
344
376
  const transactionHash = event.txHash?.toString();
345
377
  switch (event.type) {
@@ -350,25 +382,7 @@ var ReviveClientWrapper = class _ReviveClientWrapper {
350
382
  statusCallback("broadcasting");
351
383
  break;
352
384
  case "txBestBlocksState":
353
- if (event.found) {
354
- statusCallback("included");
355
- if (nonceFallback && !noncePoller) {
356
- noncePoller = setInterval(async () => {
357
- if (settled) {
358
- clearInterval(noncePoller);
359
- return;
360
- }
361
- try {
362
- const currentNonce = await fetchNonce(nonceFallback.rpc, nonceFallback.senderSS58);
363
- if (currentNonce > nonceFallback.expectedNonce) {
364
- statusCallback("finalized");
365
- finish(resolve)("nonce-confirmed");
366
- }
367
- } catch {
368
- }
369
- }, 6e3);
370
- }
371
- }
385
+ if (event.found) statusCallback("included");
372
386
  break;
373
387
  case "finalized":
374
388
  if (event.dispatchError) {
@@ -397,6 +411,27 @@ var ReviveClientWrapper = class _ReviveClientWrapper {
397
411
  }
398
412
  });
399
413
  }
414
+ // Wrapper around signAndSubmitExtrinsic that retries on transient errors
415
+ // (Stale, Future, connection, timeout — see classifyTxRetryDecision). The
416
+ // extrinsic is rebuilt per attempt via `buildExtrinsic` so polkadot-api
417
+ // assigns a fresh nonce + mortality window — same pattern as
418
+ // pool.ts:submitAliceTxWithRetry. `label` is the human-readable op name
419
+ // used in retry log lines.
420
+ async signAndSubmitWithRetry(buildExtrinsic, signer, statusCallback, label, opts = {}) {
421
+ let lastError;
422
+ for (let attempt = 1; attempt <= DOTNS_TX_MAX_ATTEMPTS; attempt++) {
423
+ try {
424
+ return await this.signAndSubmitExtrinsic(buildExtrinsic(), signer, statusCallback, opts);
425
+ } catch (e) {
426
+ lastError = e;
427
+ const decision = classifyTxRetryDecision(e);
428
+ if (decision === "abort" || attempt === DOTNS_TX_MAX_ATTEMPTS) break;
429
+ const short = (e?.message ?? String(e)).slice(0, 80);
430
+ console.log(` ${label}: attempt ${attempt}/${DOTNS_TX_MAX_ATTEMPTS} failed (${short}), retrying...`);
431
+ }
432
+ }
433
+ throw lastError instanceof Error ? lastError : new Error(String(lastError));
434
+ }
400
435
  async submitTransaction(contractAddress, value, encodedData, signerSubstrateAddress, signer, statusCallback, { rpc, useNoncePolling } = {}) {
401
436
  await this.ensureAccountMapped(signerSubstrateAddress, signer);
402
437
  const gasEstimate = await this.estimateGasForCall(signerSubstrateAddress, contractAddress, value, encodedData);
@@ -405,7 +440,7 @@ var ReviveClientWrapper = class _ReviveClientWrapper {
405
440
  const minimumStorageDeposit = 2000000000000n;
406
441
  let storageDepositLimit = gasEstimate.storageDeposit === 0n ? minimumStorageDeposit : gasEstimate.storageDeposit * 120n / 100n;
407
442
  if (storageDepositLimit < minimumStorageDeposit) storageDepositLimit = minimumStorageDeposit;
408
- const callExtrinsic = this.client.tx.Revive.call({ dest: Binary.fromHex(contractAddress), value, weight_limit: weightLimit, storage_deposit_limit: storageDepositLimit, data: Binary.fromHex(encodedData) });
443
+ const buildExtrinsic = () => this.client.tx.Revive.call({ dest: Binary.fromHex(contractAddress), value, weight_limit: weightLimit, storage_deposit_limit: storageDepositLimit, data: Binary.fromHex(encodedData) });
409
444
  let nonceFallback;
410
445
  if (useNoncePolling && rpc) {
411
446
  try {
@@ -414,7 +449,7 @@ var ReviveClientWrapper = class _ReviveClientWrapper {
414
449
  } catch {
415
450
  }
416
451
  }
417
- return await this.signAndSubmitExtrinsic(callExtrinsic, signer, statusCallback, { nonceFallback });
452
+ return await this.signAndSubmitWithRetry(buildExtrinsic, signer, statusCallback, "Revive.call", { nonceFallback });
418
453
  }
419
454
  };
420
455
  var DotNS = class {
@@ -441,7 +476,7 @@ var DotNS = class {
441
476
  try {
442
477
  console.log(` Connecting to: ${rpc}`);
443
478
  this.rpc = rpc;
444
- this.client = createClient(getWsProvider(rpc));
479
+ this.client = createClient(getWsProvider(rpc, { heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS }));
445
480
  const unsafeApi = this.client.getUnsafeApi();
446
481
  this.clientWrapper = new ReviveClientWrapper(unsafeApi);
447
482
  if (options.signer && options.signerAddress) {
@@ -911,6 +946,11 @@ export {
911
946
  CONNECTION_TIMEOUT_MS,
912
947
  OPERATION_TIMEOUT_MS,
913
948
  TX_TIMEOUT_MS,
949
+ TX_CHAIN_TIME_BUDGET_MS,
950
+ TX_WALL_CLOCK_CEILING_MS,
951
+ WS_HEARTBEAT_TIMEOUT_MS,
952
+ DOTNS_TX_MAX_ATTEMPTS,
953
+ classifyTxRetryDecision,
914
954
  DEFAULT_MNEMONIC,
915
955
  fetchNonce,
916
956
  ProofOfPersonhoodStatus,
@@ -5,7 +5,7 @@ import {
5
5
  fetchNonce,
6
6
  popStatusName,
7
7
  validateDomainLabel
8
- } from "./chunk-BIGY46US.js";
8
+ } from "./chunk-M6WNBKCT.js";
9
9
  import {
10
10
  merkleizeJS
11
11
  } from "./chunk-GZ5UUECB.js";
@@ -27,7 +27,7 @@ import {
27
27
  truncateAddress,
28
28
  withDeploySpan,
29
29
  withSpan
30
- } from "./chunk-2WNEX6D2.js";
30
+ } from "./chunk-VL2Q4CNZ.js";
31
31
 
32
32
  // src/deploy.ts
33
33
  import { Buffer } from "buffer";
@@ -7,7 +7,7 @@ import * as path from "path";
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "bulletin-deploy",
10
- version: "0.6.11",
10
+ version: "0.6.12",
11
11
  private: false,
12
12
  repository: {
13
13
  type: "git",
package/dist/deploy.js CHANGED
@@ -23,11 +23,11 @@ import {
23
23
  storeChunkedContent,
24
24
  storeDirectory,
25
25
  storeFile
26
- } from "./chunk-VT4THP4H.js";
27
- import "./chunk-BIGY46US.js";
26
+ } from "./chunk-RECT3AEW.js";
27
+ import "./chunk-M6WNBKCT.js";
28
28
  import "./chunk-GZ5UUECB.js";
29
29
  import "./chunk-JHNW2EKY.js";
30
- import "./chunk-2WNEX6D2.js";
30
+ import "./chunk-VL2Q4CNZ.js";
31
31
  import "./chunk-QGM4M3NI.js";
32
32
  export {
33
33
  DEFAULT_BULLETIN_RPC,
package/dist/dotns.d.ts CHANGED
@@ -54,6 +54,11 @@ declare const NATIVE_TO_ETH_RATIO: bigint;
54
54
  declare const CONNECTION_TIMEOUT_MS: number;
55
55
  declare const OPERATION_TIMEOUT_MS: number;
56
56
  declare const TX_TIMEOUT_MS: number;
57
+ declare const TX_CHAIN_TIME_BUDGET_MS: number;
58
+ declare const TX_WALL_CLOCK_CEILING_MS: number;
59
+ declare const WS_HEARTBEAT_TIMEOUT_MS: number;
60
+ declare const DOTNS_TX_MAX_ATTEMPTS: number;
61
+ declare function classifyTxRetryDecision(err: unknown): "retry" | "abort";
57
62
  declare const DEFAULT_MNEMONIC: string;
58
63
  declare function fetchNonce(rpc: string, ss58Address: string): Promise<number>;
59
64
  declare const ProofOfPersonhoodStatus: {
@@ -95,7 +100,14 @@ declare class ReviveClientWrapper {
95
100
  estimateGasForCall(originSubstrateAddress: string, contractAddress: string, value: bigint, encodedData: string): Promise<any>;
96
101
  checkIfAccountMapped(substrateAddress: string): Promise<boolean>;
97
102
  ensureAccountMapped(substrateAddress: string, signer: PolkadotSigner): Promise<void>;
98
- signAndSubmitExtrinsic(extrinsic: any, signer: PolkadotSigner, statusCallback: (status: string) => void, { nonceFallback }?: {
103
+ signAndSubmitExtrinsic(extrinsic: any, signer: PolkadotSigner, statusCallback: (status: string) => void, _opts?: {
104
+ nonceFallback?: {
105
+ rpc: string;
106
+ senderSS58: string;
107
+ expectedNonce: number;
108
+ };
109
+ }): Promise<string>;
110
+ signAndSubmitWithRetry(buildExtrinsic: () => any, signer: PolkadotSigner, statusCallback: (status: string) => void, label: string, opts?: {
99
111
  nonceFallback?: {
100
112
  rpc: string;
101
113
  senderSS58: string;
@@ -156,4 +168,4 @@ declare class DotNS {
156
168
  }
157
169
  declare const dotns: DotNS;
158
170
 
159
- export { CONNECTION_TIMEOUT_MS, CONTRACTS, DECIMALS, DEFAULT_MNEMONIC, DOT_NODE, DotNS, type DotNSConnectOptions, type DotnsPreflightResult, NATIVE_TO_ETH_RATIO, OPERATION_TIMEOUT_MS, type OwnershipResult, type PriceValidationResult, ProofOfPersonhoodStatus, RPC_ENDPOINTS, TX_TIMEOUT_MS, canRegister, classifyDotnsLabel, computeDomainTokenId, convertWeiToNative, countTrailingDigits, dotns, fetchNonce, isCommitmentMature, parseProofOfPersonhoodStatus, popStatusName, sanitizeDomainLabel, simulateUserStatus, stripTrailingDigits, validateDomainLabel };
171
+ export { CONNECTION_TIMEOUT_MS, CONTRACTS, DECIMALS, DEFAULT_MNEMONIC, DOTNS_TX_MAX_ATTEMPTS, DOT_NODE, DotNS, type DotNSConnectOptions, type DotnsPreflightResult, NATIVE_TO_ETH_RATIO, OPERATION_TIMEOUT_MS, type OwnershipResult, type PriceValidationResult, ProofOfPersonhoodStatus, RPC_ENDPOINTS, TX_CHAIN_TIME_BUDGET_MS, TX_TIMEOUT_MS, TX_WALL_CLOCK_CEILING_MS, WS_HEARTBEAT_TIMEOUT_MS, canRegister, classifyDotnsLabel, classifyTxRetryDecision, computeDomainTokenId, convertWeiToNative, countTrailingDigits, dotns, fetchNonce, isCommitmentMature, parseProofOfPersonhoodStatus, popStatusName, sanitizeDomainLabel, simulateUserStatus, stripTrailingDigits, validateDomainLabel };
package/dist/dotns.js CHANGED
@@ -3,15 +3,20 @@ import {
3
3
  CONTRACTS,
4
4
  DECIMALS,
5
5
  DEFAULT_MNEMONIC,
6
+ DOTNS_TX_MAX_ATTEMPTS,
6
7
  DOT_NODE,
7
8
  DotNS,
8
9
  NATIVE_TO_ETH_RATIO,
9
10
  OPERATION_TIMEOUT_MS,
10
11
  ProofOfPersonhoodStatus,
11
12
  RPC_ENDPOINTS,
13
+ TX_CHAIN_TIME_BUDGET_MS,
12
14
  TX_TIMEOUT_MS,
15
+ TX_WALL_CLOCK_CEILING_MS,
16
+ WS_HEARTBEAT_TIMEOUT_MS,
13
17
  canRegister,
14
18
  classifyDotnsLabel,
19
+ classifyTxRetryDecision,
15
20
  computeDomainTokenId,
16
21
  convertWeiToNative,
17
22
  countTrailingDigits,
@@ -24,24 +29,29 @@ import {
24
29
  simulateUserStatus,
25
30
  stripTrailingDigits,
26
31
  validateDomainLabel
27
- } from "./chunk-BIGY46US.js";
32
+ } from "./chunk-M6WNBKCT.js";
28
33
  import "./chunk-JHNW2EKY.js";
29
- import "./chunk-2WNEX6D2.js";
34
+ import "./chunk-VL2Q4CNZ.js";
30
35
  import "./chunk-QGM4M3NI.js";
31
36
  export {
32
37
  CONNECTION_TIMEOUT_MS,
33
38
  CONTRACTS,
34
39
  DECIMALS,
35
40
  DEFAULT_MNEMONIC,
41
+ DOTNS_TX_MAX_ATTEMPTS,
36
42
  DOT_NODE,
37
43
  DotNS,
38
44
  NATIVE_TO_ETH_RATIO,
39
45
  OPERATION_TIMEOUT_MS,
40
46
  ProofOfPersonhoodStatus,
41
47
  RPC_ENDPOINTS,
48
+ TX_CHAIN_TIME_BUDGET_MS,
42
49
  TX_TIMEOUT_MS,
50
+ TX_WALL_CLOCK_CEILING_MS,
51
+ WS_HEARTBEAT_TIMEOUT_MS,
43
52
  canRegister,
44
53
  classifyDotnsLabel,
54
+ classifyTxRetryDecision,
45
55
  computeDomainTokenId,
46
56
  convertWeiToNative,
47
57
  countTrailingDigits,
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  deploy
3
- } from "./chunk-VT4THP4H.js";
3
+ } from "./chunk-RECT3AEW.js";
4
4
  import {
5
5
  DotNS
6
- } from "./chunk-BIGY46US.js";
6
+ } from "./chunk-M6WNBKCT.js";
7
7
  import {
8
8
  merkleizeJS
9
9
  } from "./chunk-GZ5UUECB.js";
@@ -14,7 +14,7 @@ import {
14
14
  fetchPoolAuthorizations,
15
15
  selectAccount
16
16
  } from "./chunk-JHNW2EKY.js";
17
- import "./chunk-2WNEX6D2.js";
17
+ import "./chunk-VL2Q4CNZ.js";
18
18
  import "./chunk-QGM4M3NI.js";
19
19
  export {
20
20
  DotNS,
package/dist/telemetry.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  truncateAddress,
18
18
  withDeploySpan,
19
19
  withSpan
20
- } from "./chunk-2WNEX6D2.js";
20
+ } from "./chunk-VL2Q4CNZ.js";
21
21
  import "./chunk-QGM4M3NI.js";
22
22
  export {
23
23
  VERSION,
@@ -8,8 +8,8 @@ import {
8
8
  isPreReleaseVersion,
9
9
  preReleaseWarning,
10
10
  promptYesNo
11
- } from "./chunk-OKLWNC3M.js";
12
- import "./chunk-2WNEX6D2.js";
11
+ } from "./chunk-ELKNME32.js";
12
+ import "./chunk-VL2Q4CNZ.js";
13
13
  import "./chunk-QGM4M3NI.js";
14
14
  export {
15
15
  assessVersion,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulletin-deploy",
3
- "version": "0.6.11",
3
+ "version": "0.6.12",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",