@dcasegr/amm-client 0.0.0-stage → 0.38.2

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 (121) hide show
  1. package/README.md +107 -2
  2. package/dist/client.d.ts +534 -0
  3. package/dist/client.js +1319 -0
  4. package/dist/errors.d.ts +17 -0
  5. package/dist/errors.js +75 -0
  6. package/dist/index.d.ts +51 -0
  7. package/dist/index.js +53 -0
  8. package/dist/lib/config.d.ts +42 -0
  9. package/dist/lib/config.js +64 -0
  10. package/dist/lib/keys.d.ts +42 -0
  11. package/dist/lib/keys.js +48 -0
  12. package/dist/lib/overlay.d.ts +190 -0
  13. package/dist/lib/overlay.js +302 -0
  14. package/dist/lib/skein.d.ts +82 -0
  15. package/dist/lib/skein.js +142 -0
  16. package/dist/lib/tokenId.d.ts +72 -0
  17. package/dist/lib/tokenId.js +121 -0
  18. package/dist/lp/amounts.d.ts +12 -0
  19. package/dist/lp/amounts.js +38 -0
  20. package/dist/lp/close.d.ts +90 -0
  21. package/dist/lp/close.js +140 -0
  22. package/dist/lp/images.d.ts +11 -0
  23. package/dist/lp/images.js +45 -0
  24. package/dist/lp/inventory.d.ts +68 -0
  25. package/dist/lp/inventory.js +84 -0
  26. package/dist/lp/poolDeploy.d.ts +220 -0
  27. package/dist/lp/poolDeploy.js +291 -0
  28. package/dist/lp/poolRows.d.ts +13 -0
  29. package/dist/lp/poolRows.js +19 -0
  30. package/dist/lp/positions.d.ts +81 -0
  31. package/dist/lp/positions.js +144 -0
  32. package/dist/lp/rebalance.d.ts +101 -0
  33. package/dist/lp/rebalance.js +168 -0
  34. package/dist/lp/wallet.d.ts +12 -0
  35. package/dist/lp/wallet.js +19 -0
  36. package/dist/market/activeSwap.d.ts +158 -0
  37. package/dist/market/activeSwap.js +320 -0
  38. package/dist/market/plan.d.ts +85 -0
  39. package/dist/market/plan.js +149 -0
  40. package/dist/market/prices.d.ts +13 -0
  41. package/dist/market/prices.js +14 -0
  42. package/dist/market/relay.d.ts +84 -0
  43. package/dist/market/relay.js +201 -0
  44. package/dist/market/swapAction.d.ts +306 -0
  45. package/dist/market/swapAction.js +508 -0
  46. package/dist/market/swapFlow.d.ts +58 -0
  47. package/dist/market/swapFlow.js +86 -0
  48. package/dist/market/view.d.ts +58 -0
  49. package/dist/market/view.js +63 -0
  50. package/dist/pool/artifact.d.ts +23 -0
  51. package/dist/pool/artifact.js +38 -0
  52. package/dist/pool/index.d.ts +8 -0
  53. package/dist/pool/index.js +8 -0
  54. package/dist/pool/oneColor.d.ts +19 -0
  55. package/dist/pool/oneColor.js +39 -0
  56. package/dist/pool/pool.artifact.json +5069 -0
  57. package/dist/pool/runar-ir-schema.d.ts +21 -0
  58. package/dist/pool/runar-ir-schema.js +18 -0
  59. package/dist/pool/template.d.ts +295 -0
  60. package/dist/pool/template.js +715 -0
  61. package/dist/vendor/runar/LICENSE +21 -0
  62. package/dist/vendor/runar/NOTICE +15 -0
  63. package/dist/vendor/runar/runar-ir-schema/src/abi-type-encoding.d.ts +42 -0
  64. package/dist/vendor/runar/runar-ir-schema/src/abi-type-encoding.js +53 -0
  65. package/dist/vendor/runar/runar-ir-schema/src/anf-ir.d.ts +221 -0
  66. package/dist/vendor/runar/runar-ir-schema/src/anf-ir.js +33 -0
  67. package/dist/vendor/runar/runar-ir-schema/src/artifact.d.ts +288 -0
  68. package/dist/vendor/runar/runar-ir-schema/src/artifact.js +8 -0
  69. package/dist/vendor/runar/runar-ir-schema/src/canonical-json.d.ts +43 -0
  70. package/dist/vendor/runar/runar-ir-schema/src/canonical-json.js +204 -0
  71. package/dist/vendor/runar/runar-ir-schema/src/input-limits.d.ts +89 -0
  72. package/dist/vendor/runar/runar-ir-schema/src/input-limits.js +100 -0
  73. package/dist/vendor/runar/runar-ir-schema/src/stack-ir.d.ts +118 -0
  74. package/dist/vendor/runar/runar-ir-schema/src/stack-ir.js +8 -0
  75. package/dist/vendor/runar/runar-ir-schema/src/state-layout.d.ts +56 -0
  76. package/dist/vendor/runar/runar-ir-schema/src/state-layout.js +145 -0
  77. package/dist/vendor/runar/runar-ir-schema/src/unknown-anf-kind-error.d.ts +18 -0
  78. package/dist/vendor/runar/runar-ir-schema/src/unknown-anf-kind-error.js +25 -0
  79. package/dist/vendor/runar/runar-sdk/src/anf-interpreter.d.ts +160 -0
  80. package/dist/vendor/runar/runar-sdk/src/anf-interpreter.js +1149 -0
  81. package/dist/vendor/runar/runar-sdk/src/calling.d.ts +98 -0
  82. package/dist/vendor/runar/runar-sdk/src/calling.js +209 -0
  83. package/dist/vendor/runar/runar-sdk/src/contract.d.ts +443 -0
  84. package/dist/vendor/runar/runar-sdk/src/contract.js +2516 -0
  85. package/dist/vendor/runar/runar-sdk/src/deployment.d.ts +36 -0
  86. package/dist/vendor/runar/runar-sdk/src/deployment.js +119 -0
  87. package/dist/vendor/runar/runar-sdk/src/errors.d.ts +46 -0
  88. package/dist/vendor/runar/runar-sdk/src/errors.js +59 -0
  89. package/dist/vendor/runar/runar-sdk/src/oppushtx.d.ts +23 -0
  90. package/dist/vendor/runar/runar-sdk/src/oppushtx.js +95 -0
  91. package/dist/vendor/runar/runar-sdk/src/ordinals/envelope.d.ts +31 -0
  92. package/dist/vendor/runar/runar-sdk/src/ordinals/envelope.js +272 -0
  93. package/dist/vendor/runar/runar-sdk/src/ordinals/types.d.ts +21 -0
  94. package/dist/vendor/runar/runar-sdk/src/ordinals/types.js +4 -0
  95. package/dist/vendor/runar/runar-sdk/src/providers/provider.d.ts +65 -0
  96. package/dist/vendor/runar/runar-sdk/src/providers/provider.js +59 -0
  97. package/dist/vendor/runar/runar-sdk/src/providers/wallet-provider.d.ts +94 -0
  98. package/dist/vendor/runar/runar-sdk/src/providers/wallet-provider.js +297 -0
  99. package/dist/vendor/runar/runar-sdk/src/script-utils.d.ts +54 -0
  100. package/dist/vendor/runar/runar-sdk/src/script-utils.js +252 -0
  101. package/dist/vendor/runar/runar-sdk/src/signers/signer.d.ts +17 -0
  102. package/dist/vendor/runar/runar-sdk/src/signers/signer.js +4 -0
  103. package/dist/vendor/runar/runar-sdk/src/spend-safety.d.ts +31 -0
  104. package/dist/vendor/runar/runar-sdk/src/spend-safety.js +35 -0
  105. package/dist/vendor/runar/runar-sdk/src/state.d.ts +54 -0
  106. package/dist/vendor/runar/runar-sdk/src/state.js +584 -0
  107. package/dist/vendor/runar/runar-sdk/src/types.d.ts +227 -0
  108. package/dist/vendor/runar/runar-sdk/src/types.js +4 -0
  109. package/dist/vendor/runar/runar-sdk/src/unsound-primitives.d.ts +30 -0
  110. package/dist/vendor/runar/runar-sdk/src/unsound-primitives.js +42 -0
  111. package/dist/vendor/runar/runar-sdk.d.ts +9 -0
  112. package/dist/vendor/runar/runar-sdk.js +8 -0
  113. package/dist/wallet/action.d.ts +28 -0
  114. package/dist/wallet/action.js +42 -0
  115. package/dist/wallet/brc29.d.ts +83 -0
  116. package/dist/wallet/brc29.js +80 -0
  117. package/dist/wallet/color.d.ts +110 -0
  118. package/dist/wallet/color.js +134 -0
  119. package/dist/wallet/swapPayouts.d.ts +35 -0
  120. package/dist/wallet/swapPayouts.js +146 -0
  121. package/package.json +39 -4
