@1sat/cli 0.0.51 → 0.0.54
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/package.json +7 -6
- package/scripts/resubmit-bsv21-discovery.ts +33 -0
- package/src/cli.ts +14 -5
- package/src/commands/config.ts +1 -6
- package/src/commands/identity.ts +1 -8
- package/src/commands/locks.ts +1 -5
- package/src/commands/opns.ts +1 -5
- package/src/commands/ordinals.ts +1 -9
- package/src/commands/remote.ts +1 -11
- package/src/commands/serve-messagebox.ts +213 -0
- package/src/commands/serve.ts +26 -9
- package/src/commands/social.ts +1 -3
- package/src/commands/sweep.ts +1 -4
- package/src/commands/tokens.ts +248 -16
- package/src/commands/tx.ts +1 -3
- package/src/commands/wallet.ts +1 -20
- package/src/config.ts +24 -0
- package/src/help.ts +719 -101
- package/src/types/messagebox-server.d.ts +10 -0
package/src/commands/tokens.ts
CHANGED
|
@@ -3,12 +3,16 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
+
deployBsv21Auth,
|
|
7
|
+
deployBsv21Mint,
|
|
6
8
|
getBsv21Balances,
|
|
7
9
|
getDisplayValue,
|
|
8
10
|
listTokens,
|
|
11
|
+
mintBsv21,
|
|
9
12
|
purchaseBsv21,
|
|
10
13
|
sendBsv21,
|
|
11
14
|
} from '@1sat/actions'
|
|
15
|
+
import type { Destination } from '@1sat/types'
|
|
12
16
|
import { confirm, isCancel } from '@clack/prompts'
|
|
13
17
|
import type { GlobalFlags } from '../args'
|
|
14
18
|
import { extractFlag } from '../args'
|
|
@@ -30,18 +34,16 @@ export async function handleTokensCommand(
|
|
|
30
34
|
return tokenList(rest, opts)
|
|
31
35
|
case 'send':
|
|
32
36
|
return tokenSend(rest, opts)
|
|
33
|
-
case 'deploy':
|
|
34
|
-
return
|
|
37
|
+
case 'deploy-mint':
|
|
38
|
+
return tokenDeployMint(rest, opts)
|
|
39
|
+
case 'deploy-auth':
|
|
40
|
+
return tokenDeployAuth(rest, opts)
|
|
41
|
+
case 'mint':
|
|
42
|
+
return tokenMint(rest, opts)
|
|
35
43
|
case 'buy':
|
|
36
44
|
return tokenBuy(rest, opts)
|
|
37
45
|
default:
|
|
38
|
-
printCommandHelp('tokens',
|
|
39
|
-
balances: 'Show token balances by token ID',
|
|
40
|
-
list: 'List owned token UTXOs (--token-id <id>)',
|
|
41
|
-
send: 'Transfer tokens (--token-id <id> --to <addr> --amount <n>)',
|
|
42
|
-
deploy: 'Deploy a new BSV21 token (not yet available)',
|
|
43
|
-
buy: 'Purchase listed tokens (--outpoint <op> --token-id <id> --amount <n>)',
|
|
44
|
-
})
|
|
46
|
+
printCommandHelp('tokens', opts.json)
|
|
45
47
|
if (subcommand && subcommand !== 'help') {
|
|
46
48
|
process.exit(1)
|
|
47
49
|
}
|
|
@@ -131,23 +133,47 @@ async function tokenList(args: string[], opts: GlobalFlags): Promise<void> {
|
|
|
131
133
|
}
|
|
132
134
|
}
|
|
133
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Build a {@link Destination} from CLI flags. Only the first set wins.
|
|
138
|
+
* Returns undefined when no flags are set — actions interpret that as 'self'.
|
|
139
|
+
*/
|
|
140
|
+
function destinationFromFlags(opts: {
|
|
141
|
+
to?: string
|
|
142
|
+
counterparty?: string
|
|
143
|
+
lockingScript?: string
|
|
144
|
+
}): Destination | undefined {
|
|
145
|
+
if (opts.lockingScript) return { lockingScript: opts.lockingScript }
|
|
146
|
+
if (opts.counterparty) return { counterparty: opts.counterparty }
|
|
147
|
+
if (opts.to) return { address: opts.to }
|
|
148
|
+
return undefined
|
|
149
|
+
}
|
|
150
|
+
|
|
134
151
|
async function tokenSend(args: string[], opts: GlobalFlags): Promise<void> {
|
|
135
152
|
const tokenId = extractFlag(args, '--token-id')
|
|
136
153
|
const to = extractFlag(args, '--to')
|
|
154
|
+
const counterparty = extractFlag(args, '--counterparty')
|
|
155
|
+
const lockingScript = extractFlag(args, '--locking-script')
|
|
137
156
|
const amountStr = extractFlag(args, '--amount')
|
|
138
157
|
|
|
139
158
|
if (!tokenId) fatal('Missing --token-id <id>')
|
|
140
|
-
if (!to) fatal('Missing --to <address>')
|
|
141
159
|
if (!amountStr) fatal('Missing --amount <number>')
|
|
142
160
|
|
|
161
|
+
const destination = destinationFromFlags({ to, counterparty, lockingScript })
|
|
162
|
+
if (!destination) {
|
|
163
|
+
fatal(
|
|
164
|
+
'Missing destination — provide one of: --to <address>, --counterparty <pubkey>, --locking-script <hex>',
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
143
168
|
const amount = BigInt(amountStr)
|
|
144
169
|
if (amount <= 0n) {
|
|
145
170
|
fatal('--amount must be a positive number')
|
|
146
171
|
}
|
|
147
172
|
|
|
148
173
|
if (!opts.yes) {
|
|
174
|
+
const dest = to ?? counterparty ?? lockingScript ?? ''
|
|
149
175
|
const ok = await confirm({
|
|
150
|
-
message: `Send ${amountStr} tokens (${tokenId.slice(0, 12)}...) to ${
|
|
176
|
+
message: `Send ${amountStr} tokens (${tokenId.slice(0, 12)}...) to ${dest.slice(0, 32)}?`,
|
|
151
177
|
})
|
|
152
178
|
if (isCancel(ok) || !ok) {
|
|
153
179
|
fatal('Token send cancelled.')
|
|
@@ -162,7 +188,9 @@ async function tokenSend(args: string[], opts: GlobalFlags): Promise<void> {
|
|
|
162
188
|
try {
|
|
163
189
|
const result = await sendBsv21.execute(ctx, {
|
|
164
190
|
tokenId,
|
|
165
|
-
recipients: [
|
|
191
|
+
recipients: [
|
|
192
|
+
{ amount: amountStr, destination: destination as Destination },
|
|
193
|
+
],
|
|
166
194
|
})
|
|
167
195
|
|
|
168
196
|
if (result.error) {
|
|
@@ -175,10 +203,214 @@ async function tokenSend(args: string[], opts: GlobalFlags): Promise<void> {
|
|
|
175
203
|
}
|
|
176
204
|
}
|
|
177
205
|
|
|
178
|
-
async function
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
206
|
+
async function tokenDeployMint(
|
|
207
|
+
args: string[],
|
|
208
|
+
opts: GlobalFlags,
|
|
209
|
+
): Promise<void> {
|
|
210
|
+
const symbol = extractFlag(args, '--symbol')
|
|
211
|
+
const amountStr = extractFlag(args, '--amount')
|
|
212
|
+
const decimalsStr = extractFlag(args, '--decimals')
|
|
213
|
+
const icon = extractFlag(args, '--icon')
|
|
214
|
+
const to = extractFlag(args, '--to')
|
|
215
|
+
const counterparty = extractFlag(args, '--counterparty')
|
|
216
|
+
const lockingScript = extractFlag(args, '--locking-script')
|
|
217
|
+
|
|
218
|
+
if (!symbol) fatal('Missing --symbol <ticker>')
|
|
219
|
+
if (!amountStr) fatal('Missing --amount <total-supply>')
|
|
220
|
+
|
|
221
|
+
const amount = BigInt(amountStr)
|
|
222
|
+
if (amount <= 0n) fatal('--amount must be a positive number')
|
|
223
|
+
|
|
224
|
+
const decimals = decimalsStr ? Number.parseInt(decimalsStr, 10) : 0
|
|
225
|
+
if (Number.isNaN(decimals) || decimals < 0 || decimals > 18) {
|
|
226
|
+
fatal('--decimals must be an integer between 0 and 18')
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!opts.yes) {
|
|
230
|
+
const ok = await confirm({
|
|
231
|
+
message: `Deploy ${symbol} with fixed supply ${amountStr} (decimals=${decimals})?`,
|
|
232
|
+
})
|
|
233
|
+
if (isCancel(ok) || !ok) {
|
|
234
|
+
fatal('Deploy cancelled.')
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const privateKey = await loadKey()
|
|
239
|
+
const { ctx, destroy } = await loadContext(privateKey, {
|
|
240
|
+
chain: opts.chain,
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
const result = await deployBsv21Mint.execute(ctx, {
|
|
245
|
+
symbol,
|
|
246
|
+
amount: amountStr,
|
|
247
|
+
decimals,
|
|
248
|
+
icon,
|
|
249
|
+
destination: destinationFromFlags({ to, counterparty, lockingScript }),
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
if (result.error) {
|
|
253
|
+
fatal(result.error)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
output(
|
|
257
|
+
opts.json ? result : { txid: result.txid, tokenId: result.tokenId },
|
|
258
|
+
opts,
|
|
259
|
+
)
|
|
260
|
+
} finally {
|
|
261
|
+
await destroy()
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async function tokenDeployAuth(
|
|
266
|
+
args: string[],
|
|
267
|
+
opts: GlobalFlags,
|
|
268
|
+
): Promise<void> {
|
|
269
|
+
const symbol = extractFlag(args, '--symbol')
|
|
270
|
+
const decimalsStr = extractFlag(args, '--decimals')
|
|
271
|
+
const icon = extractFlag(args, '--icon')
|
|
272
|
+
const to = extractFlag(args, '--to')
|
|
273
|
+
const counterparty = extractFlag(args, '--counterparty')
|
|
274
|
+
const lockingScript = extractFlag(args, '--locking-script')
|
|
275
|
+
|
|
276
|
+
if (!symbol) fatal('Missing --symbol <ticker>')
|
|
277
|
+
|
|
278
|
+
const decimals = decimalsStr ? Number.parseInt(decimalsStr, 10) : 0
|
|
279
|
+
if (Number.isNaN(decimals) || decimals < 0 || decimals > 18) {
|
|
280
|
+
fatal('--decimals must be an integer between 0 and 18')
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (!opts.yes) {
|
|
284
|
+
const ok = await confirm({
|
|
285
|
+
message: `Deploy ${symbol} as a mintable token (decimals=${decimals})?`,
|
|
286
|
+
})
|
|
287
|
+
if (isCancel(ok) || !ok) {
|
|
288
|
+
fatal('Deploy cancelled.')
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const privateKey = await loadKey()
|
|
293
|
+
const { ctx, destroy } = await loadContext(privateKey, {
|
|
294
|
+
chain: opts.chain,
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
const result = await deployBsv21Auth.execute(ctx, {
|
|
299
|
+
symbol,
|
|
300
|
+
decimals,
|
|
301
|
+
icon,
|
|
302
|
+
destination: destinationFromFlags({ to, counterparty, lockingScript }),
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
if (result.error) {
|
|
306
|
+
fatal(result.error)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
output(
|
|
310
|
+
opts.json
|
|
311
|
+
? result
|
|
312
|
+
: {
|
|
313
|
+
txid: result.txid,
|
|
314
|
+
tokenId: result.tokenId,
|
|
315
|
+
authOutpoint: result.authOutpoint,
|
|
316
|
+
},
|
|
317
|
+
opts,
|
|
318
|
+
)
|
|
319
|
+
} finally {
|
|
320
|
+
await destroy()
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async function tokenMint(args: string[], opts: GlobalFlags): Promise<void> {
|
|
325
|
+
const tokenId = extractFlag(args, '--token-id')
|
|
326
|
+
const amountStr = extractFlag(args, '--amount')
|
|
327
|
+
|
|
328
|
+
const mintTo = extractFlag(args, '--to')
|
|
329
|
+
const mintCounterparty = extractFlag(args, '--counterparty')
|
|
330
|
+
const mintLockingScript = extractFlag(args, '--locking-script')
|
|
331
|
+
|
|
332
|
+
const authTo = extractFlag(args, '--auth-to')
|
|
333
|
+
const authCounterparty = extractFlag(args, '--auth-counterparty')
|
|
334
|
+
const authLockingScript = extractFlag(args, '--auth-locking-script')
|
|
335
|
+
|
|
336
|
+
const endMinting = args.includes('--end-minting')
|
|
337
|
+
|
|
338
|
+
if (!tokenId) fatal('Missing --token-id <id>')
|
|
339
|
+
|
|
340
|
+
const mintDestination = destinationFromFlags({
|
|
341
|
+
to: mintTo,
|
|
342
|
+
counterparty: mintCounterparty,
|
|
343
|
+
lockingScript: mintLockingScript,
|
|
344
|
+
})
|
|
345
|
+
const authDestination = destinationFromFlags({
|
|
346
|
+
to: authTo,
|
|
347
|
+
counterparty: authCounterparty,
|
|
348
|
+
lockingScript: authLockingScript,
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
// At least one operation
|
|
352
|
+
if (!amountStr && !authDestination && !endMinting) {
|
|
353
|
+
fatal(
|
|
354
|
+
'Provide either --amount (and optional mint destination) or auth destination, or --end-minting to burn authority',
|
|
355
|
+
)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const mint = amountStr
|
|
359
|
+
? {
|
|
360
|
+
amount: amountStr,
|
|
361
|
+
// Default mint destination to self when only amount is provided
|
|
362
|
+
destination: mintDestination ?? ({} as Destination),
|
|
363
|
+
}
|
|
364
|
+
: undefined
|
|
365
|
+
// Default auth destination to self when not ending minting. Symmetric
|
|
366
|
+
// with the mint default above. The action treats omitted `auth` as
|
|
367
|
+
// "burn authority" and requires `endMinting: true` for that, so leave
|
|
368
|
+
// auth undefined only when the user explicitly opted into ending.
|
|
369
|
+
const auth = endMinting
|
|
370
|
+
? authDestination
|
|
371
|
+
? { destination: authDestination }
|
|
372
|
+
: undefined
|
|
373
|
+
: { destination: authDestination ?? ({} as Destination) }
|
|
374
|
+
|
|
375
|
+
if (!opts.yes) {
|
|
376
|
+
const summary: string[] = []
|
|
377
|
+
if (mint) summary.push(`mint ${amountStr}`)
|
|
378
|
+
if (auth) summary.push('re-issue auth')
|
|
379
|
+
if (!auth && endMinting) summary.push('END MINTING (burn auth)')
|
|
380
|
+
const ok = await confirm({
|
|
381
|
+
message: `${summary.join(' + ')} for token ${tokenId.slice(0, 12)}...?`,
|
|
382
|
+
})
|
|
383
|
+
if (isCancel(ok) || !ok) {
|
|
384
|
+
fatal('Mint cancelled.')
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const privateKey = await loadKey()
|
|
389
|
+
const { ctx, destroy } = await loadContext(privateKey, {
|
|
390
|
+
chain: opts.chain,
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
try {
|
|
394
|
+
const result = await mintBsv21.execute(ctx, {
|
|
395
|
+
tokenId,
|
|
396
|
+
mint,
|
|
397
|
+
auth,
|
|
398
|
+
endMinting: endMinting || undefined,
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
if (result.error) {
|
|
402
|
+
fatal(result.error)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
output(
|
|
406
|
+
opts.json
|
|
407
|
+
? result
|
|
408
|
+
: { txid: result.txid, authOutpoint: result.authOutpoint },
|
|
409
|
+
opts,
|
|
410
|
+
)
|
|
411
|
+
} finally {
|
|
412
|
+
await destroy()
|
|
413
|
+
}
|
|
182
414
|
}
|
|
183
415
|
|
|
184
416
|
async function tokenBuy(args: string[], opts: GlobalFlags): Promise<void> {
|
package/src/commands/tx.ts
CHANGED
|
@@ -17,9 +17,7 @@ export async function handleTxCommand(
|
|
|
17
17
|
case 'decode':
|
|
18
18
|
return txDecode(rest, opts)
|
|
19
19
|
default:
|
|
20
|
-
printCommandHelp('tx',
|
|
21
|
-
decode: 'Decode a raw transaction hex',
|
|
22
|
-
})
|
|
20
|
+
printCommandHelp('tx', opts.json)
|
|
23
21
|
if (subcommand && subcommand !== 'help') {
|
|
24
22
|
process.exit(1)
|
|
25
23
|
}
|
package/src/commands/wallet.ts
CHANGED
|
@@ -53,26 +53,7 @@ export async function handleWalletCommand(
|
|
|
53
53
|
case 'relinquish-certificate':
|
|
54
54
|
return walletRelinquishCertificate(rest, opts)
|
|
55
55
|
default:
|
|
56
|
-
printCommandHelp('wallet',
|
|
57
|
-
balance: 'Show wallet balance in satoshis',
|
|
58
|
-
address:
|
|
59
|
-
'Show deposit address [--prefix <p>] [--start-index <n>] [--count <n>]',
|
|
60
|
-
send: 'Send BSV (--to <addr> --sats <n> | --script <hex> --sats <n> | --data-asm "<asm>")',
|
|
61
|
-
'send-all': 'Send all BSV to an address (--to <addr>)',
|
|
62
|
-
sync: 'Sync inbound payments at BRC-29 deposit addresses [--prefix <p>] [--start-index <n>] [--count <n>]',
|
|
63
|
-
info: 'Show wallet info (address, balance, network)',
|
|
64
|
-
'list-outputs':
|
|
65
|
-
'List wallet outputs (--basket <name> [--tags <t1,t2>] [--limit N] [--include-tags] [--include <val>])',
|
|
66
|
-
'relinquish-output':
|
|
67
|
-
'Remove output from basket (--basket <name> --output <txid.vout>)',
|
|
68
|
-
'list-actions': 'List wallet actions [--labels <l1,l2>] [--limit N]',
|
|
69
|
-
'create-action': 'Create action (JSON args)',
|
|
70
|
-
'sign-action': 'Sign action (JSON args)',
|
|
71
|
-
'abort-action': 'Abort action (--reference <ref>)',
|
|
72
|
-
'list-certificates': 'List certificates',
|
|
73
|
-
'relinquish-certificate':
|
|
74
|
-
'Relinquish certificate (--type <t> --serialNumber <s> --certifier <c>)',
|
|
75
|
-
})
|
|
56
|
+
printCommandHelp('wallet', opts.json)
|
|
76
57
|
if (subcommand && subcommand !== 'help') {
|
|
77
58
|
process.exit(1)
|
|
78
59
|
}
|
package/src/config.ts
CHANGED
|
@@ -40,6 +40,28 @@ export interface ServerAccountsConfig {
|
|
|
40
40
|
freeIdentityKeys?: string[]
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
export interface ServerMessageboxConfig {
|
|
44
|
+
/** Port to bind. Defaults to `8101`. */
|
|
45
|
+
port?: number
|
|
46
|
+
/** Enable WebSocket delivery. Defaults to `true`. */
|
|
47
|
+
websockets?: boolean
|
|
48
|
+
/**
|
|
49
|
+
* SQLite file path for messagebox storage. Used only when wallet storage
|
|
50
|
+
* is `bun-sqlite`. Defaults to `<dataDir>/messagebox-{chain}.db`.
|
|
51
|
+
*/
|
|
52
|
+
dbPath?: string
|
|
53
|
+
/**
|
|
54
|
+
* Postgres schema for messagebox tables. Used only when wallet storage
|
|
55
|
+
* is `pg`. Defaults to `messagebox`.
|
|
56
|
+
*/
|
|
57
|
+
pgSchema?: string
|
|
58
|
+
/**
|
|
59
|
+
* Override the wallet storage URL messagebox calls for BRC-31/BRC-29
|
|
60
|
+
* operations. Defaults to the local wallet server URL (`http://<host>:<port>/`).
|
|
61
|
+
*/
|
|
62
|
+
walletStorageUrl?: string
|
|
63
|
+
}
|
|
64
|
+
|
|
43
65
|
export interface ServerConfig {
|
|
44
66
|
/** Hostname to bind. Defaults to `127.0.0.1`. */
|
|
45
67
|
host?: string
|
|
@@ -49,6 +71,8 @@ export interface ServerConfig {
|
|
|
49
71
|
storage?: ServerStorageConfig
|
|
50
72
|
/** Optional account/metering layer (opt-in per-deployment). */
|
|
51
73
|
accounts?: ServerAccountsConfig
|
|
74
|
+
/** Messagebox subcommand settings. */
|
|
75
|
+
messagebox?: ServerMessageboxConfig
|
|
52
76
|
}
|
|
53
77
|
|
|
54
78
|
export interface OneSatCliConfig {
|