midnight-wallet-cli 0.1.6 → 0.1.7

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.
@@ -172,7 +172,7 @@ var init_network = __esm(() => {
172
172
  });
173
173
 
174
174
  // src/lib/constants.ts
175
- var GENESIS_SEED = "0000000000000000000000000000000000000000000000000000000000000001", NATIVE_TOKEN_TYPE = "0000000000000000000000000000000000000000000000000000000000000000", TOKEN_DECIMALS = 6, TOKEN_MULTIPLIER = 1e6, DUST_COST_OVERHEAD = 1000000000000n, DUST_FEE_BLOCKS_MARGIN = 5, SYNC_TIMEOUT_MS = 300000, PRE_SEND_SYNC_TIMEOUT_MS = 1e4, DUST_TIMEOUT_MS = 120000, PROOF_TIMEOUT_MS = 300000, BALANCE_CHECK_TIMEOUT_MS = 60000, TX_TTL_MINUTES = 10, MAX_RETRY_ATTEMPTS = 3, STALE_UTXO_ERROR_CODE = 115, MIDNIGHT_DIR = ".midnight", DEFAULT_WALLET_FILENAME = "wallet.json", DEFAULT_CONFIG_FILENAME = "config.json", DIR_MODE = 448, FILE_MODE = 384, LOCALNET_DIR_NAME = "localnet";
175
+ var GENESIS_SEED = "0000000000000000000000000000000000000000000000000000000000000001", NATIVE_TOKEN_TYPE = "0000000000000000000000000000000000000000000000000000000000000000", TOKEN_DECIMALS = 6, TOKEN_MULTIPLIER = 1e6, DUST_COST_OVERHEAD = 1000000000000n, DUST_FEE_BLOCKS_MARGIN = 5, SYNC_TIMEOUT_MS = 300000, PRE_SEND_SYNC_TIMEOUT_MS = 1e4, DUST_TIMEOUT_MS = 120000, PROOF_TIMEOUT_MS = 300000, BALANCE_CHECK_TIMEOUT_MS = 60000, TX_TTL_MINUTES = 10, MAX_RETRY_ATTEMPTS = 3, DUST_REGISTRATION_TIMEOUT_MS = 600000, DUST_REGISTRATION_RETRY_DELAY_MS = 15000, STALE_UTXO_ERROR_CODE = 115, MIDNIGHT_DIR = ".midnight", DEFAULT_WALLET_FILENAME = "wallet.json", DEFAULT_CONFIG_FILENAME = "config.json", DIR_MODE = 448, FILE_MODE = 384, LOCALNET_DIR_NAME = "localnet";
176
176
 
177
177
  // src/lib/cli-config.ts
178
178
  import * as fs from "fs";
