@1sat/cli 0.0.111 → 0.0.113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,15 +1,7 @@
1
1
  #!/usr/bin/env node
2
- var __esm = (fn, res, err) => () => {
3
- if (fn)
4
- try {
5
- res = fn(fn = 0);
6
- } catch (e) {
7
- err = [e];
8
- }
9
- if (err)
10
- throw err[0];
11
- return res;
12
- };
2
+ import { createRequire } from "node:module";
3
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
4
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
13
5
 
14
6
  // src/args.ts
15
7
  function parseGlobalFlags(args) {
@@ -513,11 +505,11 @@ async function handleActionCommand(args, opts) {
513
505
  Registered actions:
514
506
  `);
515
507
  const grouped = new Map;
516
- for (const action of actions) {
517
- const cat = action.meta.category;
508
+ for (const action2 of actions) {
509
+ const cat = action2.meta.category;
518
510
  if (!grouped.has(cat))
519
511
  grouped.set(cat, []);
520
- grouped.get(cat).push(action);
512
+ grouped.get(cat).push(action2);
521
513
  }
522
514
  for (const [category, categoryActions] of grouped) {
523
515
  console.log(` ${category}:`);
@@ -549,6 +541,9 @@ Run '1sat action' to list available actions.`);
549
541
  try {
550
542
  const result = await action.execute(ctx, input);
551
543
  output(result, opts);
544
+ if (actionName === "cancelOwnedListings" && result !== null && typeof result === "object" && "errors" in result && Array.isArray(result.errors) && result.errors.length > 0) {
545
+ process.exitCode = 1;
546
+ }
552
547
  } finally {
553
548
  await destroy();
554
549
  }
@@ -1048,6 +1043,8 @@ var init_help = __esm(() => {
1048
1043
  {
1049
1044
  name: "sell",
1050
1045
  description: "List an ordinal for sale (OrdLock)",
1046
+ unavailable: true,
1047
+ notes: "OrdLock listing create is disabled. Buy and cancel of existing listings remain available.",
1051
1048
  args: [
1052
1049
  { flag: "--id", values: "<tracking-id>", required: true },
1053
1050
  { flag: "--price", values: "<sats>", required: true },
@@ -1260,6 +1257,8 @@ var init_help = __esm(() => {
1260
1257
  {
1261
1258
  name: "sell",
1262
1259
  description: "List an OpNS name for sale",
1260
+ unavailable: true,
1261
+ notes: "OrdLock listing create is disabled. Buy and cancel of existing listings remain available.",
1263
1262
  args: [
1264
1263
  { flag: "--id", values: "<tracking-id>", required: true },
1265
1264
  { flag: "--price", values: "<satoshis>", required: true },
@@ -1441,7 +1440,7 @@ function createApprovalAuth(wallet) {
1441
1440
  get(target, property) {
1442
1441
  if (property === "createAction")
1443
1442
  return async (...args) => {
1444
- const satoshis = args[0].outputs?.reduce((sum, output) => sum + output.satoshis, 0) ?? 0;
1443
+ const satoshis = args[0].outputs?.reduce((sum, output2) => sum + output2.satoshis, 0) ?? 0;
1445
1444
  if (!Number.isSafeInteger(satoshis) || satoshis <= 0)
1446
1445
  throw new Error("Invalid HTTP payment amount");
1447
1446
  if (approved === false)
@@ -1965,15 +1964,15 @@ async function handleInitCommand(_args, opts) {
1965
1964
  }
1966
1965
  let wif;
1967
1966
  if (keySource === "generate") {
1968
- const pk = PrivateKey2.fromRandom();
1969
- wif = pk.toWif();
1970
- const address = pk.toPublicKey().toAddress();
1967
+ const pk2 = PrivateKey2.fromRandom();
1968
+ wif = pk2.toWif();
1969
+ const address2 = pk2.toPublicKey().toAddress();
1971
1970
  console.log();
1972
1971
  console.log(formatWarning(" IMPORTANT: Back up your private key!"));
1973
1972
  console.log(formatWarning(" If you lose it, your funds are gone forever."));
1974
1973
  console.log();
1975
1974
  console.log(` ${formatValue("WIF:")} ${wif}`);
1976
- console.log(` ${formatValue("Address:")} ${address}`);
1975
+ console.log(` ${formatValue("Address:")} ${address2}`);
1977
1976
  console.log();
1978
1977
  const confirmed = await confirm2({
1979
1978
  message: "Have you saved your private key somewhere safe?"
@@ -2442,10 +2441,9 @@ import {
2442
2441
  internalizeOpns,
2443
2442
  listOpns,
2444
2443
  registerOpns,
2445
- sellOpns,
2446
2444
  sendOpns
2447
2445
  } from "@1sat/actions";
2448
- import { P1SAT_PROTOCOL } from "@1sat/types";
2446
+ import { ORDLOCK_LISTING_CREATE_DISABLED, P1SAT_PROTOCOL } from "@1sat/types";
2449
2447
  import { confirm as confirm4, isCancel as isCancel5 } from "@clack/prompts";
2450
2448
  async function handleOpnsCommand(args, opts) {
2451
2449
  const [subcommand, ...rest] = args;
@@ -2582,37 +2580,8 @@ async function opnsCancelListingCmd(args, opts) {
2582
2580
  await destroy();
2583
2581
  }
2584
2582
  }
2585
- async function opnsSell(args, opts) {
2586
- const id = requireId(args);
2587
- const priceStr = extractFlag(args, "--price");
2588
- const payAddress = extractFlag(args, "--pay-address");
2589
- if (!priceStr)
2590
- fatal("Missing --price <satoshis>");
2591
- const price = Number(priceStr);
2592
- if (!Number.isFinite(price) || price <= 0) {
2593
- fatal("--price must be a positive number");
2594
- }
2595
- if (!opts.yes) {
2596
- const ok = await confirm4({
2597
- message: `List OpNS id ${id} for sale at ${price} satoshis?`
2598
- });
2599
- if (isCancel5(ok) || !ok)
2600
- fatal("Listing cancelled.");
2601
- }
2602
- const privateKey = await loadKey();
2603
- const { ctx, destroy } = await loadContext(privateKey, { chain: opts.chain });
2604
- try {
2605
- const result = await sellOpns.execute(ctx, {
2606
- id,
2607
- price,
2608
- ...payAddress && { payAddress }
2609
- });
2610
- if (result.error)
2611
- fatal(result.error);
2612
- output(opts.json ? result : { txid: result.txid }, opts);
2613
- } finally {
2614
- await destroy();
2615
- }
2583
+ async function opnsSell(_args, _opts) {
2584
+ fatal(ORDLOCK_LISTING_CREATE_DISABLED);
2616
2585
  }
2617
2586
  async function opnsSend(args, opts) {
2618
2587
  const id = requireId(args);
@@ -2713,9 +2682,9 @@ import {
2713
2682
  getDisplayValue as getDisplayValue2,
2714
2683
  inscribe,
2715
2684
  listOrdinals,
2716
- sellOrdinal,
2717
2685
  sendOrdinals
2718
2686
  } from "@1sat/actions";
2687
+ import { ORDLOCK_LISTING_CREATE_DISABLED as ORDLOCK_LISTING_CREATE_DISABLED2 } from "@1sat/types";
2719
2688
  import { Utils as Utils3 } from "@bsv/sdk";
2720
2689
  import { confirm as confirm5, isCancel as isCancel6 } from "@clack/prompts";
2721
2690
  async function handleOrdinalsCommand(args, opts) {
@@ -2878,37 +2847,8 @@ async function ordinalsSend(args, opts) {
2878
2847
  await destroy();
2879
2848
  }
2880
2849
  }
2881
- async function ordinalsSell(args, opts) {
2882
- const id = requireId2(args);
2883
- const priceStr = extractFlag(args, "--price");
2884
- const payAddress = extractFlag(args, "--pay-address");
2885
- if (!priceStr)
2886
- fatal("Missing --price <satoshis>");
2887
- const price = Number(priceStr);
2888
- if (!Number.isFinite(price) || price <= 0) {
2889
- fatal("--price must be a positive number");
2890
- }
2891
- if (!opts.yes) {
2892
- const ok = await confirm5({
2893
- message: `List ordinal id ${id} for sale at ${price} satoshis?`
2894
- });
2895
- if (isCancel6(ok) || !ok)
2896
- fatal("Listing cancelled.");
2897
- }
2898
- const privateKey = await loadKey();
2899
- const { ctx, destroy } = await loadContext(privateKey, { chain: opts.chain });
2900
- try {
2901
- const result = await sellOrdinal.execute(ctx, {
2902
- id,
2903
- price,
2904
- ...payAddress && { payAddress }
2905
- });
2906
- if (result.error)
2907
- fatal(result.error);
2908
- output(opts.json ? result : { txid: result.txid }, opts);
2909
- } finally {
2910
- await destroy();
2911
- }
2850
+ async function ordinalsSell(_args, _opts) {
2851
+ fatal(ORDLOCK_LISTING_CREATE_DISABLED2);
2912
2852
  }
2913
2853
  async function ordinalsCancel(args, opts) {
2914
2854
  const id = requireId2(args);
@@ -3389,7 +3329,7 @@ function buildPriceUpdateTask(options) {
3389
3329
  lastRunMsecsSinceEpoch: 0,
3390
3330
  async asyncSetup() {},
3391
3331
  trigger(now) {
3392
- if (targets.length === 0)
3332
+ if (targets.length === 0 && !options.onQuote)
3393
3333
  return { run: false };
3394
3334
  if (now - this.lastRunMsecsSinceEpoch < intervalMs)
3395
3335
  return { run: false };
@@ -3402,6 +3342,13 @@ function buildPriceUpdateTask(options) {
3402
3342
  } catch (err) {
3403
3343
  return `rate fetch failed: ${err.message}`;
3404
3344
  }
3345
+ if (options.onQuote) {
3346
+ try {
3347
+ await options.onQuote(quote);
3348
+ } catch (err) {
3349
+ return `quote persist failed: ${err.message}`;
3350
+ }
3351
+ }
3405
3352
  const parts = [];
3406
3353
  for (const target of targets) {
3407
3354
  const currentSats = target.readCurrentSats();
@@ -3498,6 +3445,7 @@ import {
3498
3445
  } from "@1sat/wallet-node";
3499
3446
  import {
3500
3447
  KnexAccountStore,
3448
+ KnexHandleCertStore,
3501
3449
  KnexPendingStore,
3502
3450
  createHostServer,
3503
3451
  createWalletServer
@@ -3644,7 +3592,7 @@ async function runWithStorage(resolved, mode) {
3644
3592
  }
3645
3593
  });
3646
3594
  }
3647
- if (r?.enabled && targets.length > 0) {
3595
+ if (r?.enabled) {
3648
3596
  walletResult.monitor.addTask(buildPriceUpdateTask({
3649
3597
  monitor: walletResult.monitor,
3650
3598
  rateProvider: resolveRateProvider(r.provider ?? "whatsonchain", {
@@ -3655,9 +3603,13 @@ async function runWithStorage(resolved, mode) {
3655
3603
  maxMovePct: r.maxMovePct ?? 25,
3656
3604
  minSats: r.minSats ?? 1
3657
3605
  },
3658
- targets
3606
+ targets,
3607
+ onQuote: async (quote) => {
3608
+ setConfigPath("server.exchangeRate", quote);
3609
+ }
3659
3610
  }));
3660
- console.log(`[repricer] enabled — ${targets.map((t) => `${t.name} $${t.targetUsd}`).join(", ")} every ${Math.round((r.intervalMs ?? 900000) / 1000)}s via ${r.provider ?? "whatsonchain"}`);
3611
+ const targetNote = targets.map((t) => `${t.name} $${t.targetUsd}`).join(", ") || "quote only";
3612
+ console.log(`[repricer] enabled — ${targetNote} every ${Math.round((r.intervalMs ?? 900000) / 1000)}s via ${r.provider ?? "whatsonchain"}`);
3661
3613
  }
3662
3614
  }
3663
3615
  if (runMonitor) {
@@ -3687,7 +3639,7 @@ async function startWalletServer(resolved, walletResult, accounts, mode) {
3687
3639
  ttlSeconds: sessionStoreCfg.ttlSeconds
3688
3640
  } : undefined;
3689
3641
  if (mode === "wallet") {
3690
- const handle = createWalletServer({
3642
+ const handle2 = createWalletServer({
3691
3643
  wallet: walletResult.wallet,
3692
3644
  storage: walletResult.getActiveStorage(),
3693
3645
  serverIdentityKey: walletResult.wallet.identityKey,
@@ -3697,10 +3649,10 @@ async function startWalletServer(resolved, walletResult, accounts, mode) {
3697
3649
  accounts: accounts?.walletServerAccounts,
3698
3650
  sessionStore
3699
3651
  });
3700
- const port = await handle.start();
3701
- const notes = [resolved.accounts.enabled ? "accounts: on" : ""].filter(Boolean).join(", ");
3702
- console.log(`[wallet] listening on ${resolved.host}:${port}${notes ? ` (${notes})` : ""}`);
3703
- return { stop: () => handle.stop() };
3652
+ const port2 = await handle2.start();
3653
+ const notes2 = [resolved.accounts.enabled ? "accounts: on" : ""].filter(Boolean).join(", ");
3654
+ console.log(`[wallet] listening on ${resolved.host}:${port2}${notes2 ? ` (${notes2})` : ""}`);
3655
+ return { stop: () => handle2.stop() };
3704
3656
  }
3705
3657
  const serverCfg = config.server ?? {};
3706
3658
  const paymailCfg = serverCfg.paymail;
@@ -3710,6 +3662,8 @@ async function startWalletServer(resolved, walletResult, accounts, mode) {
3710
3662
  await pendingStore.init();
3711
3663
  const accountStore = new KnexAccountStore(knex);
3712
3664
  await accountStore.init();
3665
+ const handleCertStore = new KnexHandleCertStore(knex);
3666
+ await handleCertStore.init();
3713
3667
  const handle = await createHostServer({
3714
3668
  wallet: walletResult.wallet,
3715
3669
  sessionStore,
@@ -3718,12 +3672,16 @@ async function startWalletServer(resolved, walletResult, accounts, mode) {
3718
3672
  listen: { port: resolved.port, host: resolved.host },
3719
3673
  accounts: accounts?.walletServerAccounts,
3720
3674
  accountStore,
3675
+ handleCertStore,
3676
+ getExchangeRate: () => loadConfig().server?.exchangeRate ?? null,
3721
3677
  paymail: {
3722
3678
  baseUrl: paymailCfg?.baseUrl ?? `http://${resolved.host}:${resolved.port}`,
3723
3679
  stackUrl: paymailCfg?.stackUrl ?? resolved.stackUrl,
3724
3680
  pendingStore,
3725
3681
  accountStore,
3682
+ certStore: handleCertStore,
3726
3683
  ...paymailCfg?.userDomain && { userDomain: paymailCfg.userDomain },
3684
+ ...paymailCfg?.verifySignature && { verifySignature: true },
3727
3685
  messageboxUrl: `http://127.0.0.1:${resolved.port}/messagebox`,
3728
3686
  hostPrivateKey: resolved.privateKey
3729
3687
  },
@@ -4867,8 +4825,8 @@ async function walletListOutputs(args, opts) {
4867
4825
  ${result.totalOutputs} total outputs in basket '${basket}':
4868
4826
  `);
4869
4827
  for (const out of result.outputs) {
4870
- const tags = out.tags?.join(", ") || "none";
4871
- console.log(` ${out.outpoint} | ${out.satoshis} sats | tags: ${tags}`);
4828
+ const tags2 = out.tags?.join(", ") || "none";
4829
+ console.log(` ${out.outpoint} | ${out.satoshis} sats | tags: ${tags2}`);
4872
4830
  }
4873
4831
  console.log();
4874
4832
  }
@@ -5126,6 +5084,7 @@ var init_wallet = __esm(() => {
5126
5084
  });
5127
5085
 
5128
5086
  // src/main.ts
5087
+ var exports_main = {};
5129
5088
  import { existsSync as existsSync4 } from "node:fs";
5130
5089
  import { resolve } from "node:path";
5131
5090
  import { config as loadEnv } from "dotenv";
@@ -5265,4 +5224,4 @@ var init_main = __esm(() => {
5265
5224
 
5266
5225
  // src/cli.ts
5267
5226
  process.env.DOTENV_CONFIG_QUIET = "true";
5268
- await Promise.resolve().then(() => (init_main(), {}));
5227
+ await Promise.resolve().then(() => (init_main(), exports_main));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1sat/cli",
3
- "version": "0.0.111",
3
+ "version": "0.0.113",
4
4
  "description": "CLI for 1Sat Ordinals SDK",
5
5
  "type": "module",
6
6
  "main": "./dist/cli.js",
@@ -27,20 +27,20 @@
27
27
  ],
28
28
  "license": "MIT",
29
29
  "dependencies": {
30
- "@1sat/actions": "0.0.208",
31
- "@1sat/client": "0.0.51",
32
- "@1sat/types": "0.0.41",
33
- "@1sat/wallet-node": "0.0.73",
34
- "@1sat/wallet-server": "0.0.48",
35
- "@bsv/sdk": "^2.1.9",
30
+ "@1sat/actions": "0.0.214",
31
+ "@1sat/client": "0.0.52",
32
+ "@1sat/types": "0.0.44",
33
+ "@1sat/wallet-node": "0.0.74",
34
+ "@1sat/wallet-server": "0.0.50",
35
+ "@bopen-io/messagebox-server": "^1.3.2",
36
+ "@bsv/sdk": "^2.5.0",
36
37
  "@bsv/wallet-toolbox": "npm:@bopen-io/wallet-toolbox@2.6.2-brc153.5",
37
- "chalk": "^5.0.0",
38
38
  "@clack/prompts": "^0.8.0",
39
39
  "bitcoin-backup": "^0.0.11",
40
+ "chalk": "^5.0.0",
40
41
  "dotenv": "^17.0.0",
41
42
  "evlog": "^2.10.0",
42
43
  "knex": "^3.1.0",
43
- "@bopen-io/messagebox-server": "^1.3.2",
44
44
  "pg": "^8.11.3",
45
45
  "sqlite3": "^6.0.1"
46
46
  },
@@ -73,6 +73,16 @@ export async function handleActionCommand(
73
73
  try {
74
74
  const result = await action.execute(ctx, input)
75
75
  output(result, opts)
76
+ if (
77
+ actionName === 'cancelOwnedListings' &&
78
+ result !== null &&
79
+ typeof result === 'object' &&
80
+ 'errors' in result &&
81
+ Array.isArray(result.errors) &&
82
+ result.errors.length > 0
83
+ ) {
84
+ process.exitCode = 1
85
+ }
76
86
  } finally {
77
87
  await destroy()
78
88
  }
@@ -10,10 +10,9 @@ import {
10
10
  internalizeOpns,
11
11
  listOpns,
12
12
  registerOpns,
13
- sellOpns,
14
13
  sendOpns,
15
14
  } from '@1sat/actions'
16
- import { P1SAT_PROTOCOL } from '@1sat/types'
15
+ import { ORDLOCK_LISTING_CREATE_DISABLED, P1SAT_PROTOCOL } from '@1sat/types'
17
16
  import { confirm, isCancel } from '@clack/prompts'
18
17
  import type { GlobalFlags } from '../args.js'
19
18
  import { extractFlag } from '../args.js'
@@ -194,38 +193,8 @@ async function opnsCancelListingCmd(
194
193
  }
195
194
  }
196
195
 
197
- async function opnsSell(args: string[], opts: GlobalFlags): Promise<void> {
198
- const id = requireId(args)
199
- const priceStr = extractFlag(args, '--price')
200
- const payAddress = extractFlag(args, '--pay-address')
201
-
202
- if (!priceStr) fatal('Missing --price <satoshis>')
203
- const price = Number(priceStr)
204
- if (!Number.isFinite(price) || price <= 0) {
205
- fatal('--price must be a positive number')
206
- }
207
-
208
- if (!opts.yes) {
209
- const ok = await confirm({
210
- message: `List OpNS id ${id} for sale at ${price} satoshis?`,
211
- })
212
- if (isCancel(ok) || !ok) fatal('Listing cancelled.')
213
- }
214
-
215
- const privateKey = await loadKey()
216
- const { ctx, destroy } = await loadContext(privateKey, { chain: opts.chain })
217
-
218
- try {
219
- const result = await sellOpns.execute(ctx, {
220
- id,
221
- price,
222
- ...(payAddress && { payAddress }),
223
- })
224
- if (result.error) fatal(result.error)
225
- output(opts.json ? result : { txid: result.txid }, opts)
226
- } finally {
227
- await destroy()
228
- }
196
+ async function opnsSell(_args: string[], _opts: GlobalFlags): Promise<void> {
197
+ fatal(ORDLOCK_LISTING_CREATE_DISABLED)
229
198
  }
230
199
 
231
200
  async function opnsSend(args: string[], opts: GlobalFlags): Promise<void> {
@@ -11,9 +11,9 @@ import {
11
11
  getDisplayValue,
12
12
  inscribe,
13
13
  listOrdinals,
14
- sellOrdinal,
15
14
  sendOrdinals,
16
15
  } from '@1sat/actions'
16
+ import { ORDLOCK_LISTING_CREATE_DISABLED } from '@1sat/types'
17
17
  import { Utils } from '@bsv/sdk'
18
18
  import { confirm, isCancel } from '@clack/prompts'
19
19
  import type { GlobalFlags } from '../args.js'
@@ -245,38 +245,11 @@ async function ordinalsSend(args: string[], opts: GlobalFlags): Promise<void> {
245
245
  }
246
246
  }
247
247
 
248
- async function ordinalsSell(args: string[], opts: GlobalFlags): Promise<void> {
249
- const id = requireId(args)
250
- const priceStr = extractFlag(args, '--price')
251
- const payAddress = extractFlag(args, '--pay-address')
252
-
253
- if (!priceStr) fatal('Missing --price <satoshis>')
254
- const price = Number(priceStr)
255
- if (!Number.isFinite(price) || price <= 0) {
256
- fatal('--price must be a positive number')
257
- }
258
-
259
- if (!opts.yes) {
260
- const ok = await confirm({
261
- message: `List ordinal id ${id} for sale at ${price} satoshis?`,
262
- })
263
- if (isCancel(ok) || !ok) fatal('Listing cancelled.')
264
- }
265
-
266
- const privateKey = await loadKey()
267
- const { ctx, destroy } = await loadContext(privateKey, { chain: opts.chain })
268
-
269
- try {
270
- const result = await sellOrdinal.execute(ctx, {
271
- id,
272
- price,
273
- ...(payAddress && { payAddress }),
274
- })
275
- if (result.error) fatal(result.error)
276
- output(opts.json ? result : { txid: result.txid }, opts)
277
- } finally {
278
- await destroy()
279
- }
248
+ async function ordinalsSell(
249
+ _args: string[],
250
+ _opts: GlobalFlags,
251
+ ): Promise<void> {
252
+ fatal(ORDLOCK_LISTING_CREATE_DISABLED)
280
253
  }
281
254
 
282
255
  async function ordinalsCancel(
@@ -26,6 +26,7 @@ import {
26
26
  } from '@1sat/wallet-node'
27
27
  import {
28
28
  KnexAccountStore,
29
+ KnexHandleCertStore,
29
30
  KnexPendingStore,
30
31
  createHostServer,
31
32
  createWalletServer,
@@ -298,7 +299,7 @@ async function runWithStorage(
298
299
  },
299
300
  })
300
301
  }
301
- if (r?.enabled && targets.length > 0) {
302
+ if (r?.enabled) {
302
303
  walletResult.monitor.addTask(
303
304
  buildPriceUpdateTask({
304
305
  monitor: walletResult.monitor,
@@ -311,12 +312,16 @@ async function runWithStorage(
311
312
  minSats: r.minSats ?? 1,
312
313
  },
313
314
  targets,
315
+ onQuote: async (quote) => {
316
+ setConfigPath('server.exchangeRate', quote)
317
+ },
314
318
  }),
315
319
  )
320
+ const targetNote =
321
+ targets.map((t) => `${t.name} $${t.targetUsd}`).join(', ') ||
322
+ 'quote only'
316
323
  console.log(
317
- `[repricer] enabled — ${targets
318
- .map((t) => `${t.name} $${t.targetUsd}`)
319
- .join(', ')} every ${Math.round(
324
+ `[repricer] enabled — ${targetNote} every ${Math.round(
320
325
  (r.intervalMs ?? 900_000) / 1000,
321
326
  )}s via ${r.provider ?? 'whatsonchain'}`,
322
327
  )
@@ -401,6 +406,8 @@ async function startWalletServer(
401
406
  await pendingStore.init()
402
407
  const accountStore = new KnexAccountStore(knex)
403
408
  await accountStore.init()
409
+ const handleCertStore = new KnexHandleCertStore(knex)
410
+ await handleCertStore.init()
404
411
 
405
412
  const handle = await createHostServer({
406
413
  wallet: walletResult.wallet,
@@ -410,13 +417,17 @@ async function startWalletServer(
410
417
  listen: { port: resolved.port, host: resolved.host },
411
418
  accounts: accounts?.walletServerAccounts,
412
419
  accountStore,
420
+ handleCertStore,
421
+ getExchangeRate: () => loadConfig().server?.exchangeRate ?? null,
413
422
  paymail: {
414
423
  baseUrl:
415
424
  paymailCfg?.baseUrl ?? `http://${resolved.host}:${resolved.port}`,
416
425
  stackUrl: paymailCfg?.stackUrl ?? resolved.stackUrl,
417
426
  pendingStore,
418
427
  accountStore,
428
+ certStore: handleCertStore,
419
429
  ...(paymailCfg?.userDomain && { userDomain: paymailCfg.userDomain }),
430
+ ...(paymailCfg?.verifySignature && { verifySignature: true }),
420
431
  messageboxUrl: `http://127.0.0.1:${resolved.port}/messagebox`,
421
432
  hostPrivateKey: resolved.privateKey,
422
433
  },
package/src/config.ts CHANGED
@@ -118,6 +118,11 @@ export interface ServerPaymailConfig {
118
118
  * only resolves when its identity holds an account on this host.
119
119
  */
120
120
  userDomain?: string
121
+ /**
122
+ * Require signed P2P metadata on receive-beef / receive-transaction.
123
+ * Advertised as Request Sender Validation on well-known. Default false.
124
+ */
125
+ verifySignature?: boolean
121
126
  }
122
127
 
123
128
  export interface ServerSessionStoreConfig {
@@ -153,6 +158,12 @@ export interface ServerConfig {
153
158
  paymail?: ServerPaymailConfig
154
159
  /** Shared BSV/USD repricer engine for the accounts storage price. */
155
160
  repricer?: RepricerConfig
161
+ /** Last successful BSV/USD quote from the repricer. Served at GET /exchange-rate. */
162
+ exchangeRate?: {
163
+ bsvUsd: number
164
+ timestamp: number
165
+ source: string
166
+ }
156
167
  /**
157
168
  * Redis-shared BRC-104 sessions. Required when multiple serve instances
158
169
  * run behind one load balancer; unset keeps sessions in-memory.
package/src/help.ts CHANGED
@@ -393,6 +393,9 @@ export const COMMANDS: CommandSpec[] = [
393
393
  {
394
394
  name: 'sell',
395
395
  description: 'List an ordinal for sale (OrdLock)',
396
+ unavailable: true,
397
+ notes:
398
+ 'OrdLock listing create is disabled. Buy and cancel of existing listings remain available.',
396
399
  args: [
397
400
  { flag: '--id', values: '<tracking-id>', required: true },
398
401
  { flag: '--price', values: '<sats>', required: true },
@@ -617,6 +620,9 @@ export const COMMANDS: CommandSpec[] = [
617
620
  {
618
621
  name: 'sell',
619
622
  description: 'List an OpNS name for sale',
623
+ unavailable: true,
624
+ notes:
625
+ 'OrdLock listing create is disabled. Buy and cancel of existing listings remain available.',
620
626
  args: [
621
627
  { flag: '--id', values: '<tracking-id>', required: true },
622
628
  { flag: '--price', values: '<satoshis>', required: true },
@@ -1,5 +1,5 @@
1
1
  import { computeReprice } from './computeReprice.js'
2
- import type { RateProvider, RepricerBounds } from './types.js'
2
+ import type { BsvUsdQuote, RateProvider, RepricerBounds } from './types.js'
3
3
 
4
4
  /** One price the repricer maintains from the shared BSV/USD quote. */
5
5
  export interface RepriceTarget {
@@ -20,6 +20,8 @@ export interface PriceUpdateTaskOptions {
20
20
  intervalMs: number
21
21
  bounds: RepricerBounds
22
22
  targets: RepriceTarget[]
23
+ /** Called after a successful rate fetch, even when every target is skipped. */
24
+ onQuote?: (quote: BsvUsdQuote) => Promise<void>
23
25
  }
24
26
 
25
27
  export interface PriceUpdateTask {
@@ -47,7 +49,7 @@ export function buildPriceUpdateTask(
47
49
  lastRunMsecsSinceEpoch: 0,
48
50
  async asyncSetup() {},
49
51
  trigger(now: number): { run: boolean } {
50
- if (targets.length === 0) return { run: false }
52
+ if (targets.length === 0 && !options.onQuote) return { run: false }
51
53
  if (now - this.lastRunMsecsSinceEpoch < intervalMs) return { run: false }
52
54
  return { run: true }
53
55
  },
@@ -59,6 +61,14 @@ export function buildPriceUpdateTask(
59
61
  return `rate fetch failed: ${(err as Error).message}`
60
62
  }
61
63
 
64
+ if (options.onQuote) {
65
+ try {
66
+ await options.onQuote(quote)
67
+ } catch (err) {
68
+ return `quote persist failed: ${(err as Error).message}`
69
+ }
70
+ }
71
+
62
72
  const parts: string[] = []
63
73
  for (const target of targets) {
64
74
  const currentSats = target.readCurrentSats()