@1sat/cli 0.0.1 → 0.0.3
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 +2 -2
- package/src/commands/identity.ts +1 -1
- package/src/commands/opns.ts +113 -21
- package/src/commands/sweep.ts +181 -14
- package/src/commands/tokens.ts +44 -18
- package/src/help.ts +10 -4
- package/src/output.ts +17 -1
- package/bin/1sat +0 -0
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1sat/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "CLI for 1Sat Ordinals SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/cli.ts",
|
|
7
7
|
"bin": {
|
|
8
8
|
"1sat": "./src/cli.ts"
|
|
9
9
|
},
|
|
10
|
-
"files": ["src"
|
|
10
|
+
"files": ["src"],
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "bun build ./src/cli.ts --compile --outfile=bin/1sat --external pg --external pg-native --external pg-query-stream --external tedious --external oracledb --external mysql",
|
|
13
13
|
"dev": "bun run src/cli.ts",
|
package/src/commands/identity.ts
CHANGED
package/src/commands/opns.ts
CHANGED
|
@@ -4,10 +4,18 @@
|
|
|
4
4
|
* Manage OpNS name identity bindings.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {
|
|
8
|
+
getOpnsNames,
|
|
9
|
+
opnsDeregister as opnsDeregisterAction,
|
|
10
|
+
opnsRegister as opnsRegisterAction,
|
|
11
|
+
} from '@1sat/actions'
|
|
12
|
+
import { confirm, isCancel } from '@clack/prompts'
|
|
7
13
|
import type { GlobalFlags } from '../args'
|
|
8
14
|
import { extractFlag } from '../args'
|
|
15
|
+
import { loadContext } from '../context'
|
|
9
16
|
import { printCommandHelp } from '../help'
|
|
10
|
-
import {
|
|
17
|
+
import { loadKey, resolvePassword } from '../keys'
|
|
18
|
+
import { fatal, formatLabel, formatValue, output } from '../output'
|
|
11
19
|
|
|
12
20
|
export async function handleOpnsCommand(
|
|
13
21
|
args: string[],
|
|
@@ -26,7 +34,7 @@ export async function handleOpnsCommand(
|
|
|
26
34
|
printCommandHelp('opns', {
|
|
27
35
|
register: 'Register identity on an OpNS name (--outpoint <op>)',
|
|
28
36
|
deregister: 'Deregister identity from an OpNS name (--outpoint <op>)',
|
|
29
|
-
lookup: '
|
|
37
|
+
lookup: 'List OpNS names from wallet',
|
|
30
38
|
})
|
|
31
39
|
if (subcommand && subcommand !== 'help') {
|
|
32
40
|
process.exit(1)
|
|
@@ -34,40 +42,124 @@ export async function handleOpnsCommand(
|
|
|
34
42
|
}
|
|
35
43
|
}
|
|
36
44
|
|
|
37
|
-
async function opnsRegister(args: string[],
|
|
45
|
+
async function opnsRegister(args: string[], opts: GlobalFlags): Promise<void> {
|
|
38
46
|
const outpoint = extractFlag(args, '--outpoint')
|
|
39
47
|
|
|
40
48
|
if (!outpoint) fatal('Missing --outpoint <txid.vout>')
|
|
41
49
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
if (!opts.yes) {
|
|
51
|
+
const ok = await confirm({
|
|
52
|
+
message: `Register identity on OpNS name ${outpoint}?`,
|
|
53
|
+
})
|
|
54
|
+
if (isCancel(ok) || !ok) {
|
|
55
|
+
fatal('Registration cancelled.')
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const privateKey = await loadKey(resolvePassword())
|
|
60
|
+
const { ctx, destroy } = await loadContext(privateKey, {
|
|
61
|
+
chain: opts.chain,
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
// Look up the OpNS ordinal from the wallet
|
|
66
|
+
const namesResult = await getOpnsNames.execute(ctx, { limit: 10000 })
|
|
67
|
+
const ordinal = namesResult.outputs.find((o) => o.outpoint === outpoint)
|
|
68
|
+
if (!ordinal) {
|
|
69
|
+
fatal(`OpNS name not found in wallet: ${outpoint}`)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const result = await opnsRegisterAction.execute(ctx, {
|
|
73
|
+
ordinal,
|
|
74
|
+
inputBEEF: namesResult.BEEF as number[] | undefined,
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
if (result.error) {
|
|
78
|
+
fatal(result.error)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
output(opts.json ? result : { txid: result.txid }, opts)
|
|
82
|
+
} finally {
|
|
83
|
+
await destroy()
|
|
84
|
+
}
|
|
47
85
|
}
|
|
48
86
|
|
|
49
87
|
async function opnsDeregister(
|
|
50
88
|
args: string[],
|
|
51
|
-
|
|
89
|
+
opts: GlobalFlags,
|
|
52
90
|
): Promise<void> {
|
|
53
91
|
const outpoint = extractFlag(args, '--outpoint')
|
|
54
92
|
|
|
55
93
|
if (!outpoint) fatal('Missing --outpoint <txid.vout>')
|
|
56
94
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
95
|
+
if (!opts.yes) {
|
|
96
|
+
const ok = await confirm({
|
|
97
|
+
message: `Deregister identity from OpNS name ${outpoint}?`,
|
|
98
|
+
})
|
|
99
|
+
if (isCancel(ok) || !ok) {
|
|
100
|
+
fatal('Deregistration cancelled.')
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const privateKey = await loadKey(resolvePassword())
|
|
105
|
+
const { ctx, destroy } = await loadContext(privateKey, {
|
|
106
|
+
chain: opts.chain,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
// Look up the OpNS ordinal from the wallet
|
|
111
|
+
const namesResult = await getOpnsNames.execute(ctx, { limit: 10000 })
|
|
112
|
+
const ordinal = namesResult.outputs.find((o) => o.outpoint === outpoint)
|
|
113
|
+
if (!ordinal) {
|
|
114
|
+
fatal(`OpNS name not found in wallet: ${outpoint}`)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const result = await opnsDeregisterAction.execute(ctx, {
|
|
118
|
+
ordinal,
|
|
119
|
+
inputBEEF: namesResult.BEEF as number[] | undefined,
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
if (result.error) {
|
|
123
|
+
fatal(result.error)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
output(opts.json ? result : { txid: result.txid }, opts)
|
|
127
|
+
} finally {
|
|
128
|
+
await destroy()
|
|
129
|
+
}
|
|
62
130
|
}
|
|
63
131
|
|
|
64
|
-
async function opnsLookup(
|
|
65
|
-
const
|
|
132
|
+
async function opnsLookup(_args: string[], opts: GlobalFlags): Promise<void> {
|
|
133
|
+
const privateKey = await loadKey(resolvePassword())
|
|
134
|
+
const { ctx, destroy } = await loadContext(privateKey, {
|
|
135
|
+
chain: opts.chain,
|
|
136
|
+
})
|
|
66
137
|
|
|
67
|
-
|
|
138
|
+
try {
|
|
139
|
+
const result = await getOpnsNames.execute(ctx, { limit: 100 })
|
|
68
140
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
141
|
+
if (opts.json) {
|
|
142
|
+
output(result.outputs, opts)
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (result.outputs.length === 0) {
|
|
147
|
+
output('No OpNS names found.', opts)
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
for (const o of result.outputs) {
|
|
152
|
+
const nameTag = o.tags?.find((t) => t.startsWith('name:'))?.slice(5) ?? ''
|
|
153
|
+
const publishedTag = o.tags?.find((t) => t === 'opns:published')
|
|
154
|
+
const status = publishedTag ? 'registered' : 'unregistered'
|
|
155
|
+
|
|
156
|
+
console.log(
|
|
157
|
+
` ${formatValue(o.outpoint)} ${nameTag ? formatValue(nameTag) : ''} ${formatLabel(status)}`,
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
console.log(`\n ${result.outputs.length} OpNS name(s) found.`)
|
|
162
|
+
} finally {
|
|
163
|
+
await destroy()
|
|
164
|
+
}
|
|
73
165
|
}
|
package/src/commands/sweep.ts
CHANGED
|
@@ -4,10 +4,21 @@
|
|
|
4
4
|
* Sweep assets from external wallets into the BRC-100 wallet.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {
|
|
8
|
+
prepareSweepInputs,
|
|
9
|
+
scanAddressUtxos,
|
|
10
|
+
sweepBsv,
|
|
11
|
+
sweepBsv21,
|
|
12
|
+
sweepOrdinals,
|
|
13
|
+
} from '@1sat/actions'
|
|
14
|
+
import { PrivateKey } from '@bsv/sdk'
|
|
15
|
+
import { confirm, isCancel } from '@clack/prompts'
|
|
7
16
|
import type { GlobalFlags } from '../args'
|
|
8
17
|
import { extractFlag } from '../args'
|
|
18
|
+
import { loadContext } from '../context'
|
|
9
19
|
import { printCommandHelp } from '../help'
|
|
10
|
-
import {
|
|
20
|
+
import { loadKey, resolvePassword } from '../keys'
|
|
21
|
+
import { fatal, formatLabel, formatValue, output } from '../output'
|
|
11
22
|
|
|
12
23
|
export async function handleSweepCommand(
|
|
13
24
|
args: string[],
|
|
@@ -22,7 +33,7 @@ export async function handleSweepCommand(
|
|
|
22
33
|
return sweepImport(rest, opts)
|
|
23
34
|
default:
|
|
24
35
|
printCommandHelp('sweep', {
|
|
25
|
-
scan: 'Scan an address for UTXOs (--
|
|
36
|
+
scan: 'Scan an address for UTXOs (--wif <key>)',
|
|
26
37
|
import: 'Import UTXOs into wallet (--wif <key>)',
|
|
27
38
|
})
|
|
28
39
|
if (subcommand && subcommand !== 'help') {
|
|
@@ -31,24 +42,180 @@ export async function handleSweepCommand(
|
|
|
31
42
|
}
|
|
32
43
|
}
|
|
33
44
|
|
|
34
|
-
async function sweepScan(args: string[],
|
|
35
|
-
const
|
|
45
|
+
async function sweepScan(args: string[], opts: GlobalFlags): Promise<void> {
|
|
46
|
+
const wif = extractFlag(args, '--wif')
|
|
47
|
+
|
|
48
|
+
if (!wif) fatal('Missing --wif <private-key>')
|
|
49
|
+
|
|
50
|
+
let address: string
|
|
51
|
+
try {
|
|
52
|
+
const pk = PrivateKey.fromWif(wif)
|
|
53
|
+
address = pk.toPublicKey().toAddress()
|
|
54
|
+
} catch {
|
|
55
|
+
fatal('Invalid WIF private key')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const privateKey = await loadKey(resolvePassword())
|
|
59
|
+
const { ctx, destroy } = await loadContext(privateKey, {
|
|
60
|
+
chain: opts.chain,
|
|
61
|
+
})
|
|
36
62
|
|
|
37
|
-
|
|
63
|
+
try {
|
|
64
|
+
if (!ctx.services) {
|
|
65
|
+
fatal('Services required for sweep scan')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const result = await scanAddressUtxos(ctx.services, address)
|
|
69
|
+
|
|
70
|
+
if (opts.json) {
|
|
71
|
+
output(result, opts)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log(`\n Scan results for ${formatValue(address)}:\n`)
|
|
76
|
+
|
|
77
|
+
console.log(` ${formatLabel('Funding UTXOs:')} ${result.funding.length}`)
|
|
78
|
+
if (result.funding.length > 0) {
|
|
79
|
+
console.log(` ${formatLabel('Total funding:')} ${formatValue(result.totalFundingSats)} satoshis`)
|
|
80
|
+
for (const f of result.funding) {
|
|
81
|
+
console.log(` ${formatValue(f.outpoint)} ${formatLabel(`${f.satoshis} sats`)}`)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
38
84
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
85
|
+
console.log(`\n ${formatLabel('Ordinal UTXOs:')} ${result.ordinals.length}`)
|
|
86
|
+
if (result.ordinals.length > 0) {
|
|
87
|
+
for (const o of result.ordinals) {
|
|
88
|
+
console.log(` ${formatValue(o.outpoint)}`)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log(`\n ${formatLabel('BSV-21 Tokens:')} ${result.bsv21Tokens.length}`)
|
|
93
|
+
if (result.bsv21Tokens.length > 0) {
|
|
94
|
+
for (const t of result.bsv21Tokens) {
|
|
95
|
+
console.log(
|
|
96
|
+
` ${formatValue(t.symbol ?? t.tokenId.slice(0, 12))} ${formatLabel('amount:')} ${formatValue(t.totalAmount)} ${formatLabel('UTXOs:')} ${t.inputs.length} ${formatLabel('dec:')} ${t.decimals}`,
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const total = result.funding.length + result.ordinals.length + result.bsv21Tokens.reduce((n, t) => n + t.inputs.length, 0)
|
|
102
|
+
console.log(`\n ${total} total UTXO(s) found.`)
|
|
103
|
+
} finally {
|
|
104
|
+
await destroy()
|
|
105
|
+
}
|
|
43
106
|
}
|
|
44
107
|
|
|
45
|
-
async function sweepImport(args: string[],
|
|
108
|
+
async function sweepImport(args: string[], opts: GlobalFlags): Promise<void> {
|
|
46
109
|
const wif = extractFlag(args, '--wif')
|
|
47
110
|
|
|
48
111
|
if (!wif) fatal('Missing --wif <private-key>')
|
|
49
112
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
113
|
+
let address: string
|
|
114
|
+
try {
|
|
115
|
+
const pk = PrivateKey.fromWif(wif)
|
|
116
|
+
address = pk.toPublicKey().toAddress()
|
|
117
|
+
} catch {
|
|
118
|
+
fatal('Invalid WIF private key')
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const privateKey = await loadKey(resolvePassword())
|
|
122
|
+
const { ctx, destroy } = await loadContext(privateKey, {
|
|
123
|
+
chain: opts.chain,
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
if (!ctx.services) {
|
|
128
|
+
fatal('Services required for sweep import')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Scan first to discover what's available
|
|
132
|
+
const scan = await scanAddressUtxos(ctx.services, address)
|
|
133
|
+
|
|
134
|
+
const hasFunding = scan.funding.length > 0
|
|
135
|
+
const hasOrdinals = scan.ordinals.length > 0
|
|
136
|
+
const hasTokens = scan.bsv21Tokens.length > 0
|
|
137
|
+
|
|
138
|
+
if (!hasFunding && !hasOrdinals && !hasTokens) {
|
|
139
|
+
fatal(`No UTXOs found at ${address}`)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Summarize what will be swept
|
|
143
|
+
const parts: string[] = []
|
|
144
|
+
if (hasFunding) parts.push(`${scan.totalFundingSats} sats (${scan.funding.length} UTXOs)`)
|
|
145
|
+
if (hasOrdinals) parts.push(`${scan.ordinals.length} ordinal(s)`)
|
|
146
|
+
if (hasTokens) {
|
|
147
|
+
for (const t of scan.bsv21Tokens) {
|
|
148
|
+
parts.push(`${t.totalAmount} ${t.symbol ?? t.tokenId.slice(0, 12)} token(s)`)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!opts.yes) {
|
|
153
|
+
const ok = await confirm({
|
|
154
|
+
message: `Sweep ${parts.join(', ')} from ${address}?`,
|
|
155
|
+
})
|
|
156
|
+
if (isCancel(ok) || !ok) {
|
|
157
|
+
fatal('Sweep cancelled.')
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const txids: string[] = []
|
|
162
|
+
|
|
163
|
+
// Convert SweepInput to IndexedOutput shape for prepareSweepInputs
|
|
164
|
+
const toIndexed = (items: Array<{ outpoint: string; satoshis: number }>) =>
|
|
165
|
+
items.map((item) => ({
|
|
166
|
+
outpoint: item.outpoint,
|
|
167
|
+
satoshis: item.satoshis,
|
|
168
|
+
score: 0,
|
|
169
|
+
}))
|
|
170
|
+
|
|
171
|
+
// Sweep BSV funding UTXOs
|
|
172
|
+
if (hasFunding) {
|
|
173
|
+
const inputs = await prepareSweepInputs(ctx, toIndexed(scan.funding))
|
|
174
|
+
|
|
175
|
+
const result = await sweepBsv.execute(ctx, { inputs, wif })
|
|
176
|
+
if (result.error) {
|
|
177
|
+
fatal(`BSV sweep failed: ${result.error}`)
|
|
178
|
+
}
|
|
179
|
+
if (result.txid) txids.push(result.txid)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Sweep ordinals
|
|
183
|
+
if (hasOrdinals) {
|
|
184
|
+
const inputs = await prepareSweepInputs(ctx, toIndexed(scan.ordinals))
|
|
185
|
+
|
|
186
|
+
const result = await sweepOrdinals.execute(ctx, { inputs, wif })
|
|
187
|
+
if (result.error) {
|
|
188
|
+
fatal(`Ordinals sweep failed: ${result.error}`)
|
|
189
|
+
}
|
|
190
|
+
if (result.txid) txids.push(result.txid)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Sweep BSV-21 tokens (one sweep per tokenId)
|
|
194
|
+
if (hasTokens) {
|
|
195
|
+
for (const tokenGroup of scan.bsv21Tokens) {
|
|
196
|
+
const inputs = await prepareSweepInputs(ctx, toIndexed(tokenGroup.inputs))
|
|
197
|
+
|
|
198
|
+
const sweepInputs = inputs.map((inp, idx) => ({
|
|
199
|
+
...inp,
|
|
200
|
+
tokenId: tokenGroup.inputs[idx].tokenId,
|
|
201
|
+
amount: tokenGroup.inputs[idx].amount,
|
|
202
|
+
}))
|
|
203
|
+
|
|
204
|
+
const result = await sweepBsv21.execute(ctx, { inputs: sweepInputs, wif })
|
|
205
|
+
if (result.error) {
|
|
206
|
+
fatal(`Token sweep failed (${tokenGroup.symbol ?? tokenGroup.tokenId.slice(0, 12)}): ${result.error}`)
|
|
207
|
+
}
|
|
208
|
+
if (result.txid) txids.push(result.txid)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
output(
|
|
213
|
+
opts.json
|
|
214
|
+
? { txids, swept: parts }
|
|
215
|
+
: `Sweep complete. ${txids.length} transaction(s): ${txids.join(', ')}`,
|
|
216
|
+
opts,
|
|
217
|
+
)
|
|
218
|
+
} finally {
|
|
219
|
+
await destroy()
|
|
220
|
+
}
|
|
54
221
|
}
|
package/src/commands/tokens.ts
CHANGED
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
* Token commands - balances, list, send, deploy, buy.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
getBsv21Balances,
|
|
7
|
+
listTokens,
|
|
8
|
+
purchaseBsv21,
|
|
9
|
+
sendBsv21,
|
|
10
|
+
} from '@1sat/actions'
|
|
6
11
|
import { confirm, isCancel } from '@clack/prompts'
|
|
7
12
|
import type { GlobalFlags } from '../args'
|
|
8
13
|
import { extractFlag } from '../args'
|
|
@@ -33,8 +38,8 @@ export async function handleTokensCommand(
|
|
|
33
38
|
balances: 'Show token balances by token ID',
|
|
34
39
|
list: 'List owned token UTXOs (--token-id <id>)',
|
|
35
40
|
send: 'Transfer tokens (--token-id <id> --to <addr> --amount <n>)',
|
|
36
|
-
deploy: 'Deploy a new BSV21 token (
|
|
37
|
-
buy: 'Purchase listed tokens (--outpoint <op>)',
|
|
41
|
+
deploy: 'Deploy a new BSV21 token (not yet available)',
|
|
42
|
+
buy: 'Purchase listed tokens (--outpoint <op> --token-id <id> --amount <n>)',
|
|
38
43
|
})
|
|
39
44
|
if (subcommand && subcommand !== 'help') {
|
|
40
45
|
process.exit(1)
|
|
@@ -170,25 +175,46 @@ async function tokenSend(args: string[], opts: GlobalFlags): Promise<void> {
|
|
|
170
175
|
}
|
|
171
176
|
}
|
|
172
177
|
|
|
173
|
-
async function tokenDeploy(
|
|
174
|
-
|
|
175
|
-
const amountStr = extractFlag(args, '--amount')
|
|
176
|
-
const _icon = extractFlag(args, '--icon')
|
|
177
|
-
|
|
178
|
-
if (!symbol) fatal('Missing --symbol <sym>')
|
|
179
|
-
if (!amountStr) fatal('Missing --amount <initial-supply>')
|
|
180
|
-
|
|
181
|
-
// TODO: Call deployToken action
|
|
182
|
-
// TODO: Output txid and token origin
|
|
183
|
-
fatal('tokens deploy is not yet implemented')
|
|
178
|
+
async function tokenDeploy(_args: string[], _opts: GlobalFlags): Promise<void> {
|
|
179
|
+
fatal('tokens deploy is not yet available. No deploy action exists in the actions package.')
|
|
184
180
|
}
|
|
185
181
|
|
|
186
|
-
async function tokenBuy(args: string[],
|
|
182
|
+
async function tokenBuy(args: string[], opts: GlobalFlags): Promise<void> {
|
|
187
183
|
const outpoint = extractFlag(args, '--outpoint')
|
|
184
|
+
const tokenId = extractFlag(args, '--token-id')
|
|
185
|
+
const amountStr = extractFlag(args, '--amount')
|
|
188
186
|
|
|
189
187
|
if (!outpoint) fatal('Missing --outpoint <txid.vout>')
|
|
188
|
+
if (!tokenId) fatal('Missing --token-id <id>')
|
|
189
|
+
if (!amountStr) fatal('Missing --amount <token-amount>')
|
|
190
|
+
|
|
191
|
+
if (!opts.yes) {
|
|
192
|
+
const ok = await confirm({
|
|
193
|
+
message: `Purchase ${amountStr} tokens (${tokenId.slice(0, 12)}...) from listing ${outpoint}?`,
|
|
194
|
+
})
|
|
195
|
+
if (isCancel(ok) || !ok) {
|
|
196
|
+
fatal('Purchase cancelled.')
|
|
197
|
+
}
|
|
198
|
+
}
|
|
190
199
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
200
|
+
const privateKey = await loadKey(resolvePassword())
|
|
201
|
+
const { ctx, destroy } = await loadContext(privateKey, {
|
|
202
|
+
chain: opts.chain,
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const result = await purchaseBsv21.execute(ctx, {
|
|
207
|
+
tokenId,
|
|
208
|
+
outpoint,
|
|
209
|
+
amount: amountStr,
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
if (result.error) {
|
|
213
|
+
fatal(result.error)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
output(opts.json ? result : { txid: result.txid }, opts)
|
|
217
|
+
} finally {
|
|
218
|
+
await destroy()
|
|
219
|
+
}
|
|
194
220
|
}
|
package/src/help.ts
CHANGED
|
@@ -2,18 +2,24 @@
|
|
|
2
2
|
* Help text and version display for the 1sat CLI.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { readFileSync } from 'node:fs'
|
|
6
5
|
import chalk from 'chalk'
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
// Version is read at build time via Bun's import. Works in both source and compiled binary.
|
|
8
|
+
const VERSION = (() => {
|
|
9
9
|
try {
|
|
10
|
+
// biome-ignore lint/style/noVar: dynamic require for Bun compiled binary compat
|
|
11
|
+
const { readFileSync } = require('node:fs')
|
|
10
12
|
const pkg = JSON.parse(
|
|
11
13
|
readFileSync(new URL('../package.json', import.meta.url), 'utf8'),
|
|
12
14
|
)
|
|
13
|
-
return pkg.version || '
|
|
15
|
+
return pkg.version || '0.0.1'
|
|
14
16
|
} catch {
|
|
15
|
-
return '
|
|
17
|
+
return '0.0.1'
|
|
16
18
|
}
|
|
19
|
+
})()
|
|
20
|
+
|
|
21
|
+
export function getVersion(): string {
|
|
22
|
+
return VERSION
|
|
17
23
|
}
|
|
18
24
|
|
|
19
25
|
export function printVersion(): void {
|
package/src/output.ts
CHANGED
|
@@ -51,7 +51,23 @@ export function output(
|
|
|
51
51
|
|
|
52
52
|
if (typeof data === 'object' && data !== null) {
|
|
53
53
|
for (const [key, value] of Object.entries(data)) {
|
|
54
|
-
|
|
54
|
+
if (Array.isArray(value)) {
|
|
55
|
+
console.log(`${formatLabel(`${key}:`)}`)
|
|
56
|
+
for (const item of value) {
|
|
57
|
+
if (typeof item === 'object' && item !== null) {
|
|
58
|
+
const parts = Object.entries(item)
|
|
59
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
60
|
+
.join(' ')
|
|
61
|
+
console.log(` ${formatValue(parts)}`)
|
|
62
|
+
} else {
|
|
63
|
+
console.log(` ${formatValue(String(item))}`)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
67
|
+
console.log(`${formatLabel(`${key}:`)} ${JSON.stringify(value)}`)
|
|
68
|
+
} else {
|
|
69
|
+
console.log(`${formatLabel(`${key}:`)} ${formatValue(String(value))}`)
|
|
70
|
+
}
|
|
55
71
|
}
|
|
56
72
|
return
|
|
57
73
|
}
|
package/bin/1sat
DELETED
|
Binary file
|