@bsv/sdk 1.10.4 → 2.0.0

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.
Files changed (156) hide show
  1. package/dist/cjs/mod.js +1 -0
  2. package/dist/cjs/mod.js.map +1 -1
  3. package/dist/cjs/package.json +2 -3
  4. package/dist/cjs/src/auth/Peer.js +18 -20
  5. package/dist/cjs/src/auth/Peer.js.map +1 -1
  6. package/dist/cjs/src/identity/IdentityClient.js +20 -124
  7. package/dist/cjs/src/identity/IdentityClient.js.map +1 -1
  8. package/dist/cjs/src/primitives/TransactionSignature.js +115 -10
  9. package/dist/cjs/src/primitives/TransactionSignature.js.map +1 -1
  10. package/dist/cjs/src/primitives/utils.js +13 -112
  11. package/dist/cjs/src/primitives/utils.js.map +1 -1
  12. package/dist/cjs/src/remittance/CommsLayer.js +3 -0
  13. package/dist/cjs/src/remittance/CommsLayer.js.map +1 -0
  14. package/dist/cjs/src/remittance/IdentityLayer.js +3 -0
  15. package/dist/cjs/src/remittance/IdentityLayer.js.map +1 -0
  16. package/dist/cjs/src/remittance/RemittanceManager.js +1245 -0
  17. package/dist/cjs/src/remittance/RemittanceManager.js.map +1 -0
  18. package/dist/cjs/src/remittance/RemittanceModule.js +3 -0
  19. package/dist/cjs/src/remittance/RemittanceModule.js.map +1 -0
  20. package/dist/cjs/src/remittance/index.js +23 -0
  21. package/dist/cjs/src/remittance/index.js.map +1 -0
  22. package/dist/cjs/src/remittance/modules/BasicBRC29.js +225 -0
  23. package/dist/cjs/src/remittance/modules/BasicBRC29.js.map +1 -0
  24. package/dist/cjs/src/remittance/modules/index.js +18 -0
  25. package/dist/cjs/src/remittance/modules/index.js.map +1 -0
  26. package/dist/cjs/src/remittance/types.js +22 -0
  27. package/dist/cjs/src/remittance/types.js.map +1 -0
  28. package/dist/cjs/src/script/OP.js +15 -13
  29. package/dist/cjs/src/script/OP.js.map +1 -1
  30. package/dist/cjs/src/script/Script.js +4 -1
  31. package/dist/cjs/src/script/Script.js.map +1 -1
  32. package/dist/cjs/src/script/Spend.js +128 -46
  33. package/dist/cjs/src/script/Spend.js.map +1 -1
  34. package/dist/cjs/src/transaction/BeefTx.js +2 -2
  35. package/dist/cjs/src/transaction/Transaction.js +160 -0
  36. package/dist/cjs/src/transaction/Transaction.js.map +1 -1
  37. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  38. package/dist/esm/mod.js +1 -0
  39. package/dist/esm/mod.js.map +1 -1
  40. package/dist/esm/src/auth/Peer.js +18 -20
  41. package/dist/esm/src/auth/Peer.js.map +1 -1
  42. package/dist/esm/src/identity/IdentityClient.js +20 -124
  43. package/dist/esm/src/identity/IdentityClient.js.map +1 -1
  44. package/dist/esm/src/primitives/TransactionSignature.js +115 -10
  45. package/dist/esm/src/primitives/TransactionSignature.js.map +1 -1
  46. package/dist/esm/src/primitives/utils.js +13 -112
  47. package/dist/esm/src/primitives/utils.js.map +1 -1
  48. package/dist/esm/src/remittance/CommsLayer.js +2 -0
  49. package/dist/esm/src/remittance/CommsLayer.js.map +1 -0
  50. package/dist/esm/src/remittance/IdentityLayer.js +2 -0
  51. package/dist/esm/src/remittance/IdentityLayer.js.map +1 -0
  52. package/dist/esm/src/remittance/RemittanceManager.js +1254 -0
  53. package/dist/esm/src/remittance/RemittanceManager.js.map +1 -0
  54. package/dist/esm/src/remittance/RemittanceModule.js +2 -0
  55. package/dist/esm/src/remittance/RemittanceModule.js.map +1 -0
  56. package/dist/esm/src/remittance/index.js +7 -0
  57. package/dist/esm/src/remittance/index.js.map +1 -0
  58. package/dist/esm/src/remittance/modules/BasicBRC29.js +227 -0
  59. package/dist/esm/src/remittance/modules/BasicBRC29.js.map +1 -0
  60. package/dist/esm/src/remittance/modules/index.js +2 -0
  61. package/dist/esm/src/remittance/modules/index.js.map +1 -0
  62. package/dist/esm/src/remittance/types.js +19 -0
  63. package/dist/esm/src/remittance/types.js.map +1 -0
  64. package/dist/esm/src/script/OP.js +15 -13
  65. package/dist/esm/src/script/OP.js.map +1 -1
  66. package/dist/esm/src/script/Script.js +4 -1
  67. package/dist/esm/src/script/Script.js.map +1 -1
  68. package/dist/esm/src/script/Spend.js +129 -46
  69. package/dist/esm/src/script/Spend.js.map +1 -1
  70. package/dist/esm/src/transaction/BeefTx.js +3 -3
  71. package/dist/esm/src/transaction/BeefTx.js.map +1 -1
  72. package/dist/esm/src/transaction/Transaction.js +160 -0
  73. package/dist/esm/src/transaction/Transaction.js.map +1 -1
  74. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  75. package/dist/types/mod.d.ts +1 -0
  76. package/dist/types/mod.d.ts.map +1 -1
  77. package/dist/types/src/auth/Peer.d.ts +3 -7
  78. package/dist/types/src/auth/Peer.d.ts.map +1 -1
  79. package/dist/types/src/identity/IdentityClient.d.ts +0 -8
  80. package/dist/types/src/identity/IdentityClient.d.ts.map +1 -1
  81. package/dist/types/src/primitives/TransactionSignature.d.ts +16 -4
  82. package/dist/types/src/primitives/TransactionSignature.d.ts.map +1 -1
  83. package/dist/types/src/primitives/utils.d.ts +1 -0
  84. package/dist/types/src/primitives/utils.d.ts.map +1 -1
  85. package/dist/types/src/remittance/CommsLayer.d.ts +50 -0
  86. package/dist/types/src/remittance/CommsLayer.d.ts.map +1 -0
  87. package/dist/types/src/remittance/IdentityLayer.d.ts +35 -0
  88. package/dist/types/src/remittance/IdentityLayer.d.ts.map +1 -0
  89. package/dist/types/src/remittance/RemittanceManager.d.ts +452 -0
  90. package/dist/types/src/remittance/RemittanceManager.d.ts.map +1 -0
  91. package/dist/types/src/remittance/RemittanceModule.d.ts +106 -0
  92. package/dist/types/src/remittance/RemittanceModule.d.ts.map +1 -0
  93. package/dist/types/src/remittance/index.d.ts +7 -0
  94. package/dist/types/src/remittance/index.d.ts.map +1 -0
  95. package/dist/types/src/remittance/modules/BasicBRC29.d.ts +133 -0
  96. package/dist/types/src/remittance/modules/BasicBRC29.d.ts.map +1 -0
  97. package/dist/types/src/remittance/modules/index.d.ts +2 -0
  98. package/dist/types/src/remittance/modules/index.d.ts.map +1 -0
  99. package/dist/types/src/remittance/types.d.ts +238 -0
  100. package/dist/types/src/remittance/types.d.ts.map +1 -0
  101. package/dist/types/src/script/OP.d.ts +5 -3
  102. package/dist/types/src/script/OP.d.ts.map +1 -1
  103. package/dist/types/src/script/Script.d.ts.map +1 -1
  104. package/dist/types/src/script/Spend.d.ts +7 -0
  105. package/dist/types/src/script/Spend.d.ts.map +1 -1
  106. package/dist/types/src/transaction/BeefTx.d.ts +2 -2
  107. package/dist/types/src/transaction/Transaction.d.ts +14 -0
  108. package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
  109. package/dist/types/src/wallet/Wallet.interfaces.d.ts +5 -5
  110. package/dist/types/src/wallet/Wallet.interfaces.d.ts.map +1 -1
  111. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  112. package/dist/umd/bundle.js +13 -13
  113. package/dist/umd/bundle.js.map +1 -1
  114. package/docs/index.md +2 -14
  115. package/docs/reference/auth.md +6 -12
  116. package/docs/reference/primitives.md +20 -78
  117. package/docs/reference/remittance.md +2166 -0
  118. package/docs/reference/script.md +11 -3
  119. package/docs/reference/transaction.md +27 -1
  120. package/docs/reference/wallet.md +6 -5
  121. package/docs/remittance-getting-started.md +138 -0
  122. package/mod.ts +1 -0
  123. package/package.json +12 -3
  124. package/src/auth/Peer.ts +18 -29
  125. package/src/auth/__tests/Peer.test.ts +253 -1
  126. package/src/identity/IdentityClient.ts +29 -153
  127. package/src/identity/__tests/IdentityClient.test.ts +1 -289
  128. package/src/overlay-tools/__tests/SHIPBroadcaster.test.ts +7 -9
  129. package/src/primitives/TransactionSignature.ts +129 -10
  130. package/src/primitives/__tests/utils.test.ts +30 -7
  131. package/src/primitives/utils.ts +13 -129
  132. package/src/remittance/CommsLayer.ts +41 -0
  133. package/src/remittance/IdentityLayer.ts +32 -0
  134. package/src/remittance/RemittanceManager.ts +1672 -0
  135. package/src/remittance/RemittanceModule.ts +92 -0
  136. package/src/remittance/__tests/BasicBRC29.test.ts +188 -0
  137. package/src/remittance/__tests/RemittanceManager.test.ts +493 -0
  138. package/src/remittance/__tests/examples.ts +130 -0
  139. package/src/remittance/index.ts +6 -0
  140. package/src/remittance/modules/BasicBRC29.ts +361 -0
  141. package/src/remittance/modules/index.ts +1 -0
  142. package/src/remittance/types.ts +284 -0
  143. package/src/script/OP.ts +15 -13
  144. package/src/script/Script.ts +3 -1
  145. package/src/script/Spend.ts +128 -52
  146. package/src/script/__tests/Chronicle.test.ts +186 -0
  147. package/src/script/__tests/Spend.test.ts +1 -1
  148. package/src/script/__tests/SpendValildVectors.test.ts +63 -0
  149. package/src/script/__tests/lrshiftnum.test.ts +185 -0
  150. package/src/script/__tests/sighashTestData.ts +1031 -0
  151. package/src/script/__tests/spend.valid.vectors.ts +9 -16
  152. package/src/transaction/BeefTx.ts +3 -3
  153. package/src/transaction/Transaction.ts +186 -0
  154. package/src/transaction/__tests/Beef.test.ts +2 -0
  155. package/src/transaction/__tests/Transaction.test.ts +641 -3
  156. package/src/wallet/Wallet.interfaces.ts +5 -5