@@ -0,0 +1,272 @@
1
+ // ---------------------------------------------------------------------------
2
+ // runar-sdk/ordinals/envelope.ts — Build and parse 1sat ordinal inscriptions
3
+ // ---------------------------------------------------------------------------
4
+ //
5
+ // Envelope layout:
6
+ // OP_FALSE OP_IF PUSH("ord") OP_1 PUSH(<content-type>) OP_0 PUSH(<data>) OP_ENDIF
7
+ //
8
+ // Hex:
9
+ // 00 63 03 6f7264 51 <push content-type> 00 <push data> 68
10
+ //
11
+ // The envelope is a no-op (OP_FALSE causes the IF block to be skipped)
12
+ // and can be placed anywhere in a script without affecting execution.
13
+ // ---------------------------------------------------------------------------
14
+ // ---------------------------------------------------------------------------
15
+ // Push-data encoding (same logic as contract.ts / state.ts, kept local to
16
+ // avoid exporting private helpers from those modules)
17
+ // ---------------------------------------------------------------------------
18
+ function encodePushData(dataHex) {
19
+ if (dataHex.length === 0)
20
+ return '00'; // OP_0
21
+ const len = dataHex.length / 2;
22
+ if (len <= 75) {
23
+ return len.toString(16).padStart(2, '0') + dataHex;
24
+ }
25
+ else if (len <= 0xff) {
26
+ return '4c' + len.toString(16).padStart(2, '0') + dataHex;
27
+ }
28
+ else if (len <= 0xffff) {
29
+ const lo = (len & 0xff).toString(16).padStart(2, '0');
30
+ const hi = ((len >> 8) & 0xff).toString(16).padStart(2, '0');
31
+ return '4d' + lo + hi + dataHex;
32
+ }
33
+ const b0 = (len & 0xff).toString(16).padStart(2, '0');
34
+ const b1 = ((len >> 8) & 0xff).toString(16).padStart(2, '0');
35
+ const b2 = ((len >> 16) & 0xff).toString(16).padStart(2, '0');
36
+ const b3 = ((len >> 24) & 0xff).toString(16).padStart(2, '0');
37
+ return '4e' + b0 + b1 + b2 + b3 + dataHex;
38
+ }
39
+ /** Convert a UTF-8 string to its hex representation. */
40
+ function utf8ToHex(str) {
41
+ const encoder = new TextEncoder();
42
+ const bytes = encoder.encode(str);
43
+ return Array.from(bytes)
44
+ .map((b) => b.toString(16).padStart(2, '0'))
45
+ .join('');
46
+ }
47
+ /** Convert a hex string to UTF-8. */
48
+ function hexToUtf8(hex) {
49
+ const bytes = new Uint8Array(hex.length / 2);
50
+ for (let i = 0; i < hex.length; i += 2) {
51
+ bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
52
+ }
53
+ const decoder = new TextDecoder();
54
+ return decoder.decode(bytes);
55
+ }
56
+ // ---------------------------------------------------------------------------
57
+ // Build
58
+ // ---------------------------------------------------------------------------
59
+ /**
60
+ * Build a 1sat ordinals inscription envelope as hex.
61
+ *
62
+ * @param contentType - MIME type (e.g. "image/png", "application/bsv-20")
63
+ * @param data - Hex-encoded inscription content
64
+ * @returns Hex string of the full envelope script fragment
65
+ */
66
+ export function buildInscriptionEnvelope(contentType, data) {
67
+ const contentTypeHex = utf8ToHex(contentType);
68
+ // OP_FALSE (00) OP_IF (63) PUSH "ord" (03 6f7264) OP_1 (51)
69
+ let hex = '006303' + '6f7264' + '51';
70
+ // PUSH content-type
71
+ hex += encodePushData(contentTypeHex);
72
+ // OP_0 (00) — content delimiter
73
+ hex += '00';
74
+ // PUSH data
75
+ hex += encodePushData(data);
76
+ // OP_ENDIF (68)
77
+ hex += '68';
78
+ return hex;
79
+ }
80
+ // ---------------------------------------------------------------------------
81
+ // Parse / Find
82
+ // ---------------------------------------------------------------------------
83
+ /**
84
+ * Read a push-data value at the given hex offset. Returns the pushed data
85
+ * (hex) and the total number of hex chars consumed (including the length
86
+ * prefix).
87
+ */
88
+ function readPushData(scriptHex, offset) {
89
+ if (offset + 2 > scriptHex.length)
90
+ return null;
91
+ const opcode = parseInt(scriptHex.slice(offset, offset + 2), 16);
92
+ if (opcode >= 0x01 && opcode <= 0x4b) {
93
+ const dataLen = opcode * 2;
94
+ if (offset + 2 + dataLen > scriptHex.length)
95
+ return null;
96
+ return { data: scriptHex.slice(offset + 2, offset + 2 + dataLen), bytesRead: 2 + dataLen };
97
+ }
98
+ else if (opcode === 0x4c) {
99
+ // OP_PUSHDATA1
100
+ if (offset + 4 > scriptHex.length)
101
+ return null;
102
+ const len = parseInt(scriptHex.slice(offset + 2, offset + 4), 16);
103
+ const dataLen = len * 2;
104
+ if (offset + 4 + dataLen > scriptHex.length)
105
+ return null;
106
+ return { data: scriptHex.slice(offset + 4, offset + 4 + dataLen), bytesRead: 4 + dataLen };
107
+ }
108
+ else if (opcode === 0x4d) {
109
+ // OP_PUSHDATA2
110
+ if (offset + 6 > scriptHex.length)
111
+ return null;
112
+ const lo = parseInt(scriptHex.slice(offset + 2, offset + 4), 16);
113
+ const hi = parseInt(scriptHex.slice(offset + 4, offset + 6), 16);
114
+ const len = lo | (hi << 8);
115
+ const dataLen = len * 2;
116
+ if (offset + 6 + dataLen > scriptHex.length)
117
+ return null;
118
+ return { data: scriptHex.slice(offset + 6, offset + 6 + dataLen), bytesRead: 6 + dataLen };
119
+ }
120
+ else if (opcode === 0x4e) {
121
+ // OP_PUSHDATA4
122
+ if (offset + 10 > scriptHex.length)
123
+ return null;
124
+ const b0 = parseInt(scriptHex.slice(offset + 2, offset + 4), 16);
125
+ const b1 = parseInt(scriptHex.slice(offset + 4, offset + 6), 16);
126
+ const b2 = parseInt(scriptHex.slice(offset + 6, offset + 8), 16);
127
+ const b3 = parseInt(scriptHex.slice(offset + 8, offset + 10), 16);
128
+ const len = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
129
+ const dataLen = len * 2;
130
+ if (offset + 10 + dataLen > scriptHex.length)
131
+ return null;
132
+ return { data: scriptHex.slice(offset + 10, offset + 10 + dataLen), bytesRead: 10 + dataLen };
133
+ }
134
+ return null;
135
+ }
136
+ /**
137
+ * Compute the number of hex chars an opcode occupies (including its push
138
+ * data) so we can advance past it while walking a script.
139
+ */
140
+ function opcodeSize(scriptHex, offset) {
141
+ if (offset + 2 > scriptHex.length)
142
+ return 2;
143
+ const opcode = parseInt(scriptHex.slice(offset, offset + 2), 16);
144
+ if (opcode >= 0x01 && opcode <= 0x4b) {
145
+ return 2 + opcode * 2;
146
+ }
147
+ else if (opcode === 0x4c) {
148
+ if (offset + 4 > scriptHex.length)
149
+ return 2;
150
+ const len = parseInt(scriptHex.slice(offset + 2, offset + 4), 16);
151
+ return 4 + len * 2;
152
+ }
153
+ else if (opcode === 0x4d) {
154
+ if (offset + 6 > scriptHex.length)
155
+ return 2;
156
+ const lo = parseInt(scriptHex.slice(offset + 2, offset + 4), 16);
157
+ const hi = parseInt(scriptHex.slice(offset + 4, offset + 6), 16);
158
+ return 6 + (lo | (hi << 8)) * 2;
159
+ }
160
+ else if (opcode === 0x4e) {
161
+ if (offset + 10 > scriptHex.length)
162
+ return 2;
163
+ const b0 = parseInt(scriptHex.slice(offset + 2, offset + 4), 16);
164
+ const b1 = parseInt(scriptHex.slice(offset + 4, offset + 6), 16);
165
+ const b2 = parseInt(scriptHex.slice(offset + 6, offset + 8), 16);
166
+ const b3 = parseInt(scriptHex.slice(offset + 8, offset + 10), 16);
167
+ return 10 + (b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)) * 2;
168
+ }
169
+ return 2; // all other opcodes are 1 byte
170
+ }
171
+ /**
172
+ * Find the inscription envelope within a script hex string.
173
+ *
174
+ * Walks the script as Bitcoin Script opcodes (identical in shape to
175
+ * `findLastOpReturn` in state.ts) looking for the pattern:
176
+ * OP_FALSE(00) OP_IF(63) PUSH3 "ord"(03 6f7264) ...
177
+ *
178
+ * @returns Hex-char offsets of the envelope, or null if not found.
179
+ */
180
+ export function findInscriptionEnvelope(scriptHex) {
181
+ let offset = 0;
182
+ const len = scriptHex.length;
183
+ while (offset + 2 <= len) {
184
+ const opcode = parseInt(scriptHex.slice(offset, offset + 2), 16);
185
+ // Look for OP_FALSE (0x00)
186
+ if (opcode === 0x00) {
187
+ // Check: OP_IF (63) PUSH3 (03) "ord" (6f7264)
188
+ if (offset + 12 <= len &&
189
+ scriptHex.slice(offset + 2, offset + 4) === '63' && // OP_IF
190
+ scriptHex.slice(offset + 4, offset + 12) === '036f7264' // PUSH3 "ord"
191
+ ) {
192
+ const envelopeStart = offset;
193
+ // Skip: OP_FALSE(2) + OP_IF(2) + PUSH3 "ord"(8) = 12 hex chars
194
+ let pos = offset + 12;
195
+ // Expect OP_1 (0x51)
196
+ if (pos + 2 > len || scriptHex.slice(pos, pos + 2) !== '51') {
197
+ offset += 2;
198
+ continue;
199
+ }
200
+ pos += 2; // skip OP_1
201
+ // Read content-type push
202
+ const ctPush = readPushData(scriptHex, pos);
203
+ if (!ctPush) {
204
+ offset += 2;
205
+ continue;
206
+ }
207
+ pos += ctPush.bytesRead;
208
+ // Expect OP_0 (0x00) — content delimiter
209
+ if (pos + 2 > len || scriptHex.slice(pos, pos + 2) !== '00') {
210
+ offset += 2;
211
+ continue;
212
+ }
213
+ pos += 2; // skip OP_0
214
+ // Read data push
215
+ const dataPush = readPushData(scriptHex, pos);
216
+ if (!dataPush) {
217
+ offset += 2;
218
+ continue;
219
+ }
220
+ pos += dataPush.bytesRead;
221
+ // Expect OP_ENDIF (0x68)
222
+ if (pos + 2 > len || scriptHex.slice(pos, pos + 2) !== '68') {
223
+ offset += 2;
224
+ continue;
225
+ }
226
+ pos += 2; // skip OP_ENDIF
227
+ return { startHex: envelopeStart, endHex: pos };
228
+ }
229
+ }
230
+ // Advance past this opcode
231
+ offset += opcodeSize(scriptHex, offset);
232
+ }
233
+ return null;
234
+ }
235
+ /**
236
+ * Parse an inscription envelope from a script hex string.
237
+ *
238
+ * @returns The inscription data, or null if no envelope is found.
239
+ */
240
+ export function parseInscriptionEnvelope(scriptHex) {
241
+ const bounds = findInscriptionEnvelope(scriptHex);
242
+ if (!bounds)
243
+ return null;
244
+ const envelopeHex = scriptHex.slice(bounds.startHex, bounds.endHex);
245
+ // Parse the envelope contents:
246
+ // 00 63 03 6f7264 51 <ct-push> 00 <data-push> 68
247
+ let pos = 12; // skip OP_FALSE + OP_IF + PUSH3 "ord"
248
+ pos += 2; // skip OP_1
249
+ const ctPush = readPushData(envelopeHex, pos);
250
+ if (!ctPush)
251
+ return null;
252
+ pos += ctPush.bytesRead;
253
+ pos += 2; // skip OP_0
254
+ const dataPush = readPushData(envelopeHex, pos);
255
+ if (!dataPush)
256
+ return null;
257
+ return {
258
+ contentType: hexToUtf8(ctPush.data),
259
+ data: dataPush.data,
260
+ };
261
+ }
262
+ /**
263
+ * Remove the inscription envelope from a script, returning the bare script.
264
+ *
265
+ * @returns Script hex with the envelope removed, or the original if none found.
266
+ */
267
+ export function stripInscriptionEnvelope(scriptHex) {
268
+ const bounds = findInscriptionEnvelope(scriptHex);
269
+ if (!bounds)
270
+ return scriptHex;
271
+ return scriptHex.slice(0, bounds.startHex) + scriptHex.slice(bounds.endHex);
272
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Inscription data: content type and hex-encoded payload.
3
+ *
4
+ * The `data` field is a hex string representing the raw inscription bytes.
5
+ * For text content, encode the UTF-8 bytes as hex first. For BSV-20 JSON,
6
+ * use the `BSV20` / `BSV21` helper classes which handle encoding.
7
+ */
8
+ export interface Inscription {
9
+ contentType: string;
10
+ data: string;
11
+ }
12
+ /**
13
+ * Hex-char offsets bounding an inscription envelope within a script.
14
+ * Used internally by `findInscriptionEnvelope` and `stripInscriptionEnvelope`.
15
+ */
16
+ export interface EnvelopeBounds {
17
+ /** Hex-char offset where the envelope starts (at OP_FALSE). */
18
+ startHex: number;
19
+ /** Hex-char offset where the envelope ends (after OP_ENDIF). */
20
+ endHex: number;
21
+ }
@@ -0,0 +1,4 @@
1
+ // ---------------------------------------------------------------------------
2
+ // runar-sdk/ordinals/types.ts — Types for 1sat ordinals support
3
+ // ---------------------------------------------------------------------------
4
+ export {};
@@ -0,0 +1,65 @@
1
+ import type { Transaction } from '@bsv/sdk';
2
+ import type { TransactionData, UTXO } from '../types.js';
3
+ export interface Provider {
4
+ /** Fetch a transaction by its txid (as a plain data shape). */
5
+ getTransaction(txid: string): Promise<TransactionData>;
6
+ /**
7
+ * Broadcast a transaction. Returns the txid on success.
8
+ * Accepts a @bsv/sdk Transaction object — implementations call
9
+ * `tx.toHex()` (or `tx.toHexEF()` for ARC) as needed.
10
+ */
11
+ broadcast(tx: Transaction): Promise<string>;
12
+ /** Get all UTXOs for a given address. */
13
+ getUtxos(address: string): Promise<UTXO[]>;
14
+ /**
15
+ * Get the UTXO holding a contract identified by its script hash.
16
+ * Returns null if no matching UTXO is found on chain.
17
+ */
18
+ getContractUtxo(scriptHash: string): Promise<UTXO | null>;
19
+ /** Return the network this provider is connected to. */
20
+ getNetwork(): 'mainnet' | 'testnet';
21
+ /**
22
+ * Get the current fee rate in satoshis per KB (1000 bytes).
23
+ * Defaults to 100 sat/KB for BSV (0.1 sat/byte standard relay fee).
24
+ */
25
+ getFeeRate(): Promise<number>;
26
+ /** Fetch the raw transaction hex by its txid. */
27
+ getRawTransaction(txid: string): Promise<string>;
28
+ }
29
+ /**
30
+ * Report a failure that is deliberately non-fatal (R-273).
31
+ *
32
+ * Four paths in this SDK — two overlay submissions, the funding broadcast and
33
+ * the BEEF parse after deploy — are fire-and-forget by design: the caller's
34
+ * transaction is already broadcast, and an indexing or caching failure must not
35
+ * turn a successful spend into a thrown error. They were written as
36
+ * `.catch(() => {})` and `catch { /* non-fatal *\/ }`, which is the intent
37
+ * correctly expressed and the evidence thrown away: an overlay that is down
38
+ * looks exactly like an overlay that is fine.
39
+ *
40
+ * `console.warn` is what the rest of this SDK already uses for advisory
41
+ * failures (contract.ts and providers/mock.ts), so these now match.
42
+ */
43
+ export declare function warnNonFatal(context: string, err: unknown): void;
44
+ /**
45
+ * Audit finding C4: project a locally-held @bsv/sdk `Transaction` into the
46
+ * plain `TransactionData` shape `getTransaction()` returns.
47
+ *
48
+ * Two call sites need exactly this, and they must not drift apart:
49
+ * - `MockProvider.broadcast()` registers the tx it just accepted, so
50
+ * `getTransaction()` can resolve it instead of throwing "not found".
51
+ * - `RunarContract.deploy()` / `finalizeCall()` fall back to it when
52
+ * `provider.getTransaction()` fails (a real node can 404 a transaction it
53
+ * has accepted but not yet indexed).
54
+ *
55
+ * Both previously improvised their own shape, and the contract.ts one
56
+ * improvised an EMPTY one — `inputs: []`, `outputs: []` — indistinguishable
57
+ * from a confirmed transaction with no outputs, which is what made every
58
+ * post-broadcast `result.tx.outputs` assertion vacuous. Lives here because
59
+ * `providers/provider.ts` is a leaf both sides already import.
60
+ *
61
+ * `txid` is passed in rather than read off `tx`: the mock provider assigns
62
+ * synthetic txids, and the real deploy/call path uses the txid the provider
63
+ * returned from `broadcast()`.
64
+ */
65
+ export declare function txToTransactionData(txid: string, tx: Transaction): TransactionData;
@@ -0,0 +1,59 @@
1
+ // ---------------------------------------------------------------------------
2
+ // runar-sdk/providers/provider.ts — Provider interface for blockchain access
3
+ // ---------------------------------------------------------------------------
4
+ /**
5
+ * Report a failure that is deliberately non-fatal (R-273).
6
+ *
7
+ * Four paths in this SDK — two overlay submissions, the funding broadcast and
8
+ * the BEEF parse after deploy — are fire-and-forget by design: the caller's
9
+ * transaction is already broadcast, and an indexing or caching failure must not
10
+ * turn a successful spend into a thrown error. They were written as
11
+ * `.catch(() => {})` and `catch { /* non-fatal *\/ }`, which is the intent
12
+ * correctly expressed and the evidence thrown away: an overlay that is down
13
+ * looks exactly like an overlay that is fine.
14
+ *
15
+ * `console.warn` is what the rest of this SDK already uses for advisory
16
+ * failures (contract.ts and providers/mock.ts), so these now match.
17
+ */
18
+ export function warnNonFatal(context, err) {
19
+ console.warn(`runar-sdk: ${context} (non-fatal):`, err);
20
+ }
21
+ /**
22
+ * Audit finding C4: project a locally-held @bsv/sdk `Transaction` into the
23
+ * plain `TransactionData` shape `getTransaction()` returns.
24
+ *
25
+ * Two call sites need exactly this, and they must not drift apart:
26
+ * - `MockProvider.broadcast()` registers the tx it just accepted, so
27
+ * `getTransaction()` can resolve it instead of throwing "not found".
28
+ * - `RunarContract.deploy()` / `finalizeCall()` fall back to it when
29
+ * `provider.getTransaction()` fails (a real node can 404 a transaction it
30
+ * has accepted but not yet indexed).
31
+ *
32
+ * Both previously improvised their own shape, and the contract.ts one
33
+ * improvised an EMPTY one — `inputs: []`, `outputs: []` — indistinguishable
34
+ * from a confirmed transaction with no outputs, which is what made every
35
+ * post-broadcast `result.tx.outputs` assertion vacuous. Lives here because
36
+ * `providers/provider.ts` is a leaf both sides already import.
37
+ *
38
+ * `txid` is passed in rather than read off `tx`: the mock provider assigns
39
+ * synthetic txids, and the real deploy/call path uses the txid the provider
40
+ * returned from `broadcast()`.
41
+ */
42
+ export function txToTransactionData(txid, tx) {
43
+ return {
44
+ txid,
45
+ version: tx.version,
46
+ inputs: tx.inputs.map((input) => ({
47
+ txid: input.sourceTXID ?? input.sourceTransaction?.id('hex') ?? '',
48
+ outputIndex: input.sourceOutputIndex,
49
+ script: input.unlockingScript?.toHex() ?? '',
50
+ sequence: input.sequence ?? 0xffffffff,
51
+ })),
52
+ outputs: tx.outputs.map((out) => ({
53
+ satoshis: out.satoshis ?? 0,
54
+ script: out.lockingScript.toHex(),
55
+ })),
56
+ locktime: tx.lockTime,
57
+ raw: tx.toHex(),
58
+ };
59
+ }
@@ -0,0 +1,94 @@
1
+ import type { Provider } from './provider.js';
2
+ import type { Signer } from '../signers/signer.js';
3
+ import type { TransactionData, UTXO } from '../types.js';
4
+ import { Transaction, type WalletClient, type Broadcaster } from '@bsv/sdk';
5
+ export interface WalletProviderOptions {
6
+ /** BRC-100 WalletClient instance. */
7
+ wallet: WalletClient;
8
+ /** Signer derived from the same wallet (e.g. WalletSigner). */
9
+ signer: Signer;
10
+ /** Wallet basket name for UTXO management (e.g. 'my-app'). */
11
+ basket: string;
12
+ /** Tag for funding UTXOs within the basket (default: 'funding'). */
13
+ fundingTag?: string;
14
+ /**
15
+ * ARC broadcast endpoint. Defaults to the MAINNET endpoint
16
+ * ('https://arc.gorillapool.io') and is therefore REQUIRED when `network` is
17
+ * anything other than 'mainnet' — see R-179.
18
+ */
19
+ arcUrl?: string;
20
+ /** Overlay service URL for tx submission and raw tx lookups (optional). */
21
+ overlayUrl?: string;
22
+ /** Overlay topic names for tx submission (optional, e.g. ['tm_myapp']). */
23
+ overlayTopics?: string[];
24
+ /** Network (default: 'mainnet'). */
25
+ network?: 'mainnet' | 'testnet';
26
+ /** Fee rate in sats/KB (default: 100, i.e. 0.1 sat/byte). */
27
+ feeRate?: number;
28
+ /**
29
+ * Injected `@bsv/sdk` Broadcaster (issue #107). When provided, `broadcast()`
30
+ * delegates to this instance instead of the hardcoded ARC URL, so the SDK
31
+ * and a downstream layer (e.g. wallet-toolbox) share ONE broadcaster/config
32
+ * — keeping parent and child txs at the same ARC deployment (avoids
33
+ * `SEEN_IN_ORPHAN_MEMPOOL`). Additive and non-breaking; omit for the default
34
+ * ARC path.
35
+ */
36
+ broadcaster?: Broadcaster;
37
+ }
38
+ export declare class WalletProvider implements Provider {
39
+ protected readonly wallet: WalletClient;
40
+ protected readonly signer: Signer;
41
+ protected readonly basket: string;
42
+ protected readonly fundingTag: string;
43
+ protected readonly arcUrl: string;
44
+ protected readonly overlayUrl: string | undefined;
45
+ protected readonly overlayTopics: string[] | undefined;
46
+ protected readonly _network: 'mainnet' | 'testnet';
47
+ protected readonly _feeRate: number;
48
+ protected readonly broadcaster: Broadcaster | undefined;
49
+ protected readonly txCache: Map<string, string>;
50
+ constructor(options: WalletProviderOptions);
51
+ /** The BRC-100 wallet client this provider wraps. */
52
+ get walletClient(): WalletClient;
53
+ /** The wallet basket used for UTXO management. */
54
+ get basketName(): string;
55
+ /** Cache a raw tx hex by its txid (for EF parent lookups). */
56
+ cacheTx(txid: string, rawHex: string): void;
57
+ /**
58
+ * Fetch raw tx hex: local cache → overlay → throw.
59
+ *
60
+ * Protected so subclasses can supply parents from another source (their
61
+ * own index, a node RPC, …); the EF assembly in `broadcastTx` dispatches
62
+ * through the override.
63
+ */
64
+ protected fetchRawTx(txid: string): Promise<string>;
65
+ /**
66
+ * Broadcast a transaction via ARC in EF format.
67
+ *
68
+ * Protected so subclasses can reroute broadcast (e.g. through an overlay
69
+ * that gates admission) while `broadcast()` and `ensureFunding()` keep
70
+ * dispatching through the override.
71
+ */
72
+ protected broadcastTx(tx: Transaction): Promise<string>;
73
+ /** Submit a transaction to the overlay for indexing (non-fatal).
74
+ * Protected so subclasses can adapt the submit request to their overlay. */
75
+ protected submitToOverlay(tx: Transaction): Promise<void>;
76
+ /**
77
+ * Get UTXOs from the wallet's basket.
78
+ * Returns only spendable P2PKH UTXOs locked to the signer's derived key.
79
+ */
80
+ getUtxos(_address: string): Promise<UTXO[]>;
81
+ getTransaction(txid: string): Promise<TransactionData>;
82
+ broadcast(tx: any): Promise<string>;
83
+ getContractUtxo(_scriptHash: string): Promise<UTXO | null>;
84
+ getNetwork(): 'mainnet' | 'testnet';
85
+ getRawTransaction(txid: string): Promise<string>;
86
+ getFeeRate(): Promise<number>;
87
+ /**
88
+ * Ensure there are enough P2PKH funding UTXOs in the wallet basket.
89
+ * Creates a new funding UTXO via the wallet if the balance is insufficient.
90
+ *
91
+ * @param minSatoshis - Minimum total satoshis required.
92
+ */
93
+ ensureFunding(minSatoshis: number): Promise<void>;
94
+ }