@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.
- package/README.md +107 -2
- package/dist/client.d.ts +534 -0
- package/dist/client.js +1319 -0
- package/dist/errors.d.ts +17 -0
- package/dist/errors.js +75 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +53 -0
- package/dist/lib/config.d.ts +42 -0
- package/dist/lib/config.js +64 -0
- package/dist/lib/keys.d.ts +42 -0
- package/dist/lib/keys.js +48 -0
- package/dist/lib/overlay.d.ts +190 -0
- package/dist/lib/overlay.js +302 -0
- package/dist/lib/skein.d.ts +82 -0
- package/dist/lib/skein.js +142 -0
- package/dist/lib/tokenId.d.ts +72 -0
- package/dist/lib/tokenId.js +121 -0
- package/dist/lp/amounts.d.ts +12 -0
- package/dist/lp/amounts.js +38 -0
- package/dist/lp/close.d.ts +90 -0
- package/dist/lp/close.js +140 -0
- package/dist/lp/images.d.ts +11 -0
- package/dist/lp/images.js +45 -0
- package/dist/lp/inventory.d.ts +68 -0
- package/dist/lp/inventory.js +84 -0
- package/dist/lp/poolDeploy.d.ts +220 -0
- package/dist/lp/poolDeploy.js +291 -0
- package/dist/lp/poolRows.d.ts +13 -0
- package/dist/lp/poolRows.js +19 -0
- package/dist/lp/positions.d.ts +81 -0
- package/dist/lp/positions.js +144 -0
- package/dist/lp/rebalance.d.ts +101 -0
- package/dist/lp/rebalance.js +168 -0
- package/dist/lp/wallet.d.ts +12 -0
- package/dist/lp/wallet.js +19 -0
- package/dist/market/activeSwap.d.ts +158 -0
- package/dist/market/activeSwap.js +320 -0
- package/dist/market/plan.d.ts +85 -0
- package/dist/market/plan.js +149 -0
- package/dist/market/prices.d.ts +13 -0
- package/dist/market/prices.js +14 -0
- package/dist/market/relay.d.ts +84 -0
- package/dist/market/relay.js +201 -0
- package/dist/market/swapAction.d.ts +306 -0
- package/dist/market/swapAction.js +508 -0
- package/dist/market/swapFlow.d.ts +58 -0
- package/dist/market/swapFlow.js +86 -0
- package/dist/market/view.d.ts +58 -0
- package/dist/market/view.js +63 -0
- package/dist/pool/artifact.d.ts +23 -0
- package/dist/pool/artifact.js +38 -0
- package/dist/pool/index.d.ts +8 -0
- package/dist/pool/index.js +8 -0
- package/dist/pool/oneColor.d.ts +19 -0
- package/dist/pool/oneColor.js +39 -0
- package/dist/pool/pool.artifact.json +5069 -0
- package/dist/pool/runar-ir-schema.d.ts +21 -0
- package/dist/pool/runar-ir-schema.js +18 -0
- package/dist/pool/template.d.ts +295 -0
- package/dist/pool/template.js +715 -0
- package/dist/vendor/runar/LICENSE +21 -0
- package/dist/vendor/runar/NOTICE +15 -0
- package/dist/vendor/runar/runar-ir-schema/src/abi-type-encoding.d.ts +42 -0
- package/dist/vendor/runar/runar-ir-schema/src/abi-type-encoding.js +53 -0
- package/dist/vendor/runar/runar-ir-schema/src/anf-ir.d.ts +221 -0
- package/dist/vendor/runar/runar-ir-schema/src/anf-ir.js +33 -0
- package/dist/vendor/runar/runar-ir-schema/src/artifact.d.ts +288 -0
- package/dist/vendor/runar/runar-ir-schema/src/artifact.js +8 -0
- package/dist/vendor/runar/runar-ir-schema/src/canonical-json.d.ts +43 -0
- package/dist/vendor/runar/runar-ir-schema/src/canonical-json.js +204 -0
- package/dist/vendor/runar/runar-ir-schema/src/input-limits.d.ts +89 -0
- package/dist/vendor/runar/runar-ir-schema/src/input-limits.js +100 -0
- package/dist/vendor/runar/runar-ir-schema/src/stack-ir.d.ts +118 -0
- package/dist/vendor/runar/runar-ir-schema/src/stack-ir.js +8 -0
- package/dist/vendor/runar/runar-ir-schema/src/state-layout.d.ts +56 -0
- package/dist/vendor/runar/runar-ir-schema/src/state-layout.js +145 -0
- package/dist/vendor/runar/runar-ir-schema/src/unknown-anf-kind-error.d.ts +18 -0
- package/dist/vendor/runar/runar-ir-schema/src/unknown-anf-kind-error.js +25 -0
- package/dist/vendor/runar/runar-sdk/src/anf-interpreter.d.ts +160 -0
- package/dist/vendor/runar/runar-sdk/src/anf-interpreter.js +1149 -0
- package/dist/vendor/runar/runar-sdk/src/calling.d.ts +98 -0
- package/dist/vendor/runar/runar-sdk/src/calling.js +209 -0
- package/dist/vendor/runar/runar-sdk/src/contract.d.ts +443 -0
- package/dist/vendor/runar/runar-sdk/src/contract.js +2516 -0
- package/dist/vendor/runar/runar-sdk/src/deployment.d.ts +36 -0
- package/dist/vendor/runar/runar-sdk/src/deployment.js +119 -0
- package/dist/vendor/runar/runar-sdk/src/errors.d.ts +46 -0
- package/dist/vendor/runar/runar-sdk/src/errors.js +59 -0
- package/dist/vendor/runar/runar-sdk/src/oppushtx.d.ts +23 -0
- package/dist/vendor/runar/runar-sdk/src/oppushtx.js +95 -0
- package/dist/vendor/runar/runar-sdk/src/ordinals/envelope.d.ts +31 -0
- package/dist/vendor/runar/runar-sdk/src/ordinals/envelope.js +272 -0
- package/dist/vendor/runar/runar-sdk/src/ordinals/types.d.ts +21 -0
- package/dist/vendor/runar/runar-sdk/src/ordinals/types.js +4 -0
- package/dist/vendor/runar/runar-sdk/src/providers/provider.d.ts +65 -0
- package/dist/vendor/runar/runar-sdk/src/providers/provider.js +59 -0
- package/dist/vendor/runar/runar-sdk/src/providers/wallet-provider.d.ts +94 -0
- package/dist/vendor/runar/runar-sdk/src/providers/wallet-provider.js +297 -0
- package/dist/vendor/runar/runar-sdk/src/script-utils.d.ts +54 -0
- package/dist/vendor/runar/runar-sdk/src/script-utils.js +252 -0
- package/dist/vendor/runar/runar-sdk/src/signers/signer.d.ts +17 -0
- package/dist/vendor/runar/runar-sdk/src/signers/signer.js +4 -0
- package/dist/vendor/runar/runar-sdk/src/spend-safety.d.ts +31 -0
- package/dist/vendor/runar/runar-sdk/src/spend-safety.js +35 -0
- package/dist/vendor/runar/runar-sdk/src/state.d.ts +54 -0
- package/dist/vendor/runar/runar-sdk/src/state.js +584 -0
- package/dist/vendor/runar/runar-sdk/src/types.d.ts +227 -0
- package/dist/vendor/runar/runar-sdk/src/types.js +4 -0
- package/dist/vendor/runar/runar-sdk/src/unsound-primitives.d.ts +30 -0
- package/dist/vendor/runar/runar-sdk/src/unsound-primitives.js +42 -0
- package/dist/vendor/runar/runar-sdk.d.ts +9 -0
- package/dist/vendor/runar/runar-sdk.js +8 -0
- package/dist/wallet/action.d.ts +28 -0
- package/dist/wallet/action.js +42 -0
- package/dist/wallet/brc29.d.ts +83 -0
- package/dist/wallet/brc29.js +80 -0
- package/dist/wallet/color.d.ts +110 -0
- package/dist/wallet/color.js +134 -0
- package/dist/wallet/swapPayouts.d.ts +35 -0
- package/dist/wallet/swapPayouts.js +146 -0
- 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,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
|
+
}
|