@@ -1243,27 +1243,98 @@ function validateRecipientAddress(address, networkConfig) {
1243
1243
  ` + `Expected a bech32m address for network "${networkConfig.networkId}"`);
1244
1244
  }
1245
1245
  }
1246
+ function isTransactionRejectedError(err) {
1247
+ let current = err;
1248
+ while (current) {
1249
+ const msg = String(current?.message ?? "").toLowerCase();
1250
+ if (msg.includes("submission error"))
1251
+ return true;
1252
+ if (msg.includes("transaction") && msg.includes("invalid"))
1253
+ return true;
1254
+ if (msg.includes("138"))
1255
+ return true;
1256
+ const tag = current?._tag;
1257
+ if (tag === "TransactionInvalidError" || tag === "SubmissionError")
1258
+ return true;
1259
+ current = current.cause;
1260
+ }
1261
+ return false;
1262
+ }
1263
+ function formatElapsed(ms) {
1264
+ const totalSeconds = Math.round(ms / 1000);
1265
+ if (totalSeconds < 60)
1266
+ return `${totalSeconds}s`;
1267
+ const minutes = Math.floor(totalSeconds / 60);
1268
+ const seconds = totalSeconds % 60;
1269
+ return `${minutes}m ${seconds}s`;
1270
+ }
1271
+ async function submitDustRegistration(bundle, dustUtxos, dustReceiverAddress) {
1272
+ const ttl = new Date(Date.now() + TX_TTL_MINUTES * 60 * 1000);
1273
+ await bundle.facade.dust.waitForSyncedState();
1274
+ const unprovenTx = await bundle.facade.dust.createDustGenerationTransaction(new Date, ttl, dustUtxos, bundle.keystore.getPublicKey(), dustReceiverAddress);
1275
+ const intent = unprovenTx.intents?.get(1);
1276
+ if (!intent) {
1277
+ throw new Error("Dust generation intent not found on transaction");
1278
+ }
1279
+ const signature = bundle.keystore.signData(intent.signatureData(1));
1280
+ const signedTx = await bundle.facade.dust.addDustGenerationSignature(unprovenTx, signature);
1281
+ const finalized = await bundle.facade.finalizeTransaction(signedTx);
1282
+ return await bundle.facade.submitTransaction(finalized);
1283
+ }
1284
+ async function registerNightUtxos(bundle, dustUtxos, dustReceiverAddress, onStatus) {
1285
+ const startTime = Date.now();
1286
+ const deadline = startTime + DUST_REGISTRATION_TIMEOUT_MS;
1287
+ let lastError;
1288
+ const originalWarn = console.warn;
1289
+ const originalError = console.error;
1290
+ const hasRpcNoise = (args) => args.some((a) => String(a).includes("RPC-CORE"));
1291
+ const suppressRpcNoise = () => {
1292
+ console.warn = (...args) => {
1293
+ if (hasRpcNoise(args))
1294
+ return;
1295
+ originalWarn(...args);
1296
+ };
1297
+ console.error = (...args) => {
1298
+ if (hasRpcNoise(args))
1299
+ return;
1300
+ originalError(...args);
1301
+ };
1302
+ };
1303
+ const restoreConsole = () => {
1304
+ console.warn = originalWarn;
1305
+ console.error = originalError;
1306
+ };
1307
+ suppressRpcNoise();
1308
+ try {
1309
+ while (Date.now() < deadline) {
1310
+ try {
1311
+ return await submitDustRegistration(bundle, dustUtxos, dustReceiverAddress);
1312
+ } catch (err) {
1313
+ lastError = err;
1314
+ if (isTransactionRejectedError(err) && Date.now() + DUST_REGISTRATION_RETRY_DELAY_MS < deadline) {
1315
+ const elapsed = formatElapsed(Date.now() - startTime);
1316
+ onStatus?.(`Waiting for dust generation capacity (${elapsed} elapsed, ~5 min on fresh wallets)...`);
1317
+ await new Promise((resolve4) => setTimeout(resolve4, DUST_REGISTRATION_RETRY_DELAY_MS));
1318
+ continue;
1319
+ }
1320
+ throw err;
1321
+ }
1322
+ }
1323
+ throw lastError ?? new Error("Dust registration timed out");
1324
+ } finally {
1325
+ restoreConsole();
1326
+ }
1327
+ }
1246
1328
  async function ensureDust(bundle, onDust) {
1247
1329
  const state = await rx2.firstValueFrom(bundle.facade.state().pipe(rx2.filter((s) => s.isSynced)));
1248
1330
  const nightUtxos = state.unshielded.availableCoins.filter((coin) => coin.meta?.registeredForDustGeneration !== true);
1249
1331
  if (nightUtxos.length > 0) {
1250
1332
  onDust?.(`Registering ${nightUtxos.length} UTXO(s) for dust generation...`);
1251
- const ttl = new Date(Date.now() + TX_TTL_MINUTES * 60 * 1000);
1252
- const dustReceiverAddress = state.dust.dustAddress;
1253
1333
  const dustUtxos = nightUtxos.map((coin) => ({
1254
1334
  ...coin.utxo,
1255
1335
  ctime: new Date(coin.meta.ctime)
1256
1336
  }));
1257
- await bundle.facade.dust.waitForSyncedState();
1258
- const unprovenTx = await bundle.facade.dust.createDustGenerationTransaction(new Date, ttl, dustUtxos, bundle.keystore.getPublicKey(), dustReceiverAddress);
1259
- const intent = unprovenTx.intents?.get(1);
1260
- if (!intent) {
1261
- throw new Error("Dust generation intent not found on transaction");
1262
- }
1263
- const signature = bundle.keystore.signData(intent.signatureData(1));
1264
- const signedTx = await bundle.facade.dust.addDustGenerationSignature(unprovenTx, signature);
1265
- const finalized = await bundle.facade.finalizeTransaction(signedTx);
1266
- await bundle.facade.submitTransaction(finalized);
1337
+ await registerNightUtxos(bundle, dustUtxos, state.dust.dustAddress, onDust);
1267
1338
  } else if (state.dust.availableCoins.length > 0) {
1268
1339
  onDust?.("Dust available");
1269
1340
  return;
@@ -1274,7 +1345,7 @@ async function ensureDust(bundle, onDust) {
1274
1345
  await rx2.firstValueFrom(bundle.facade.state().pipe(rx2.throttleTime(5000), rx2.filter((s) => s.isSynced), rx2.filter((s) => s.dust.walletBalance(new Date) > 0n), rx2.timeout(DUST_TIMEOUT_MS)));
1275
1346
  onDust?.("Dust available");
1276
1347
  }
1277
- async function buildAndSubmitTransfer(bundle, recipientAddress, amount, onProving, onSubmitting) {
1348
+ async function buildAndSubmitTransfer(bundle, recipientAddress, amount, onProving, onSubmitting, onDust) {
1278
1349
  let lastError;
1279
1350
  for (let attempt = 1;attempt <= MAX_RETRY_ATTEMPTS; attempt++) {
1280
1351
  try {
@@ -1311,6 +1382,12 @@ async function buildAndSubmitTransfer(bundle, recipientAddress, amount, onProvin
1311
1382
  if (isStaleUtxo && attempt < MAX_RETRY_ATTEMPTS) {
1312
1383
  continue;
1313
1384
  }
1385
+ const isDustInsufficient = err?.message?.toLowerCase().includes("not enough dust") || err?.message?.toLowerCase().includes("dust generated");
1386
+ if (isDustInsufficient && attempt < MAX_RETRY_ATTEMPTS) {
1387
+ onDust?.("Waiting for more dust to accumulate...");
1388
+ await new Promise((resolve4) => setTimeout(resolve4, DUST_REGISTRATION_RETRY_DELAY_MS));
1389
+ continue;
1390
+ }
1314
1391
  throw err;
1315
1392
  }
1316
1393
  }
@@ -1361,7 +1438,7 @@ async function executeTransfer(params) {
1361
1438
  await ensureDust(bundle, onDust);
1362
1439
  if (signal?.aborted)
1363
1440
  throw new Error("Operation cancelled");
1364
- const txHash = await buildAndSubmitTransfer(bundle, recipientAddress, amount, onProving, onSubmitting);
1441
+ const txHash = await buildAndSubmitTransfer(bundle, recipientAddress, amount, onProving, onSubmitting, onDust);
1365
1442
  return { txHash, amountMicroNight: amount };
1366
1443
  } finally {
1367
1444
  signal?.removeEventListener("abort", onAbort);
@@ -1465,7 +1542,13 @@ async function airdropCommand(args, signal) {
1465
1542
  process.stderr.write(`
1466
1543
  ` + divider() + `
1467
1544
  `);
1468
- process.stderr.write(dim(" Verify: midnight balance") + `
1545
+ process.stderr.write(dim(" Verify: midnight balance") + `
1546
+ `);
1547
+ process.stderr.write(dim(" Register dust: midnight dust register") + `
1548
+ `);
1549
+ process.stderr.write(dim(" Note: Dust generation takes a few minutes on a fresh wallet.") + `
1550
+ `);
1551
+ process.stderr.write(dim(" It will happen automatically on your first transfer.") + `
1469
1552
 
1470
1553
  `);
1471
1554
  } catch (err) {
@@ -1684,22 +1767,13 @@ async function dustRegister(bundle, networkName, jsonMode, signal, warningRef) {
1684
1767
  spinner.update("All UTXOs already registered, waiting for dust generation...");
1685
1768
  } else {
1686
1769
  spinner.update(`Registering ${nightUtxos.length} UTXO(s) for dust generation...`);
1687
- const ttl = new Date(Date.now() + TX_TTL_MINUTES * 60 * 1000);
1688
- const dustReceiverAddress = state.dust.dustAddress;
1689
1770
  const dustUtxos = nightUtxos.map((coin) => ({
1690
1771
  ...coin.utxo,
1691
1772
  ctime: new Date(coin.meta.ctime)
1692
1773
  }));
1693
- await bundle.facade.dust.waitForSyncedState();
1694
- const unprovenTx = await bundle.facade.dust.createDustGenerationTransaction(new Date, ttl, dustUtxos, bundle.keystore.getPublicKey(), dustReceiverAddress);
1695
- const intent = unprovenTx.intents?.get(1);
1696
- if (!intent) {
1697
- throw new Error("Dust generation intent not found on transaction");
1698
- }
1699
- const signature = bundle.keystore.signData(intent.signatureData(1));
1700
- const signedTx = await bundle.facade.dust.addDustGenerationSignature(unprovenTx, signature);
1701
- const finalized = await bundle.facade.finalizeTransaction(signedTx);
1702
- txHash = await bundle.facade.submitTransaction(finalized);
1774
+ txHash = await registerNightUtxos(bundle, dustUtxos, state.dust.dustAddress, (status) => {
1775
+ spinner.update(status);
1776
+ });
1703
1777
  spinner.update(`Registration submitted (${txHash.slice(0, 12)}...), waiting for dust...`);
1704
1778
  }
1705
1779
  if (signal?.aborted)
@@ -1795,6 +1869,7 @@ var init_dust = __esm(() => {
1795
1869
  init_wallet_config();
1796
1870
  init_resolve_network();
1797
1871
  init_facade();
1872
+ init_transfer();
1798
1873
  init_format();
1799
1874
  init_spinner();
1800
1875
  });
@@ -2292,7 +2367,7 @@ function classifyError(err) {
2292
2367
  // package.json
2293
2368
  var package_default = {
2294
2369
  name: "midnight-wallet-cli",
2295
- version: "0.1.6",
2370
+ version: "0.1.7",
2296
2371
  type: "module",
2297
2372
  description: "Git-style CLI wallet for the Midnight blockchain",
2298
2373
  license: "Apache-2.0",
package/dist/wallet.js CHANGED
@@ -69,7 +69,7 @@ function requireFlag(args, name, description) {
69
69
  }
70
70
 
71
71
  // src/lib/constants.ts
72
- var GENESIS_SEED = "0000000000000000000000000000000000000000000000000000000000000001", NATIVE_TOKEN_TYPE = "0000000000000000000000000000000000000000000000000000000000000000", TOKEN_DECIMALS = 6, TOKEN_MULTIPLIER = 1e6, DUST_COST_OVERHEAD = 1000000000000n, DUST_FEE_BLOCKS_MARGIN = 5, SYNC_TIMEOUT_MS = 300000, PRE_SEND_SYNC_TIMEOUT_MS = 1e4, DUST_TIMEOUT_MS = 120000, PROOF_TIMEOUT_MS = 300000, BALANCE_CHECK_TIMEOUT_MS = 60000, TX_TTL_MINUTES = 10, MAX_RETRY_ATTEMPTS = 3, STALE_UTXO_ERROR_CODE = 115, MIDNIGHT_DIR = ".midnight", DEFAULT_WALLET_FILENAME = "wallet.json", DEFAULT_CONFIG_FILENAME = "config.json", DIR_MODE = 448, FILE_MODE = 384, LOCALNET_DIR_NAME = "localnet";
72
+ var GENESIS_SEED = "0000000000000000000000000000000000000000000000000000000000000001", NATIVE_TOKEN_TYPE = "0000000000000000000000000000000000000000000000000000000000000000", TOKEN_DECIMALS = 6, TOKEN_MULTIPLIER = 1e6, DUST_COST_OVERHEAD = 1000000000000n, DUST_FEE_BLOCKS_MARGIN = 5, SYNC_TIMEOUT_MS = 300000, PRE_SEND_SYNC_TIMEOUT_MS = 1e4, DUST_TIMEOUT_MS = 120000, PROOF_TIMEOUT_MS = 300000, BALANCE_CHECK_TIMEOUT_MS = 60000, TX_TTL_MINUTES = 10, MAX_RETRY_ATTEMPTS = 3, DUST_REGISTRATION_TIMEOUT_MS = 600000, DUST_REGISTRATION_RETRY_DELAY_MS = 15000, STALE_UTXO_ERROR_CODE = 115, MIDNIGHT_DIR = ".midnight", DEFAULT_WALLET_FILENAME = "wallet.json", DEFAULT_CONFIG_FILENAME = "config.json", DIR_MODE = 448, FILE_MODE = 384, LOCALNET_DIR_NAME = "localnet";
73
73
 
74
74
  // src/ui/colors.ts
75
75
  function isColorEnabled() {
@@ -315,7 +315,7 @@ var package_default;
315
315
  var init_package = __esm(() => {
316
316
  package_default = {
317
317
  name: "midnight-wallet-cli",
318
- version: "0.1.6",
318
+ version: "0.1.7",
319
319
  type: "module",
320
320
  description: "Git-style CLI wallet for the Midnight blockchain",
321
321
  license: "Apache-2.0",
@@ -1492,27 +1492,98 @@ function validateRecipientAddress(address, networkConfig) {
1492
1492
  ` + `Expected a bech32m address for network "${networkConfig.networkId}"`);
1493
1493
  }
1494
1494
  }
1495
+ function isTransactionRejectedError(err) {
1496
+ let current = err;
1497
+ while (current) {
1498
+ const msg = String(current?.message ?? "").toLowerCase();
1499
+ if (msg.includes("submission error"))
1500
+ return true;
1501
+ if (msg.includes("transaction") && msg.includes("invalid"))
1502
+ return true;
1503
+ if (msg.includes("138"))
1504
+ return true;
1505
+ const tag = current?._tag;
1506
+ if (tag === "TransactionInvalidError" || tag === "SubmissionError")
1507
+ return true;
1508
+ current = current.cause;
1509
+ }
1510
+ return false;
1511
+ }
1512
+ function formatElapsed(ms) {
1513
+ const totalSeconds = Math.round(ms / 1000);
1514
+ if (totalSeconds < 60)
1515
+ return `${totalSeconds}s`;
1516
+ const minutes = Math.floor(totalSeconds / 60);
1517
+ const seconds = totalSeconds % 60;
1518
+ return `${minutes}m ${seconds}s`;
1519
+ }
1520
+ async function submitDustRegistration(bundle, dustUtxos, dustReceiverAddress) {
1521
+ const ttl = new Date(Date.now() + TX_TTL_MINUTES * 60 * 1000);
1522
+ await bundle.facade.dust.waitForSyncedState();
1523
+ const unprovenTx = await bundle.facade.dust.createDustGenerationTransaction(new Date, ttl, dustUtxos, bundle.keystore.getPublicKey(), dustReceiverAddress);
1524
+ const intent = unprovenTx.intents?.get(1);
1525
+ if (!intent) {
1526
+ throw new Error("Dust generation intent not found on transaction");
1527
+ }
1528
+ const signature = bundle.keystore.signData(intent.signatureData(1));
1529
+ const signedTx = await bundle.facade.dust.addDustGenerationSignature(unprovenTx, signature);
1530
+ const finalized = await bundle.facade.finalizeTransaction(signedTx);
1531
+ return await bundle.facade.submitTransaction(finalized);
1532
+ }
1533
+ async function registerNightUtxos(bundle, dustUtxos, dustReceiverAddress, onStatus) {
1534
+ const startTime = Date.now();
1535
+ const deadline = startTime + DUST_REGISTRATION_TIMEOUT_MS;
1536
+ let lastError;
1537
+ const originalWarn = console.warn;
1538
+ const originalError = console.error;
1539
+ const hasRpcNoise = (args) => args.some((a) => String(a).includes("RPC-CORE"));
1540
+ const suppressRpcNoise = () => {
1541
+ console.warn = (...args) => {
1542
+ if (hasRpcNoise(args))
1543
+ return;
1544
+ originalWarn(...args);
1545
+ };
1546
+ console.error = (...args) => {
1547
+ if (hasRpcNoise(args))
1548
+ return;
1549
+ originalError(...args);
1550
+ };
1551
+ };
1552
+ const restoreConsole = () => {
1553
+ console.warn = originalWarn;
1554
+ console.error = originalError;
1555
+ };
1556
+ suppressRpcNoise();
1557
+ try {
1558
+ while (Date.now() < deadline) {
1559
+ try {
1560
+ return await submitDustRegistration(bundle, dustUtxos, dustReceiverAddress);
1561
+ } catch (err) {
1562
+ lastError = err;
1563
+ if (isTransactionRejectedError(err) && Date.now() + DUST_REGISTRATION_RETRY_DELAY_MS < deadline) {
1564
+ const elapsed = formatElapsed(Date.now() - startTime);
1565
+ onStatus?.(`Waiting for dust generation capacity (${elapsed} elapsed, ~5 min on fresh wallets)...`);
1566
+ await new Promise((resolve4) => setTimeout(resolve4, DUST_REGISTRATION_RETRY_DELAY_MS));
1567
+ continue;
1568
+ }
1569
+ throw err;
1570
+ }
1571
+ }
1572
+ throw lastError ?? new Error("Dust registration timed out");
1573
+ } finally {
1574
+ restoreConsole();
1575
+ }
1576
+ }
1495
1577
  async function ensureDust(bundle, onDust) {
1496
1578
  const state = await rx2.firstValueFrom(bundle.facade.state().pipe(rx2.filter((s) => s.isSynced)));
1497
1579
  const nightUtxos = state.unshielded.availableCoins.filter((coin) => coin.meta?.registeredForDustGeneration !== true);
1498
1580
  if (nightUtxos.length > 0) {
1499
1581
  onDust?.(`Registering ${nightUtxos.length} UTXO(s) for dust generation...`);
1500
- const ttl = new Date(Date.now() + TX_TTL_MINUTES * 60 * 1000);
1501
- const dustReceiverAddress = state.dust.dustAddress;
1502
1582
  const dustUtxos = nightUtxos.map((coin) => ({
1503
1583
  ...coin.utxo,
1504
1584
  ctime: new Date(coin.meta.ctime)
1505
1585
  }));
1506
- await bundle.facade.dust.waitForSyncedState();
1507
- const unprovenTx = await bundle.facade.dust.createDustGenerationTransaction(new Date, ttl, dustUtxos, bundle.keystore.getPublicKey(), dustReceiverAddress);
1508
- const intent = unprovenTx.intents?.get(1);
1509
- if (!intent) {
1510
- throw new Error("Dust generation intent not found on transaction");
1511
- }
1512
- const signature = bundle.keystore.signData(intent.signatureData(1));
1513
- const signedTx = await bundle.facade.dust.addDustGenerationSignature(unprovenTx, signature);
1514
- const finalized = await bundle.facade.finalizeTransaction(signedTx);
1515
- await bundle.facade.submitTransaction(finalized);
1586
+ await registerNightUtxos(bundle, dustUtxos, state.dust.dustAddress, onDust);
1516
1587
  } else if (state.dust.availableCoins.length > 0) {
1517
1588
  onDust?.("Dust available");
1518
1589
  return;
@@ -1523,7 +1594,7 @@ async function ensureDust(bundle, onDust) {
1523
1594
  await rx2.firstValueFrom(bundle.facade.state().pipe(rx2.throttleTime(5000), rx2.filter((s) => s.isSynced), rx2.filter((s) => s.dust.walletBalance(new Date) > 0n), rx2.timeout(DUST_TIMEOUT_MS)));
1524
1595
  onDust?.("Dust available");
1525
1596
  }
1526
- async function buildAndSubmitTransfer(bundle, recipientAddress, amount, onProving, onSubmitting) {
1597
+ async function buildAndSubmitTransfer(bundle, recipientAddress, amount, onProving, onSubmitting, onDust) {
1527
1598
  let lastError;
1528
1599
  for (let attempt = 1;attempt <= MAX_RETRY_ATTEMPTS; attempt++) {
1529
1600
  try {
@@ -1560,6 +1631,12 @@ async function buildAndSubmitTransfer(bundle, recipientAddress, amount, onProvin
1560
1631
  if (isStaleUtxo && attempt < MAX_RETRY_ATTEMPTS) {
1561
1632
  continue;
1562
1633
  }
1634
+ const isDustInsufficient = err?.message?.toLowerCase().includes("not enough dust") || err?.message?.toLowerCase().includes("dust generated");
1635
+ if (isDustInsufficient && attempt < MAX_RETRY_ATTEMPTS) {
1636
+ onDust?.("Waiting for more dust to accumulate...");
1637
+ await new Promise((resolve4) => setTimeout(resolve4, DUST_REGISTRATION_RETRY_DELAY_MS));
1638
+ continue;
1639
+ }
1563
1640
  throw err;
1564
1641
  }
1565
1642
  }
@@ -1610,7 +1687,7 @@ async function executeTransfer(params) {
1610
1687
  await ensureDust(bundle, onDust);
1611
1688
  if (signal?.aborted)
1612
1689
  throw new Error("Operation cancelled");
1613
- const txHash = await buildAndSubmitTransfer(bundle, recipientAddress, amount, onProving, onSubmitting);
1690
+ const txHash = await buildAndSubmitTransfer(bundle, recipientAddress, amount, onProving, onSubmitting, onDust);
1614
1691
  return { txHash, amountMicroNight: amount };
1615
1692
  } finally {
1616
1693
  signal?.removeEventListener("abort", onAbort);
@@ -1714,7 +1791,13 @@ async function airdropCommand(args, signal) {
1714
1791
  process.stderr.write(`
1715
1792
  ` + divider() + `
1716
1793
  `);
1717
- process.stderr.write(dim(" Verify: midnight balance") + `
1794
+ process.stderr.write(dim(" Verify: midnight balance") + `
1795
+ `);
1796
+ process.stderr.write(dim(" Register dust: midnight dust register") + `
1797
+ `);
1798
+ process.stderr.write(dim(" Note: Dust generation takes a few minutes on a fresh wallet.") + `
1799
+ `);
1800
+ process.stderr.write(dim(" It will happen automatically on your first transfer.") + `
1718
1801
 
1719
1802
  `);
1720
1803
  } catch (err) {
@@ -1933,22 +2016,13 @@ async function dustRegister(bundle, networkName, jsonMode, signal, warningRef) {
1933
2016
  spinner.update("All UTXOs already registered, waiting for dust generation...");
1934
2017
  } else {
1935
2018
  spinner.update(`Registering ${nightUtxos.length} UTXO(s) for dust generation...`);
1936
- const ttl = new Date(Date.now() + TX_TTL_MINUTES * 60 * 1000);
1937
- const dustReceiverAddress = state.dust.dustAddress;
1938
2019
  const dustUtxos = nightUtxos.map((coin) => ({
1939
2020
  ...coin.utxo,
1940
2021
  ctime: new Date(coin.meta.ctime)
1941
2022
  }));
1942
- await bundle.facade.dust.waitForSyncedState();
1943
- const unprovenTx = await bundle.facade.dust.createDustGenerationTransaction(new Date, ttl, dustUtxos, bundle.keystore.getPublicKey(), dustReceiverAddress);
1944
- const intent = unprovenTx.intents?.get(1);
1945
- if (!intent) {
1946
- throw new Error("Dust generation intent not found on transaction");
1947
- }
1948
- const signature = bundle.keystore.signData(intent.signatureData(1));
1949
- const signedTx = await bundle.facade.dust.addDustGenerationSignature(unprovenTx, signature);
1950
- const finalized = await bundle.facade.finalizeTransaction(signedTx);
1951
- txHash = await bundle.facade.submitTransaction(finalized);
2023
+ txHash = await registerNightUtxos(bundle, dustUtxos, state.dust.dustAddress, (status) => {
2024
+ spinner.update(status);
2025
+ });
1952
2026
  spinner.update(`Registration submitted (${txHash.slice(0, 12)}...), waiting for dust...`);
1953
2027
  }
1954
2028
  if (signal?.aborted)
@@ -2044,6 +2118,7 @@ var init_dust = __esm(() => {
2044
2118
  init_wallet_config();
2045
2119
  init_resolve_network();
2046
2120
  init_facade();
2121
+ init_transfer();
2047
2122
  init_format();
2048
2123
  init_spinner();
2049
2124
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "midnight-wallet-cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "description": "Git-style CLI wallet for the Midnight blockchain",
6
6
  "license": "Apache-2.0",