@dcasegr/amm-client 0.0.0-stage → 0.38.1

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,36 @@
1
+ import { Transaction } from '@bsv/sdk';
2
+ import type { UTXO } from './types.js';
3
+ /**
4
+ * Build a transaction that creates an output with the given locking script.
5
+ * The transaction consumes the provided UTXOs, places the contract output
6
+ * first, and sends any remaining value (minus fees) to a change address.
7
+ *
8
+ * Returns the unsigned Transaction object and the number of inputs (needed
9
+ * so the caller knows how many inputs to sign).
10
+ */
11
+ export declare function buildDeployTransaction(lockingScript: string, utxos: UTXO[], satoshis: number, changeAddress: string, changeScript: string, feeRate?: number): {
12
+ tx: Transaction;
13
+ inputCount: number;
14
+ };
15
+ /**
16
+ * Estimate the fee for a deploy transaction given the number of P2PKH
17
+ * inputs and the contract locking script byte length. Includes a P2PKH
18
+ * change output.
19
+ *
20
+ * @param numInputs - Number of P2PKH inputs.
21
+ * @param lockingScriptByteLen - Byte length of the contract locking script.
22
+ * @param feeRate - Fee rate in satoshis per KB (default: 100).
23
+ * @param extraInputBytes - Serialized byte size of any NON-P2PKH inputs
24
+ * that this fee must also cover (e.g. a contract/covenant input being spent
25
+ * in the same tx). Defaults to 0 — pure-deploy and funding-only selection
26
+ * are unaffected. Used by `prepareCall` so funding coin-selection sizes the
27
+ * fee against the real contract-input unlock bytes (which can be tens of KB
28
+ * for a MERGE that embeds both parent txs) instead of ignoring them.
29
+ */
30
+ export declare function estimateDeployFee(numInputs: number, lockingScriptByteLen: number, feeRate?: number, extraInputBytes?: number, extraOutputBytes?: number): number;
31
+ /**
32
+ * Select the minimum set of UTXOs needed to fund a deployment, using a
33
+ * largest-first strategy. Returns the selected subset (possibly all UTXOs
34
+ * if the total is still insufficient -- the caller should check).
35
+ */
36
+ export declare function selectUtxos(utxos: UTXO[], targetSatoshis: number, lockingScriptByteLen: number, feeRate?: number, extraInputBytes?: number, extraOutputBytes?: number): UTXO[];
@@ -0,0 +1,119 @@
1
+ // ---------------------------------------------------------------------------
2
+ // runar-sdk/deployment.ts -- Transaction construction for contract deployment
3
+ // ---------------------------------------------------------------------------
4
+ import { Transaction, LockingScript, UnlockingScript } from '@bsv/sdk';
5
+ import { buildP2PKHScript } from './script-utils.js';
6
+ /**
7
+ * Build a transaction that creates an output with the given locking script.
8
+ * The transaction consumes the provided UTXOs, places the contract output
9
+ * first, and sends any remaining value (minus fees) to a change address.
10
+ *
11
+ * Returns the unsigned Transaction object and the number of inputs (needed
12
+ * so the caller knows how many inputs to sign).
13
+ */
14
+ export function buildDeployTransaction(lockingScript, utxos, satoshis, changeAddress, changeScript, feeRate = 100) {
15
+ if (utxos.length === 0) {
16
+ throw new Error('buildDeployTransaction: no UTXOs provided');
17
+ }
18
+ const totalInput = utxos.reduce((sum, u) => sum + u.satoshis, 0);
19
+ const fee = estimateDeployFee(utxos.length, lockingScript.length / 2, feeRate);
20
+ const change = totalInput - satoshis - fee;
21
+ if (change < 0) {
22
+ throw new Error(`buildDeployTransaction: insufficient funds. Need ${satoshis + fee} sats, have ${totalInput}`);
23
+ }
24
+ const tx = new Transaction();
25
+ // Inputs (unsigned — no unlocking script)
26
+ for (const utxo of utxos) {
27
+ tx.addInput({
28
+ sourceTXID: utxo.txid,
29
+ sourceOutputIndex: utxo.outputIndex,
30
+ unlockingScript: new UnlockingScript(),
31
+ sequence: 0xffffffff,
32
+ });
33
+ }
34
+ // Output 0: contract locking script
35
+ tx.addOutput({
36
+ satoshis,
37
+ lockingScript: LockingScript.fromHex(lockingScript),
38
+ });
39
+ // Output 1: change (if any)
40
+ if (change > 0) {
41
+ const actualChangeScript = changeScript || buildP2PKHScript(changeAddress);
42
+ tx.addOutput({
43
+ satoshis: change,
44
+ lockingScript: LockingScript.fromHex(actualChangeScript),
45
+ });
46
+ }
47
+ return { tx, inputCount: utxos.length };
48
+ }
49
+ // ---------------------------------------------------------------------------
50
+ // Fee estimation
51
+ // ---------------------------------------------------------------------------
52
+ /** Estimated size of a P2PKH input (prevTxid + index + sig + pubkey + seq). */
53
+ const P2PKH_INPUT_SIZE = 148;
54
+ /** Estimated size of a P2PKH output (satoshis + varint + 25-byte script). */
55
+ const P2PKH_OUTPUT_SIZE = 34;
56
+ /** Transaction overhead: version(4) + input varint(1) + output varint(1) + locktime(4). */
57
+ const TX_OVERHEAD = 10;
58
+ function varIntByteSize(n) {
59
+ if (n < 0xfd)
60
+ return 1;
61
+ if (n <= 0xffff)
62
+ return 3;
63
+ if (n <= 0xffffffff)
64
+ return 5;
65
+ return 9;
66
+ }
67
+ /**
68
+ * Estimate the fee for a deploy transaction given the number of P2PKH
69
+ * inputs and the contract locking script byte length. Includes a P2PKH
70
+ * change output.
71
+ *
72
+ * @param numInputs - Number of P2PKH inputs.
73
+ * @param lockingScriptByteLen - Byte length of the contract locking script.
74
+ * @param feeRate - Fee rate in satoshis per KB (default: 100).
75
+ * @param extraInputBytes - Serialized byte size of any NON-P2PKH inputs
76
+ * that this fee must also cover (e.g. a contract/covenant input being spent
77
+ * in the same tx). Defaults to 0 — pure-deploy and funding-only selection
78
+ * are unaffected. Used by `prepareCall` so funding coin-selection sizes the
79
+ * fee against the real contract-input unlock bytes (which can be tens of KB
80
+ * for a MERGE that embeds both parent txs) instead of ignoring them.
81
+ */
82
+ export function estimateDeployFee(numInputs, lockingScriptByteLen, feeRate = 100, extraInputBytes = 0, extraOutputBytes = 0) {
83
+ const inputsSize = numInputs * P2PKH_INPUT_SIZE + extraInputBytes;
84
+ const contractOutputSize = 8 + varIntByteSize(lockingScriptByteLen) + lockingScriptByteLen;
85
+ // extraOutputBytes carries the serialized framing (8 + varint + scriptLen) of
86
+ // every output BEYOND the single `lockingScriptByteLen` one counted above —
87
+ // additional multi-outputs, raw outputs (finding G1), and data outputs. A
88
+ // call-path selection that ignored them under-provisioned funding on
89
+ // multi-output / large-dataOutput calls, so it would stop one UTXO short and
90
+ // the built tx's change would go negative (finding C15). Deploy passes 0.
91
+ const changeOutputSize = P2PKH_OUTPUT_SIZE;
92
+ const txSize = TX_OVERHEAD + inputsSize + contractOutputSize + extraOutputBytes + changeOutputSize;
93
+ return Math.ceil(txSize * feeRate / 1000);
94
+ }
95
+ /**
96
+ * Select the minimum set of UTXOs needed to fund a deployment, using a
97
+ * largest-first strategy. Returns the selected subset (possibly all UTXOs
98
+ * if the total is still insufficient -- the caller should check).
99
+ */
100
+ export function selectUtxos(utxos, targetSatoshis, lockingScriptByteLen, feeRate = 100, extraInputBytes = 0, extraOutputBytes = 0) {
101
+ const sorted = [...utxos].sort((a, b) => b.satoshis - a.satoshis);
102
+ const selected = [];
103
+ let total = 0;
104
+ for (const utxo of sorted) {
105
+ selected.push(utxo);
106
+ total += utxo.satoshis;
107
+ // extraInputBytes carries the serialized size of any non-P2PKH inputs the
108
+ // same tx spends (the contract/covenant input); extraOutputBytes carries the
109
+ // framing of any outputs beyond the single continuation (multi-output / raw /
110
+ // data outputs). Both keep the fee — and therefore how much funding we
111
+ // select — from being under-provisioned (findings C2 / C15).
112
+ const fee = estimateDeployFee(selected.length, lockingScriptByteLen, feeRate, extraInputBytes, extraOutputBytes);
113
+ if (total >= targetSatoshis + fee) {
114
+ return selected;
115
+ }
116
+ }
117
+ // Return all UTXOs; buildDeployTransaction will throw if still insufficient
118
+ return selected;
119
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Thrown when a deploy / call entry point or provider UTXO lookup
3
+ * encounters a script that exceeds {@link InputLimits.MAX_SCRIPT_BYTES}.
4
+ *
5
+ * Guards fire BEFORE any heavy operation (signing, broadcasting) so that
6
+ * pathological scripts cannot drive the SDK into expensive crypto work.
7
+ */
8
+ export declare class ScriptSizeExceededError extends Error {
9
+ readonly limit: number;
10
+ readonly actual: number;
11
+ readonly context: string;
12
+ constructor(info: {
13
+ limit: number;
14
+ actual: number;
15
+ context: string;
16
+ });
17
+ }
18
+ /**
19
+ * Assert that a script (hex string or byte length) is within the SDK's
20
+ * MAX_SCRIPT_BYTES bound. Throws {@link ScriptSizeExceededError} otherwise.
21
+ *
22
+ * @param scriptHex hex-encoded script (2 hex chars per byte)
23
+ * @param context human-readable origin string for the error message
24
+ */
25
+ export declare function assertScriptHexUnderLimit(scriptHex: string, limit: number, context: string): void;
26
+ /**
27
+ * Thrown when a method call requires a caller-supplied intent-intrinsic
28
+ * witness value (auto-injected `_prevOutScript_<i>` or `_serialisedOutputs`)
29
+ * that has not been set on the RunarContract.
30
+ *
31
+ * Auto-injected witness params come from the compiler when a contract method
32
+ * uses `extractPrevOutputScript(i)` or `requireOutputP2PKH(...)`. The caller
33
+ * must supply concrete bytes for each before invoking `call()` /
34
+ * `prepareCall()` via {@link RunarContract.setPrevOutScript} and
35
+ * {@link RunarContract.setSerialisedOutputs}.
36
+ */
37
+ export declare class WitnessValueMissingError extends Error {
38
+ readonly paramName: string;
39
+ readonly methodName: string;
40
+ readonly contractName: string;
41
+ constructor(info: {
42
+ paramName: string;
43
+ methodName: string;
44
+ contractName: string;
45
+ });
46
+ }
@@ -0,0 +1,59 @@
1
+ // ---------------------------------------------------------------------------
2
+ // runar-sdk/errors.ts — Typed SDK errors
3
+ // ---------------------------------------------------------------------------
4
+ /**
5
+ * Thrown when a deploy / call entry point or provider UTXO lookup
6
+ * encounters a script that exceeds {@link InputLimits.MAX_SCRIPT_BYTES}.
7
+ *
8
+ * Guards fire BEFORE any heavy operation (signing, broadcasting) so that
9
+ * pathological scripts cannot drive the SDK into expensive crypto work.
10
+ */
11
+ export class ScriptSizeExceededError extends Error {
12
+ limit;
13
+ actual;
14
+ context;
15
+ constructor(info) {
16
+ super(`script exceeds MAX_SCRIPT_BYTES (limit=${info.limit}, actual=${info.actual}, context=${info.context})`);
17
+ this.name = 'ScriptSizeExceededError';
18
+ this.limit = info.limit;
19
+ this.actual = info.actual;
20
+ this.context = info.context;
21
+ }
22
+ }
23
+ /**
24
+ * Assert that a script (hex string or byte length) is within the SDK's
25
+ * MAX_SCRIPT_BYTES bound. Throws {@link ScriptSizeExceededError} otherwise.
26
+ *
27
+ * @param scriptHex hex-encoded script (2 hex chars per byte)
28
+ * @param context human-readable origin string for the error message
29
+ */
30
+ export function assertScriptHexUnderLimit(scriptHex, limit, context) {
31
+ // hex string is 2x the byte length; tolerate odd-length defensively
32
+ const actualBytes = Math.ceil(scriptHex.length / 2);
33
+ if (actualBytes > limit) {
34
+ throw new ScriptSizeExceededError({ limit, actual: actualBytes, context });
35
+ }
36
+ }
37
+ /**
38
+ * Thrown when a method call requires a caller-supplied intent-intrinsic
39
+ * witness value (auto-injected `_prevOutScript_<i>` or `_serialisedOutputs`)
40
+ * that has not been set on the RunarContract.
41
+ *
42
+ * Auto-injected witness params come from the compiler when a contract method
43
+ * uses `extractPrevOutputScript(i)` or `requireOutputP2PKH(...)`. The caller
44
+ * must supply concrete bytes for each before invoking `call()` /
45
+ * `prepareCall()` via {@link RunarContract.setPrevOutScript} and
46
+ * {@link RunarContract.setSerialisedOutputs}.
47
+ */
48
+ export class WitnessValueMissingError extends Error {
49
+ paramName;
50
+ methodName;
51
+ contractName;
52
+ constructor(info) {
53
+ super(`witness value missing for auto-injected param '${info.paramName}' on ${info.contractName}.${info.methodName} — call setPrevOutScript(i, bytes) or setSerialisedOutputs(bytes) before invoking the method`);
54
+ this.name = 'WitnessValueMissingError';
55
+ this.paramName = info.paramName;
56
+ this.methodName = info.methodName;
57
+ this.contractName = info.contractName;
58
+ }
59
+ }
@@ -0,0 +1,23 @@
1
+ import { Transaction } from '@bsv/sdk';
2
+ /**
3
+ * Compute the OP_PUSH_TX DER signature and BIP-143 preimage for a contract input.
4
+ *
5
+ * @param txOrHex - The Transaction object or raw hex string
6
+ * @param inputIndex - The contract input index (usually 0)
7
+ * @param subscript - The locking script of the UTXO being spent (hex)
8
+ * @param satoshis - The satoshi value of the UTXO being spent
9
+ * @param codeSeparatorIndex - Byte offset of OP_CODESEPARATOR in the locking script (optional).
10
+ * When present, the scriptCode in BIP-143 is the portion AFTER
11
+ * the OP_CODESEPARATOR (excluding the separator byte itself).
12
+ * @param sigHashType - BIP-143 sighash type the preimage is built under (issue #123).
13
+ * Defaults to `0x41` (ALL|FORKID). Must match the method's declared
14
+ * `@sighash` mode: it drives which preimage fields @bsv/sdk zeroes
15
+ * (hashPrevouts / hashSequence / hashOutputs) AND the sighash flag
16
+ * byte appended to the DER signature. A mismatch makes the on-chain
17
+ * OP_PUSH_TX binding fail to verify.
18
+ * @returns Object with `sigHex` (DER + sighash byte) and `preimageHex` (raw preimage)
19
+ */
20
+ export declare function computeOpPushTx(txOrHex: Transaction | string, inputIndex: number, subscript: string, satoshis: number, codeSeparatorIndex?: number, sigHashType?: number): {
21
+ sigHex: string;
22
+ preimageHex: string;
23
+ };
@@ -0,0 +1,95 @@
1
+ // ---------------------------------------------------------------------------
2
+ // runar-sdk/oppushtx.ts — OP_PUSH_TX helper for checkPreimage contracts
3
+ // ---------------------------------------------------------------------------
4
+ //
5
+ // Computes the BIP-143 sighash preimage and OP_PUSH_TX signature for contracts
6
+ // that use checkPreimage() (both stateful and stateless).
7
+ //
8
+ // The OP_PUSH_TX technique uses private key k=1 (public key = generator point G).
9
+ // The on-chain script derives the signature from the preimage, so both must be
10
+ // provided in the unlocking script.
11
+ // ---------------------------------------------------------------------------
12
+ import { PrivateKey, TransactionSignature, Hash, Transaction, Script, BigNumber } from '@bsv/sdk';
13
+ /** SIGHASH_ALL | SIGHASH_FORKID — the default BSV sighash type. */
14
+ const SIGHASH_ALL_FORKID = 0x41;
15
+ /** secp256k1 curve order N. */
16
+ const CURVE_ORDER = new BigNumber('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16);
17
+ /** OP_PUSH_TX private key (k=1). */
18
+ const opPushTxPrivKey = PrivateKey.fromHex('0000000000000000000000000000000000000000000000000000000000000001');
19
+ /**
20
+ * Compute the OP_PUSH_TX DER signature and BIP-143 preimage for a contract input.
21
+ *
22
+ * @param txOrHex - The Transaction object or raw hex string
23
+ * @param inputIndex - The contract input index (usually 0)
24
+ * @param subscript - The locking script of the UTXO being spent (hex)
25
+ * @param satoshis - The satoshi value of the UTXO being spent
26
+ * @param codeSeparatorIndex - Byte offset of OP_CODESEPARATOR in the locking script (optional).
27
+ * When present, the scriptCode in BIP-143 is the portion AFTER
28
+ * the OP_CODESEPARATOR (excluding the separator byte itself).
29
+ * @param sigHashType - BIP-143 sighash type the preimage is built under (issue #123).
30
+ * Defaults to `0x41` (ALL|FORKID). Must match the method's declared
31
+ * `@sighash` mode: it drives which preimage fields @bsv/sdk zeroes
32
+ * (hashPrevouts / hashSequence / hashOutputs) AND the sighash flag
33
+ * byte appended to the DER signature. A mismatch makes the on-chain
34
+ * OP_PUSH_TX binding fail to verify.
35
+ * @returns Object with `sigHex` (DER + sighash byte) and `preimageHex` (raw preimage)
36
+ */
37
+ export function computeOpPushTx(txOrHex, inputIndex, subscript, satoshis, codeSeparatorIndex, sigHashType = SIGHASH_ALL_FORKID) {
38
+ const tx = typeof txOrHex === 'string' ? Transaction.fromHex(txOrHex) : txOrHex;
39
+ const input = tx.inputs[inputIndex];
40
+ const otherInputs = tx.inputs
41
+ .filter((_inp, i) => i !== inputIndex)
42
+ .map((inp) => ({
43
+ sourceTXID: inp.sourceTXID,
44
+ sourceOutputIndex: inp.sourceOutputIndex,
45
+ sequence: inp.sequence,
46
+ }));
47
+ const outputs = tx.outputs.map((out) => ({
48
+ satoshis: out.satoshis,
49
+ lockingScript: out.lockingScript,
50
+ }));
51
+ // If OP_CODESEPARATOR is present, use only the script after it as scriptCode.
52
+ // The separator byte itself (0xab) is excluded from the scriptCode.
53
+ let scriptCode = subscript;
54
+ if (codeSeparatorIndex !== undefined) {
55
+ // Each byte is 2 hex chars. Skip past the separator byte (+1 byte = +2 hex chars).
56
+ // R-178: String.slice() past the end returns '', so an out-of-range index
57
+ // used to sign an EMPTY scriptCode — silently, from a fund-moving
58
+ // primitive. There is no correct signature for a separator offset that is
59
+ // not in the script.
60
+ const trimPos = (codeSeparatorIndex + 1) * 2;
61
+ if (trimPos > subscript.length) {
62
+ throw new Error(`computeOpPushTx: codeSeparatorIndex ${codeSeparatorIndex} is past the end of ` +
63
+ `the subscript (${subscript.length / 2} bytes)`);
64
+ }
65
+ scriptCode = subscript.slice(trimPos);
66
+ }
67
+ // Compute BIP-143 preimage
68
+ const preimage = TransactionSignature.format({
69
+ sourceTXID: input.sourceTXID,
70
+ sourceOutputIndex: input.sourceOutputIndex,
71
+ sourceSatoshis: satoshis,
72
+ transactionVersion: tx.version,
73
+ otherInputs: otherInputs,
74
+ outputs: outputs,
75
+ inputIndex,
76
+ subscript: Script.fromHex(scriptCode),
77
+ inputSequence: input.sequence,
78
+ lockTime: tx.lockTime,
79
+ scope: sigHashType,
80
+ });
81
+ // Double-SHA256 for BIP-143 sighash
82
+ const sighash = Hash.sha256(preimage);
83
+ // Sign with k=1 private key
84
+ const signature = opPushTxPrivKey.sign(sighash);
85
+ // Enforce low-S
86
+ const halfN = CURVE_ORDER.div(new BigNumber(2));
87
+ if (signature.s.gt(halfN)) {
88
+ signature.s = CURVE_ORDER.sub(signature.s);
89
+ }
90
+ const derHex = signature.toDER('hex');
91
+ const sigHex = derHex + (sigHashType & 0xff).toString(16).padStart(2, '0');
92
+ // Convert preimage to hex
93
+ const preimageHex = Array.from(preimage).map((b) => b.toString(16).padStart(2, '0')).join('');
94
+ return { sigHex, preimageHex };
95
+ }
@@ -0,0 +1,31 @@
1
+ import type { Inscription, EnvelopeBounds } from './types.js';
2
+ /**
3
+ * Build a 1sat ordinals inscription envelope as hex.
4
+ *
5
+ * @param contentType - MIME type (e.g. "image/png", "application/bsv-20")
6
+ * @param data - Hex-encoded inscription content
7
+ * @returns Hex string of the full envelope script fragment
8
+ */
9
+ export declare function buildInscriptionEnvelope(contentType: string, data: string): string;
10
+ /**
11
+ * Find the inscription envelope within a script hex string.
12
+ *
13
+ * Walks the script as Bitcoin Script opcodes (identical in shape to
14
+ * `findLastOpReturn` in state.ts) looking for the pattern:
15
+ * OP_FALSE(00) OP_IF(63) PUSH3 "ord"(03 6f7264) ...
16
+ *
17
+ * @returns Hex-char offsets of the envelope, or null if not found.
18
+ */
19
+ export declare function findInscriptionEnvelope(scriptHex: string): EnvelopeBounds | null;
20
+ /**
21
+ * Parse an inscription envelope from a script hex string.
22
+ *
23
+ * @returns The inscription data, or null if no envelope is found.
24
+ */
25
+ export declare function parseInscriptionEnvelope(scriptHex: string): Inscription | null;
26
+ /**
27
+ * Remove the inscription envelope from a script, returning the bare script.
28
+ *
29
+ * @returns Script hex with the envelope removed, or the original if none found.
30
+ */
31
+ export declare function stripInscriptionEnvelope(scriptHex: string): string;