@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,98 @@
|
|
|
1
|
+
import { Transaction } from '@bsv/sdk';
|
|
2
|
+
import type { RunarArtifact } from '../../../../pool/runar-ir-schema.js';
|
|
3
|
+
import type { UTXO } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Build a transaction that spends a contract UTXO (method call).
|
|
6
|
+
*
|
|
7
|
+
* The transaction:
|
|
8
|
+
* - Input 0: the current contract UTXO with the given unlocking script.
|
|
9
|
+
* - Additional inputs: funding UTXOs if provided.
|
|
10
|
+
* - Output 0 (optional): new contract UTXO with updated locking script
|
|
11
|
+
* (for stateful contracts).
|
|
12
|
+
* - Last output (optional): change.
|
|
13
|
+
*
|
|
14
|
+
* Returns the Transaction object (with unlocking script for input 0
|
|
15
|
+
* already placed) and the total input count.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildCallTransaction(currentUtxo: UTXO, unlockingScript: string, newLockingScript?: string, newSatoshis?: number, changeAddress?: string, changeScript?: string, additionalUtxos?: UTXO[], feeRate?: number, options?: {
|
|
18
|
+
/** Multiple contract outputs (replaces single newLockingScript). */
|
|
19
|
+
contractOutputs?: Array<{
|
|
20
|
+
script: string;
|
|
21
|
+
satoshis: number;
|
|
22
|
+
}>;
|
|
23
|
+
/** Additional contract inputs with their own unlocking scripts (for merge). */
|
|
24
|
+
additionalContractInputs?: Array<{
|
|
25
|
+
utxo: UTXO;
|
|
26
|
+
unlockingScript: string;
|
|
27
|
+
}>;
|
|
28
|
+
/** Data outputs declared via `this.addDataOutput(...)` in the method
|
|
29
|
+
* body. Emitted between contract (state) outputs and the change output,
|
|
30
|
+
* in declaration order — matching the compile-time continuation-hash
|
|
31
|
+
* layout. */
|
|
32
|
+
dataOutputs?: Array<{
|
|
33
|
+
script: string;
|
|
34
|
+
satoshis: number;
|
|
35
|
+
}>;
|
|
36
|
+
/** Override the call tx's nLockTime. Unset → defaults to 0 (legacy).
|
|
37
|
+
* Set to a non-zero value for contracts that assert
|
|
38
|
+
* `extractLocktime(preimage) >= deadline` (e.g. auction close/claim). */
|
|
39
|
+
locktime?: number;
|
|
40
|
+
/** Override the nSequence written onto every input (issue #131). Unset →
|
|
41
|
+
* 0xfffffffe when a non-zero locktime is set (so nLockTime is consensus-
|
|
42
|
+
* enforced), else 0xffffffff (final, legacy). */
|
|
43
|
+
sequence?: number;
|
|
44
|
+
}): {
|
|
45
|
+
tx: Transaction;
|
|
46
|
+
inputCount: number;
|
|
47
|
+
changeAmount: number;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Resolve the nSequence for a call tx's inputs (issue #131).
|
|
51
|
+
*
|
|
52
|
+
* An all-0xffffffff input set makes nLockTime a consensus no-op, so a
|
|
53
|
+
* locktime-gated method would be script-enforced (via extractLocktime) yet
|
|
54
|
+
* NOT consensus-enforced. When a non-zero locktime is set we therefore default
|
|
55
|
+
* every input to 0xfffffffe (non-final, enforceable). Explicit `sequence`
|
|
56
|
+
* always wins; with no/zero locktime we keep the legacy 0xffffffff.
|
|
57
|
+
*
|
|
58
|
+
* Shared by `buildCallTransaction` and the terminal-path tx builder so both
|
|
59
|
+
* sites stay byte-consistent.
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveInputSequence(locktime: number | undefined, sequence: number | undefined): number;
|
|
62
|
+
/**
|
|
63
|
+
* Estimate the fee for a method call transaction.
|
|
64
|
+
*
|
|
65
|
+
* @param lockingScriptByteLen - Byte length of the (single) contract
|
|
66
|
+
* continuation output's locking script.
|
|
67
|
+
* @param unlockingScriptByteLen - Byte length of the primary contract input's
|
|
68
|
+
* unlocking script.
|
|
69
|
+
* @param numFundingInputs - Number of P2PKH funding inputs (~148 bytes
|
|
70
|
+
* each), NOT continuation outputs. Use `extraOutputBytes` to size
|
|
71
|
+
* additional continuation/data outputs.
|
|
72
|
+
* @param feeRate - Sat/KB (default 100).
|
|
73
|
+
* @param extraOutputBytes - Serialized byte size of any outputs BEYOND
|
|
74
|
+
* the single `lockingScriptByteLen` one already counted (additional
|
|
75
|
+
* continuation outputs, data outputs) — mirrors `estimateDeployFee`'s
|
|
76
|
+
* `extraOutputBytes` in deployment.ts (finding C15). Defaults to 0.
|
|
77
|
+
*/
|
|
78
|
+
export declare function estimateCallFee(lockingScriptByteLen: number, unlockingScriptByteLen: number, numFundingInputs: number, feeRate?: number, extraOutputBytes?: number): number;
|
|
79
|
+
export interface EstimateFeeForArtifactOpts {
|
|
80
|
+
/** Sat/byte. Default 0.1. */
|
|
81
|
+
feeRate?: number;
|
|
82
|
+
/** Number of continuation outputs, each assumed the same byte size as the
|
|
83
|
+
* artifact's own locking script (homogeneous multi-output fan-out).
|
|
84
|
+
* Default 1. Fixed under finding C4: previously this value was fed
|
|
85
|
+
* directly into `estimateCallFee`'s `numFundingInputs` slot (each priced
|
|
86
|
+
* at ~148 bytes, a P2PKH input) instead of sizing extra continuation
|
|
87
|
+
* OUTPUTS — silently mispricing every caller of this helper. */
|
|
88
|
+
outputCount?: number;
|
|
89
|
+
/** Unlocking-script byte length. Default: ceil(artifact.script.length / 4). */
|
|
90
|
+
unlockingScriptLen?: number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Estimate the fee for a single call against a deployed contract built
|
|
94
|
+
* from the given artifact. Wraps {@link estimateCallFee} with defaults
|
|
95
|
+
* derived from the artifact and accepts `feeRate` in sat/byte (vs the
|
|
96
|
+
* sat/kilobyte unit `estimateCallFee` itself takes).
|
|
97
|
+
*/
|
|
98
|
+
export declare function estimateFeeForArtifact(artifact: RunarArtifact, opts?: EstimateFeeForArtifactOpts): number;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// runar-sdk/calling.ts — Transaction construction for method invocation
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
import { Transaction, LockingScript, UnlockingScript } from '@bsv/sdk';
|
|
5
|
+
import { buildP2PKHScript } from './script-utils.js';
|
|
6
|
+
/**
|
|
7
|
+
* Build a transaction that spends a contract UTXO (method call).
|
|
8
|
+
*
|
|
9
|
+
* The transaction:
|
|
10
|
+
* - Input 0: the current contract UTXO with the given unlocking script.
|
|
11
|
+
* - Additional inputs: funding UTXOs if provided.
|
|
12
|
+
* - Output 0 (optional): new contract UTXO with updated locking script
|
|
13
|
+
* (for stateful contracts).
|
|
14
|
+
* - Last output (optional): change.
|
|
15
|
+
*
|
|
16
|
+
* Returns the Transaction object (with unlocking script for input 0
|
|
17
|
+
* already placed) and the total input count.
|
|
18
|
+
*/
|
|
19
|
+
export function buildCallTransaction(currentUtxo, unlockingScript, newLockingScript, newSatoshis, changeAddress, changeScript, additionalUtxos, feeRate = 100, options) {
|
|
20
|
+
const extraContractInputs = options?.additionalContractInputs ?? [];
|
|
21
|
+
const dataOutputs = options?.dataOutputs ?? [];
|
|
22
|
+
const allUtxos = [currentUtxo, ...extraContractInputs.map((i) => i.utxo), ...(additionalUtxos ?? [])];
|
|
23
|
+
const totalInput = allUtxos.reduce((sum, u) => sum + u.satoshis, 0);
|
|
24
|
+
// Determine contract outputs: multi-output takes priority over single
|
|
25
|
+
const contractOutputs = options?.contractOutputs ??
|
|
26
|
+
(newLockingScript
|
|
27
|
+
? [{ script: newLockingScript, satoshis: newSatoshis ?? currentUtxo.satoshis }]
|
|
28
|
+
: []);
|
|
29
|
+
const contractOutputSats = contractOutputs.reduce((sum, o) => sum + o.satoshis, 0)
|
|
30
|
+
+ dataOutputs.reduce((sum, o) => sum + o.satoshis, 0);
|
|
31
|
+
// Estimate fee using actual script sizes
|
|
32
|
+
const input0Size = 32 + 4 + varIntByteSize(unlockingScript.length / 2) +
|
|
33
|
+
unlockingScript.length / 2 + 4;
|
|
34
|
+
let extraContractInputsSize = 0;
|
|
35
|
+
for (const ci of extraContractInputs) {
|
|
36
|
+
extraContractInputsSize += 32 + 4 +
|
|
37
|
+
varIntByteSize(ci.unlockingScript.length / 2) +
|
|
38
|
+
ci.unlockingScript.length / 2 + 4;
|
|
39
|
+
}
|
|
40
|
+
const p2pkhInputsSize = (additionalUtxos?.length ?? 0) * 148;
|
|
41
|
+
const inputsSize = input0Size + extraContractInputsSize + p2pkhInputsSize;
|
|
42
|
+
let outputsSize = 0;
|
|
43
|
+
for (const co of contractOutputs) {
|
|
44
|
+
outputsSize += 8 + varIntByteSize(co.script.length / 2) + co.script.length / 2;
|
|
45
|
+
}
|
|
46
|
+
for (const do_ of dataOutputs) {
|
|
47
|
+
outputsSize += 8 + varIntByteSize(do_.script.length / 2) + do_.script.length / 2;
|
|
48
|
+
}
|
|
49
|
+
if (changeAddress || changeScript) {
|
|
50
|
+
outputsSize += 34; // P2PKH change
|
|
51
|
+
}
|
|
52
|
+
const estimatedSize = 10 + inputsSize + outputsSize;
|
|
53
|
+
const fee = Math.ceil(estimatedSize * feeRate / 1000);
|
|
54
|
+
const change = totalInput - contractOutputSats - fee;
|
|
55
|
+
// Fail closed on a genuinely underfunded call (finding C3): if the inputs
|
|
56
|
+
// cannot even cover the (non-change) contract + data outputs, the tx spends
|
|
57
|
+
// more than it takes in — it can never confirm and a covenant would strand
|
|
58
|
+
// the funds. Do NOT throw merely because `change < 0`: an exact-cover
|
|
59
|
+
// continuation (issue #116) keeps the full input value in the continuation
|
|
60
|
+
// output and adds no funding, so `change === -fee` (negative) even though
|
|
61
|
+
// `totalInput === contractOutputSats` and the resulting zero-fee tx is valid
|
|
62
|
+
// (the covenant accepts a no-change spend). Guard on the only value-invalid
|
|
63
|
+
// case — `totalInput < contractOutputSats` (contractOutputSats already
|
|
64
|
+
// includes data outputs) — and keep the existing clamp (change output
|
|
65
|
+
// omitted when `change <= 0`). Mirrors Java's fail-closed selection loop.
|
|
66
|
+
if (totalInput < contractOutputSats) {
|
|
67
|
+
throw new Error(`buildCallTransaction: insufficient funds. Need ${contractOutputSats + fee} sats, have ${totalInput}`);
|
|
68
|
+
}
|
|
69
|
+
// Build Transaction object
|
|
70
|
+
const tx = new Transaction();
|
|
71
|
+
// Locktime: default 0 (legacy); overridable via options.locktime for
|
|
72
|
+
// contracts asserting `extractLocktime(preimage) >= deadline`.
|
|
73
|
+
tx.lockTime = options?.locktime ?? 0;
|
|
74
|
+
// Sequence (issue #131): an all-0xffffffff input set makes nLockTime a
|
|
75
|
+
// consensus no-op. When a non-zero locktime is set, default every input to
|
|
76
|
+
// 0xfffffffe (non-final) so the locktime is actually enforced. Explicit
|
|
77
|
+
// options.sequence always wins.
|
|
78
|
+
const inputSequence = resolveInputSequence(options?.locktime, options?.sequence);
|
|
79
|
+
// Input 0: primary contract UTXO with unlocking script
|
|
80
|
+
tx.addInput({
|
|
81
|
+
sourceTXID: currentUtxo.txid,
|
|
82
|
+
sourceOutputIndex: currentUtxo.outputIndex,
|
|
83
|
+
unlockingScript: UnlockingScript.fromHex(unlockingScript),
|
|
84
|
+
sequence: inputSequence,
|
|
85
|
+
});
|
|
86
|
+
// Additional contract inputs (with their own unlocking scripts)
|
|
87
|
+
for (const ci of extraContractInputs) {
|
|
88
|
+
tx.addInput({
|
|
89
|
+
sourceTXID: ci.utxo.txid,
|
|
90
|
+
sourceOutputIndex: ci.utxo.outputIndex,
|
|
91
|
+
unlockingScript: UnlockingScript.fromHex(ci.unlockingScript),
|
|
92
|
+
sequence: inputSequence,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
// P2PKH funding inputs (unsigned)
|
|
96
|
+
if (additionalUtxos) {
|
|
97
|
+
for (const utxo of additionalUtxos) {
|
|
98
|
+
tx.addInput({
|
|
99
|
+
sourceTXID: utxo.txid,
|
|
100
|
+
sourceOutputIndex: utxo.outputIndex,
|
|
101
|
+
unlockingScript: new UnlockingScript(),
|
|
102
|
+
sequence: inputSequence,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Contract outputs
|
|
107
|
+
for (const co of contractOutputs) {
|
|
108
|
+
tx.addOutput({
|
|
109
|
+
satoshis: co.satoshis,
|
|
110
|
+
lockingScript: LockingScript.fromHex(co.script),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// Data outputs (from this.addDataOutput in method body). Emitted after
|
|
114
|
+
// state outputs and before change to match the continuation hash.
|
|
115
|
+
for (const do_ of dataOutputs) {
|
|
116
|
+
tx.addOutput({
|
|
117
|
+
satoshis: do_.satoshis,
|
|
118
|
+
lockingScript: LockingScript.fromHex(do_.script),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
// Change output
|
|
122
|
+
if (change > 0 && (changeAddress || changeScript)) {
|
|
123
|
+
const actualChangeScript = changeScript || buildP2PKHScript(changeAddress);
|
|
124
|
+
tx.addOutput({
|
|
125
|
+
satoshis: change,
|
|
126
|
+
lockingScript: LockingScript.fromHex(actualChangeScript),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return { tx, inputCount: allUtxos.length, changeAmount: change > 0 ? change : 0 };
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Resolve the nSequence for a call tx's inputs (issue #131).
|
|
133
|
+
*
|
|
134
|
+
* An all-0xffffffff input set makes nLockTime a consensus no-op, so a
|
|
135
|
+
* locktime-gated method would be script-enforced (via extractLocktime) yet
|
|
136
|
+
* NOT consensus-enforced. When a non-zero locktime is set we therefore default
|
|
137
|
+
* every input to 0xfffffffe (non-final, enforceable). Explicit `sequence`
|
|
138
|
+
* always wins; with no/zero locktime we keep the legacy 0xffffffff.
|
|
139
|
+
*
|
|
140
|
+
* Shared by `buildCallTransaction` and the terminal-path tx builder so both
|
|
141
|
+
* sites stay byte-consistent.
|
|
142
|
+
*/
|
|
143
|
+
export function resolveInputSequence(locktime, sequence) {
|
|
144
|
+
if (sequence !== undefined)
|
|
145
|
+
return sequence;
|
|
146
|
+
if (locktime !== undefined && locktime !== 0)
|
|
147
|
+
return 0xfffffffe;
|
|
148
|
+
return 0xffffffff;
|
|
149
|
+
}
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// Fee estimation
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
const P2PKH_INPUT_SIZE = 148;
|
|
154
|
+
const P2PKH_OUTPUT_SIZE = 34;
|
|
155
|
+
const TX_OVERHEAD = 10;
|
|
156
|
+
/**
|
|
157
|
+
* Estimate the fee for a method call transaction.
|
|
158
|
+
*
|
|
159
|
+
* @param lockingScriptByteLen - Byte length of the (single) contract
|
|
160
|
+
* continuation output's locking script.
|
|
161
|
+
* @param unlockingScriptByteLen - Byte length of the primary contract input's
|
|
162
|
+
* unlocking script.
|
|
163
|
+
* @param numFundingInputs - Number of P2PKH funding inputs (~148 bytes
|
|
164
|
+
* each), NOT continuation outputs. Use `extraOutputBytes` to size
|
|
165
|
+
* additional continuation/data outputs.
|
|
166
|
+
* @param feeRate - Sat/KB (default 100).
|
|
167
|
+
* @param extraOutputBytes - Serialized byte size of any outputs BEYOND
|
|
168
|
+
* the single `lockingScriptByteLen` one already counted (additional
|
|
169
|
+
* continuation outputs, data outputs) — mirrors `estimateDeployFee`'s
|
|
170
|
+
* `extraOutputBytes` in deployment.ts (finding C15). Defaults to 0.
|
|
171
|
+
*/
|
|
172
|
+
export function estimateCallFee(lockingScriptByteLen, unlockingScriptByteLen, numFundingInputs, feeRate = 100, extraOutputBytes = 0) {
|
|
173
|
+
const contractInputSize = 32 + 4 + varIntByteSize(unlockingScriptByteLen) + unlockingScriptByteLen + 4;
|
|
174
|
+
const fundingInputsSize = numFundingInputs * P2PKH_INPUT_SIZE;
|
|
175
|
+
const contractOutputSize = 8 + varIntByteSize(lockingScriptByteLen) + lockingScriptByteLen;
|
|
176
|
+
const changeOutputSize = P2PKH_OUTPUT_SIZE;
|
|
177
|
+
const txSize = TX_OVERHEAD + contractInputSize + fundingInputsSize + contractOutputSize + extraOutputBytes + changeOutputSize;
|
|
178
|
+
return Math.ceil(txSize * feeRate / 1000);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Estimate the fee for a single call against a deployed contract built
|
|
182
|
+
* from the given artifact. Wraps {@link estimateCallFee} with defaults
|
|
183
|
+
* derived from the artifact and accepts `feeRate` in sat/byte (vs the
|
|
184
|
+
* sat/kilobyte unit `estimateCallFee` itself takes).
|
|
185
|
+
*/
|
|
186
|
+
export function estimateFeeForArtifact(artifact, opts = {}) {
|
|
187
|
+
const satPerByte = opts.feeRate ?? 0.1;
|
|
188
|
+
const outputs = opts.outputCount ?? 1;
|
|
189
|
+
const lockingLen = artifact.script.length / 2;
|
|
190
|
+
const unlockingLen = opts.unlockingScriptLen ?? Math.ceil(artifact.script.length / 4);
|
|
191
|
+
// estimateCallFee already prices ONE continuation output of lockingLen
|
|
192
|
+
// bytes; extraOutputBytes covers any additional outputs beyond that one
|
|
193
|
+
// (finding C4 — outputCount used to be smuggled into numFundingInputs).
|
|
194
|
+
const perOutputSize = 8 + varIntByteSize(lockingLen) + lockingLen;
|
|
195
|
+
const extraOutputBytes = Math.max(0, outputs - 1) * perOutputSize;
|
|
196
|
+
return estimateCallFee(lockingLen, unlockingLen, 0, satPerByte * 1000, extraOutputBytes);
|
|
197
|
+
}
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
// Helpers
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
function varIntByteSize(n) {
|
|
202
|
+
if (n < 0xfd)
|
|
203
|
+
return 1;
|
|
204
|
+
if (n <= 0xffff)
|
|
205
|
+
return 3;
|
|
206
|
+
if (n <= 0xffffffff)
|
|
207
|
+
return 5;
|
|
208
|
+
return 9;
|
|
209
|
+
}
|