@@ -0,0 +1,361 @@
1
+ import type {
2
+ Invoice,
3
+ RemittanceOptionId,
4
+ Termination
5
+ } from '../types.js'
6
+ import type { ModuleContext } from '../types.js'
7
+ import type { RemittanceModule } from '../RemittanceModule.js'
8
+ import type {
9
+ WalletInterface,
10
+ WalletCounterparty,
11
+ PubKeyHex,
12
+ OriginatorDomainNameStringUnder250Bytes,
13
+ WalletProtocol
14
+ } from '../../wallet/Wallet.interfaces.js'
15
+ import { createNonce } from '../../auth/utils/createNonce.js'
16
+ import P2PKH from '../../script/templates/P2PKH.js'
17
+ import PublicKey from '../../primitives/PublicKey.js'
18
+
19
+ /**
20
+ * BRC-29-like payment option terms.
21
+ *
22
+ * This module intentionally keeps option terms minimal:
23
+ * - Amount is taken from the invoice total (and validated as satoshis)
24
+ * - The payer derives the payee's per-payment public key using wallet.getPublicKey with a stable protocolID
25
+ */
26
+ export interface Brc29OptionTerms {
27
+ /** Payment amount in satoshis. */
28
+ amountSatoshis: number
29
+ /** The recipient of the payment */
30
+ payee: PubKeyHex
31
+ /** Which output index to internalize, default 0. */
32
+ outputIndex?: number
33
+ /** Optionally override the protocolID used in getPublicKey. */
34
+ protocolID?: WalletProtocol
35
+ /** Optional labels for createAction. */
36
+ labels?: string[]
37
+ /** Optional description for createAction. */
38
+ description?: string
39
+ }
40
+
41
+ /**
42
+ * Settlement artifact carried in the settlement message.
43
+ */
44
+ export interface Brc29SettlementArtifact {
45
+ customInstructions: {
46
+ derivationPrefix: string
47
+ derivationSuffix: string
48
+ }
49
+ transaction: number[]
50
+ amountSatoshis: number
51
+ outputIndex?: number
52
+ }
53
+
54
+ /**
55
+ * Receipt data for BRC-29 settlements.
56
+ */
57
+ export interface Brc29ReceiptData {
58
+ /** Result returned from wallet.internalizeAction, if accepted. */
59
+ internalizeResult?: unknown
60
+ /** Human-readable rejection reason, if rejected. */
61
+ rejectedReason?: string
62
+ /** If rejected with refund, contains the refund payment token. */
63
+ refund?: {
64
+ token: Brc29SettlementArtifact
65
+ feeSatoshis: number
66
+ }
67
+ }
68
+
69
+ export interface NonceProvider {
70
+ createNonce: (wallet: WalletInterface, scope: WalletCounterparty, originator?: unknown) => Promise<string>
71
+ }
72
+
73
+ export interface LockingScriptProvider {
74
+ /** Converts a public key string to a P2PKH locking script hex. */
75
+ pubKeyToP2PKHLockingScript: (publicKey: string) => Promise<string> | string
76
+ }
77
+
78
+ /**
79
+ * Default nonce provider using SDK createNonce.
80
+ */
81
+ export const DefaultNonceProvider: NonceProvider = {
82
+ async createNonce(wallet, scope, originator) {
83
+ const origin = originator as OriginatorDomainNameStringUnder250Bytes | undefined
84
+ return await createNonce(wallet, scope, origin)
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Default locking script provider using SDK P2PKH template.
90
+ */
91
+ export const DefaultLockingScriptProvider: LockingScriptProvider = {
92
+ async pubKeyToP2PKHLockingScript(publicKey: string) {
93
+ const address = PublicKey.fromString(publicKey).toAddress()
94
+ return new P2PKH().lock(address).toHex()
95
+ }
96
+ }
97
+
98
+ export interface Brc29RemittanceModuleConfig {
99
+ /** Default protocolID to use with wallet.getPublicKey. */
100
+ protocolID?: WalletProtocol
101
+ /** Labels applied to created actions. */
102
+ labels?: string[]
103
+ /** Description applied to created actions. */
104
+ description?: string
105
+ /** Output description for created actions. */
106
+ outputDescription?: string
107
+
108
+ /**
109
+ * Fee charged on refunds, in satoshis.
110
+ */
111
+ refundFeeSatoshis?: number
112
+
113
+ /**
114
+ * Minimum refund to issue. If refund would be smaller, module will reject without refund.
115
+ */
116
+ minRefundSatoshis?: number
117
+
118
+ /** How wallet internalizes the payment. */
119
+ internalizeProtocol?: 'wallet payment' | 'basket insertion'
120
+
121
+ nonceProvider?: NonceProvider
122
+ lockingScriptProvider?: LockingScriptProvider
123
+ }
124
+
125
+ /**
126
+ * BRC-29-based remittance module.
127
+ * - payer creates a payment action to a derived P2PKH output
128
+ * - payer sends { tx, derivationPrefix, derivationSuffix } as settlement artifact
129
+ * - payee internalizes the tx output using wallet.internalizeAction
130
+ * - optional rejection can include a refund token embedded in the termination details
131
+ */
132
+ export class Brc29RemittanceModule
133
+ implements RemittanceModule<Brc29OptionTerms, Brc29SettlementArtifact, Brc29ReceiptData> {
134
+ readonly id: RemittanceOptionId = 'brc29.p2pkh'
135
+ readonly name = 'BSV (BRC-29 derived P2PKH)'
136
+ readonly allowUnsolicitedSettlements = true
137
+
138
+ private readonly protocolID: WalletProtocol
139
+ private readonly labels: string[]
140
+ private readonly description: string
141
+ private readonly outputDescription: string
142
+ private readonly refundFeeSatoshis: number
143
+ private readonly minRefundSatoshis: number
144
+ private readonly internalizeProtocol: 'wallet payment' | 'basket insertion'
145
+ private readonly nonceProvider: NonceProvider
146
+ private readonly lockingScriptProvider: LockingScriptProvider
147
+
148
+ constructor(cfg: Brc29RemittanceModuleConfig = {}) {
149
+ // BRC-29 Protocol.
150
+ this.protocolID = cfg.protocolID ?? [2, '3241645161d8']
151
+ this.labels = cfg.labels ?? ['brc29']
152
+ this.description = cfg.description ?? 'BRC-29 payment'
153
+ this.outputDescription = cfg.outputDescription ?? 'Payment for remittance invoice'
154
+ this.refundFeeSatoshis = cfg.refundFeeSatoshis ?? 1000
155
+ this.minRefundSatoshis = cfg.minRefundSatoshis ?? 1000
156
+ this.internalizeProtocol = cfg.internalizeProtocol ?? 'wallet payment'
157
+ this.nonceProvider = cfg.nonceProvider ?? DefaultNonceProvider
158
+ this.lockingScriptProvider = cfg.lockingScriptProvider ?? DefaultLockingScriptProvider
159
+ }
160
+
161
+ async buildSettlement(
162
+ args: { threadId: string; option: Brc29OptionTerms; note?: string },
163
+ ctx: ModuleContext
164
+ ): Promise<{ action: 'settle'; artifact: Brc29SettlementArtifact } | { action: 'terminate'; termination: Termination }> {
165
+ const { wallet, originator } = ctx
166
+
167
+ let option: Brc29OptionTerms
168
+ try {
169
+ option = ensureValidOption(args.option)
170
+ } catch (error) {
171
+ const message = error instanceof Error ? error.message : String(error)
172
+ return terminate('brc29.invalid_option', message)
173
+ }
174
+
175
+ const amountSatoshis = option.amountSatoshis
176
+ const origin = originator as OriginatorDomainNameStringUnder250Bytes | undefined
177
+
178
+ try {
179
+ // Create per-payment derivation values.
180
+ const derivationPrefix = await this.nonceProvider.createNonce(wallet, 'self', origin)
181
+ const derivationSuffix = await this.nonceProvider.createNonce(wallet, 'self', origin)
182
+
183
+ // Derive payee public key.
184
+ const protocolID = option.protocolID ?? this.protocolID
185
+ const keyID = `${derivationPrefix} ${derivationSuffix}`
186
+
187
+ const { publicKey } = await wallet.getPublicKey(
188
+ {
189
+ protocolID,
190
+ keyID,
191
+ counterparty: option.payee
192
+ },
193
+ origin
194
+ )
195
+
196
+ if (typeof publicKey !== 'string' || publicKey.trim() === '') {
197
+ return terminate('brc29.public_key_missing', 'Failed to derive payee public key for BRC-29 settlement.')
198
+ }
199
+
200
+ const lockingScript = await this.lockingScriptProvider.pubKeyToP2PKHLockingScript(publicKey)
201
+ if (typeof lockingScript !== 'string' || lockingScript.trim() === '') {
202
+ return terminate('brc29.locking_script_missing', 'Failed to produce P2PKH locking script.')
203
+ }
204
+
205
+ const action = await wallet.createAction(
206
+ {
207
+ description: option.description ?? this.description,
208
+ labels: option.labels ?? this.labels,
209
+ outputs: [
210
+ {
211
+ satoshis: amountSatoshis,
212
+ lockingScript,
213
+ customInstructions: JSON.stringify({
214
+ derivationPrefix,
215
+ derivationSuffix,
216
+ payee: option.payee,
217
+ threadId: args.threadId,
218
+ note: args.note
219
+ }),
220
+ outputDescription: this.outputDescription
221
+ }
222
+ ],
223
+ options: {
224
+ randomizeOutputs: false
225
+ }
226
+ },
227
+ origin
228
+ )
229
+
230
+ const tx = action.tx ?? action.signableTransaction?.tx
231
+ if (tx == null) {
232
+ return terminate('brc29.missing_tx', 'wallet.createAction did not return a transaction.')
233
+ }
234
+ if (!isAtomicBeef(tx)) {
235
+ return terminate('brc29.invalid_tx', 'wallet.createAction returned an invalid transaction payload.')
236
+ }
237
+
238
+ return {
239
+ action: 'settle',
240
+ artifact: {
241
+ customInstructions: { derivationPrefix, derivationSuffix },
242
+ transaction: tx,
243
+ amountSatoshis: option.amountSatoshis,
244
+ outputIndex: option.outputIndex ?? 0
245
+ }
246
+ }
247
+ } catch (error) {
248
+ const message = error instanceof Error ? error.message : String(error)
249
+ return terminate('brc29.build_failed', `BRC-29 settlement failed: ${message}`)
250
+ }
251
+ }
252
+
253
+ async acceptSettlement(
254
+ args: { threadId: string; settlement: Brc29SettlementArtifact; sender: PubKeyHex },
255
+ ctx: ModuleContext
256
+ ): Promise<{ action: 'accept'; receiptData?: Brc29ReceiptData } | { action: 'terminate'; termination: Termination }> {
257
+ const { wallet, originator } = ctx
258
+ const origin = originator as OriginatorDomainNameStringUnder250Bytes | undefined
259
+ console.log('acceptSettlement', args)
260
+ try {
261
+ const settlement = ensureValidSettlement(args.settlement)
262
+ const outputIndex = settlement.outputIndex ?? 0
263
+ debugger
264
+ const internalizeResult = await wallet.internalizeAction(
265
+ {
266
+ tx: settlement.transaction,
267
+ outputs: [
268
+ {
269
+ paymentRemittance: {
270
+ derivationPrefix: settlement.customInstructions.derivationPrefix,
271
+ derivationSuffix: settlement.customInstructions.derivationSuffix,
272
+ senderIdentityKey: args.sender
273
+ },
274
+ outputIndex,
275
+ protocol: this.internalizeProtocol
276
+ }
277
+ ],
278
+ labels: this.labels,
279
+ description: 'BRC-29 payment received'
280
+ },
281
+ origin
282
+ )
283
+
284
+ return { action: 'accept', receiptData: { internalizeResult } }
285
+ } catch (error) {
286
+ const message = error instanceof Error ? error.message : String(error)
287
+ return terminate('brc29.internalize_failed', `Failed to internalize BRC-29 settlement: ${message}`)
288
+ }
289
+ }
290
+ }
291
+
292
+ function terminate(code: string, message: string, details?: unknown): { action: 'terminate'; termination: Termination } {
293
+ return { action: 'terminate', termination: { code, message, details } }
294
+ }
295
+
296
+ function ensureValidOption(option: Brc29OptionTerms): Brc29OptionTerms {
297
+ if (option == null || typeof option !== 'object') {
298
+ throw new Error('BRC-29 option terms are required')
299
+ }
300
+ const amountSatoshis = (option as Brc29OptionTerms).amountSatoshis
301
+ if (!Number.isInteger(amountSatoshis) || amountSatoshis <= 0) {
302
+ throw new Error('BRC-29 option amount must be a positive integer')
303
+ }
304
+ const outputIndex = (option as Brc29OptionTerms).outputIndex
305
+ if (outputIndex != null && (!Number.isInteger(outputIndex) || outputIndex < 0)) {
306
+ throw new Error('BRC-29 option outputIndex must be a non-negative integer')
307
+ }
308
+ const protocolID = (option as Brc29OptionTerms).protocolID
309
+ if (protocolID != null) {
310
+ if (!Array.isArray(protocolID) || protocolID.length !== 2) {
311
+ throw new Error('BRC-29 option protocolID must be a tuple [number, string]')
312
+ }
313
+ const [protocolNumber, protocolString] = protocolID
314
+ if (!Number.isInteger(protocolNumber) || protocolNumber < 0 || !isNonEmptyString(protocolString)) {
315
+ throw new Error('BRC-29 option protocolID must be a tuple [number, string]')
316
+ }
317
+ }
318
+ const labels = (option as Brc29OptionTerms).labels
319
+ if (labels != null && (!Array.isArray(labels) || labels.some((label) => !isNonEmptyString(label)))) {
320
+ throw new Error('BRC-29 option labels must be a list of non-empty strings')
321
+ }
322
+ const description = (option as Brc29OptionTerms).description
323
+ if (description != null && !isNonEmptyString(description)) {
324
+ throw new Error('BRC-29 option description must be a non-empty string')
325
+ }
326
+ return option
327
+ }
328
+
329
+ function ensureValidSettlement(settlement: Brc29SettlementArtifact): Brc29SettlementArtifact {
330
+ if (settlement == null || typeof settlement !== 'object') {
331
+ throw new Error('BRC-29 settlement artifact is required')
332
+ }
333
+ const instructions = settlement.customInstructions
334
+ if (instructions == null || typeof instructions !== 'object') {
335
+ throw new Error('BRC-29 settlement requires customInstructions')
336
+ }
337
+ if (!isNonEmptyString(instructions.derivationPrefix) || !isNonEmptyString(instructions.derivationSuffix)) {
338
+ throw new Error('BRC-29 settlement derivation values are required')
339
+ }
340
+ const amountSatoshis = settlement.amountSatoshis
341
+ if (!Number.isInteger(amountSatoshis) || amountSatoshis <= 0) {
342
+ throw new Error('BRC-29 settlement amount must be a positive integer')
343
+ }
344
+ const outputIndex = settlement.outputIndex
345
+ if (outputIndex != null && (!Number.isInteger(outputIndex) || outputIndex < 0)) {
346
+ throw new Error('BRC-29 settlement outputIndex must be a non-negative integer')
347
+ }
348
+ if (!isAtomicBeef(settlement.transaction)) {
349
+ throw new Error('BRC-29 settlement transaction must be a non-empty byte array')
350
+ }
351
+ return settlement
352
+ }
353
+
354
+ function isAtomicBeef(tx: unknown): tx is number[] {
355
+ if (!Array.isArray(tx) || tx.length === 0) return false
356
+ return tx.every((byte) => Number.isInteger(byte) && byte >= 0 && byte <= 255)
357
+ }
358
+
359
+ function isNonEmptyString(value: unknown): value is string {
360
+ return typeof value === 'string' && value.trim().length > 0
361
+ }
@@ -0,0 +1 @@
1
+ export * from './BasicBRC29.js'
@@ -0,0 +1,284 @@
1
+ import { HexString, OutpointString, PubKeyHex, Base64String, WalletInterface } from '../wallet/index.js'
2
+
3
+ /**
4
+ * Types for core Remittance protocol.
5
+ *
6
+ * The goal is to keep the core protocol:
7
+ * - UTXO-friendly (transactions and partial transactions can be carried as artifacts)
8
+ * - Denomination-agnostic (amounts are typed, not forced to satoshis)
9
+ * - Module-oriented (remittance option payloads are opaque to the core)
10
+ */
11
+
12
+ export type ThreadId = Base64String
13
+ export type RemittanceOptionId = Base64String
14
+ export type UnixMillis = number
15
+
16
+ /**
17
+ * Remittance thread state machine.
18
+ *
19
+ * States:
20
+ * - new: thread exists but no identity/invoice/settlement activity yet.
21
+ * - identityRequested: identity request sent or received.
22
+ * - identityResponded: identity response sent or received.
23
+ * - identityAcknowledged: identity response acknowledged (required before proceeding).
24
+ * - invoiced: invoice sent or received.
25
+ * - settled: settlement sent or received.
26
+ * - receipted: receipt issued or received.
27
+ * - terminated: thread terminated with a reason.
28
+ * - errored: unexpected error occurred while processing the thread.
29
+ */
30
+ export type RemittanceThreadState =
31
+ | 'new'
32
+ | 'identityRequested'
33
+ | 'identityResponded'
34
+ | 'identityAcknowledged'
35
+ | 'invoiced'
36
+ | 'settled'
37
+ | 'receipted'
38
+ | 'terminated'
39
+ | 'errored'
40
+
41
+ /**
42
+ * Allowed remittance state transitions.
43
+ *
44
+ * This is the canonical state machine for remittance threads.
45
+ * Use it to validate transitions and to build audits/visualizations.
46
+ */
47
+ export const REMITTANCE_STATE_TRANSITIONS: Record<RemittanceThreadState, RemittanceThreadState[]> = {
48
+ new: ['identityRequested', 'invoiced', 'settled', 'terminated', 'errored'],
49
+ identityRequested: ['identityResponded', 'identityAcknowledged', 'invoiced', 'settled', 'terminated', 'errored'],
50
+ identityResponded: ['identityAcknowledged', 'invoiced', 'settled', 'terminated', 'errored'],
51
+ identityAcknowledged: ['invoiced', 'settled', 'terminated', 'errored'],
52
+ invoiced: ['identityRequested', 'identityResponded', 'identityAcknowledged', 'settled', 'terminated', 'errored'],
53
+ settled: ['receipted', 'terminated', 'errored'],
54
+ receipted: ['terminated', 'errored'],
55
+ terminated: ['errored'],
56
+ errored: []
57
+ }
58
+
59
+ export interface Unit {
60
+ /** Namespace for disambiguation, e.g. 'bsv', 'iso4217', 'token'. */
61
+ namespace: string
62
+ /** Unit code within the namespace, e.g. 'sat', 'USD', 'mnee'. */
63
+ code: string
64
+ /** Optional decimal places for display/normalization. */
65
+ decimals?: number
66
+ }
67
+
68
+ export interface Amount {
69
+ /** Decimal string. Avoid floats at the protocol layer. */
70
+ value: string
71
+ unit: Unit
72
+ }
73
+
74
+ export const SAT_UNIT: Unit = { namespace: 'bsv', code: 'sat', decimals: 0 }
75
+
76
+ export interface LineItem {
77
+ id?: string
78
+ description: string
79
+ /** Decimal string, e.g. '1', '2', '0.5'. */
80
+ quantity?: string
81
+ unitPrice?: Amount
82
+ /** Total amount for the line (optional if derivable). */
83
+ amount?: Amount
84
+ metadata?: Record<string, unknown>
85
+ }
86
+
87
+ /**
88
+ * Shared commercial/metadata fields for invoice and receipt-like instruments.
89
+ *
90
+ * NOTE: "payee" and "payer" are identity keys, not addresses.
91
+ * Payment addresses / scripts are settlement-module concerns.
92
+ */
93
+ export interface InstrumentBase {
94
+ threadId: ThreadId
95
+ payee: PubKeyHex
96
+ payer: PubKeyHex
97
+ note?: string
98
+ lineItems: LineItem[]
99
+ total: Amount
100
+ invoiceNumber: string
101
+ createdAt: UnixMillis
102
+ arbitrary?: Record<string, unknown>
103
+ }
104
+
105
+ /**
106
+ * Invoice (solicitation) that contains N remittance options.
107
+ *
108
+ * Each remittance option is keyed by a module id.
109
+ * The payload for each option is module-defined and opaque to the core.
110
+ *
111
+ * This is where “UTXO offers” live: a module option payload can include a partial tx template,
112
+ * UTXO references, scripts, overlay anchors, SPV, etc. The manager does not interpret them.
113
+ */
114
+ export interface Invoice extends InstrumentBase {
115
+ kind: 'invoice'
116
+ expiresAt?: UnixMillis
117
+ options: Record<RemittanceOptionId, unknown>
118
+ }
119
+
120
+ /**
121
+ * An identity certificate request.
122
+ *
123
+ * Contains a list of requested certificate types, fields from each, plus acceptable certifiers.
124
+ */
125
+ export interface IdentityVerificationRequest {
126
+ kind: 'identityVerificationRequest'
127
+ threadId: ThreadId
128
+ /** Details of the requested certificates. */
129
+ request: {
130
+ /** Map of certificate types to requested fields from each. */
131
+ types: Record<string, string[]>
132
+ /** List of acceptable certifier identity keys. */
133
+ certifiers: PubKeyHex[]
134
+ }
135
+ }
136
+
137
+ /**
138
+ * An identity certificate response.
139
+ *
140
+ * Contains certificates issued by the certifiers named in the corresponding request, with fields revealed to the counterparty.
141
+ */
142
+ export interface IdentityVerificationResponse {
143
+ kind: 'identityVerificationResponse'
144
+ threadId: ThreadId
145
+ /** List of certificates issued by the certifiers named in the corresponding request, with fields revealed to the counterparty. */
146
+ certificates: Array<{
147
+ /** Certificate type, e.g. base64 Type ID corresponding to 'personalId', 'businessId', etc. */
148
+ type: Base64String
149
+ /** Certifier identity key. */
150
+ certifier: PubKeyHex
151
+ /** Subject identity key. */
152
+ subject: PubKeyHex
153
+ /** The certificate's encrypted fields that have been signed. */
154
+ fields: Record<string, Base64String>
155
+ /** Signature over the cert. */
156
+ signature: HexString
157
+ /** Certificate serial number. */
158
+ serialNumber: Base64String
159
+ /** Revocation outpoint. If spent on-chain, the certificate is invalid. */
160
+ revocationOutpoint: OutpointString
161
+ /** Field revelation keys for the counterparty. */
162
+ keyringForVerifier: Record<string, Base64String>
163
+ }>
164
+ }
165
+
166
+ /**
167
+ * An identity verification acknowledgment.
168
+ *
169
+ * A simple ack message indicating that a requested identity verification has been completed successfully.
170
+ */
171
+ export interface IdentityVerificationAcknowledgment {
172
+ kind: 'identityVerificationAcknowledgment'
173
+ threadId: ThreadId
174
+ }
175
+
176
+ /**
177
+ * A settlement attempt.
178
+ *
179
+ * This is module-agnostic: "artifact" can be a transaction, a partial transaction,
180
+ * a stablecoin transfer result, even a fiat card-payment approval code, etc.
181
+ */
182
+ export interface Settlement {
183
+ kind: 'settlement'
184
+ threadId: ThreadId
185
+ moduleId: RemittanceOptionId
186
+ optionId: RemittanceOptionId
187
+ sender: PubKeyHex
188
+ createdAt: UnixMillis
189
+ artifact: unknown
190
+ note?: string
191
+ }
192
+
193
+ /**
194
+ * Receipt issued by the payee (or service provider).
195
+ *
196
+ * A receipt could be a PDF, a photo/oroof-of-delivery, a copy of the payment transaction, etc.
197
+ *
198
+ * A receipt should NOT be issued when a settlement is rejected/failed. Use a Termination instead.
199
+ */
200
+ export interface Receipt {
201
+ kind: 'receipt'
202
+ threadId: ThreadId
203
+ moduleId: RemittanceOptionId
204
+ optionId: RemittanceOptionId
205
+ payee: PubKeyHex
206
+ payer: PubKeyHex
207
+ createdAt: UnixMillis
208
+ receiptData: unknown
209
+ }
210
+
211
+ /**
212
+ * Termination details for failed operations.
213
+ */
214
+ export interface Termination {
215
+ /** Reason code (module-specific). */
216
+ code: string
217
+ /** Human-readable message. */
218
+ message: string
219
+ /** Optional module-specific details or refund information. */
220
+ details?: unknown
221
+ }
222
+
223
+ /**
224
+ * Transport message format expected from the CommsLayer.
225
+ *
226
+ * It closely matches the message-box-client shapes:
227
+ * messageId, sender, body, etc.
228
+ */
229
+ export interface PeerMessage {
230
+ messageId: string
231
+ sender: PubKeyHex
232
+ recipient: PubKeyHex
233
+ messageBox: string
234
+ body: string
235
+ }
236
+
237
+ /**
238
+ * Protocol envelope kinds.
239
+ * Everything runs in “threads” and carries a threadId.
240
+ */
241
+ export type RemittanceKind =
242
+ | 'invoice'
243
+ | 'identityVerificationRequest'
244
+ | 'identityVerificationResponse'
245
+ | 'identityVerificationAcknowledgment'
246
+ | 'settlement'
247
+ | 'receipt'
248
+ | 'termination'
249
+
250
+ /**
251
+ * Protocol envelope.
252
+ *
253
+ * This is what RemittanceManager serializes into the CommsLayer message body.
254
+ */
255
+ export interface RemittanceEnvelope<K extends RemittanceKind = RemittanceKind, P = unknown> {
256
+ /** Protocol version. */
257
+ v: 1
258
+ /** Envelope id (idempotency key). Not the transport messageId. */
259
+ id: string
260
+ kind: K
261
+ threadId: ThreadId
262
+ createdAt: UnixMillis
263
+ payload: P
264
+ }
265
+
266
+ /**
267
+ * Simple logger interface.
268
+ */
269
+ export interface LoggerLike {
270
+ log: (...args: any[]) => void
271
+ warn: (...args: any[]) => void
272
+ error: (...args: any[]) => void
273
+ }
274
+
275
+ /**
276
+ * Context object passed to module methods.
277
+ */
278
+ export interface ModuleContext {
279
+ wallet: WalletInterface
280
+ /** Optional originator domain forwarded to wallet methods. */
281
+ originator?: unknown
282
+ now: () => number
283
+ logger?: LoggerLike
284
+ }
package/src/script/OP.ts CHANGED
@@ -63,12 +63,9 @@ const OP = {
63
63
 
64
64
  // data manipulation ops
65
65
  OP_CAT: 0x7e,
66
- OP_SUBSTR: 0x7f, // Replaced in BSV
67
- OP_SPLIT: 0x7f,
68
- OP_LEFT: 0x80, // Replaced in BSV
69
- OP_NUM2BIN: 0x80,
70
- OP_RIGHT: 0x81, // Replaced in BSV
71
- OP_BIN2NUM: 0x81,
66
+ OP_SPLIT: 0x7f, // after monolith upgrade (May 2018)
67
+ OP_NUM2BIN: 0x80, // after monolith upgrade (May 2018)
68
+ OP_BIN2NUM: 0x81, // after monolith upgrade (May 2018)
72
69
  OP_SIZE: 0x82,
73
70
 
74
71
  // bit logic
@@ -127,13 +124,18 @@ const OP = {
127
124
 
128
125
  // expansion
129
126
  OP_NOP1: 0xb0,
130
- OP_NOP2: 0xb1,
131
- OP_NOP3: 0xb2,
132
- OP_NOP4: 0xb3,
133
- OP_NOP5: 0xb4,
134
- OP_NOP6: 0xb5,
135
- OP_NOP7: 0xb6,
136
- OP_NOP8: 0xb7,
127
+ OP_NOP2: 0xb1, // Used on BTC for OP_CHECKLOCKTIMEVERIFY
128
+ OP_NOP3: 0xb2, // Used on BTC for OP_CHECKSEQUENCEVERIFY
129
+ OP_NOP4: 0xb3, // OP_NOP4 allocated to restore OP_SUBSTR in 2026 CHRONICLE upgrade
130
+ OP_SUBSTR: 0xb3, // OP_NOP4 allocated to restore OP_SUBSTR in 2026 CHRONICLE upgrade
131
+ OP_NOP5: 0xb4, // OP_NOP5 allocated to restore OP_LEFT in 2026 CHRONICLE upgrade
132
+ OP_LEFT: 0xb4, // OP_NOP5 allocated to restore OP_LEFT in 2026 CHRONICLE upgrade
133
+ OP_NOP6: 0xb5, // OP_NOP6 allocated to restore OP_RIGHT in 2026 CHRONICLE upgrade
134
+ OP_RIGHT: 0xb5, // OP_NOP6 allocated to restore OP_RIGHT in 2026 CHRONICLE upgrade
135
+ OP_NOP7: 0xb6, // OP_NOP7 allocated to restore OP_LSHIFTNUM in 2026 CHRONICLE upgrade
136
+ OP_LSHIFTNUM: 0xb6, // OP_NOP7 allocated to restore OP_LSHIFTNUM in 2026 CHRONICLE upgrade
137
+ OP_NOP8: 0xb7, // OP_NOP7 allocated to restore OP_RSHIFTNUM in 2026 CHRONICLE upgrade
138
+ OP_RSHIFTNUM: 0xb7, // OP_NOP7 allocated to restore OP_RSHIFTNUM in 2026 CHRONICLE upgrade
137
139
  OP_NOP9: 0xb8,
138
140
  OP_NOP10: 0xb9,
139
141
  OP_NOP11: 0xba,