diff --git a/packages/wasm-utxo/js/ast/formatNode.ts b/packages/wasm-utxo/js/ast/formatNode.ts index 9c6fa8dde46..a04617c44f2 100644 --- a/packages/wasm-utxo/js/ast/formatNode.ts +++ b/packages/wasm-utxo/js/ast/formatNode.ts @@ -46,7 +46,8 @@ type Miniscript = | Wrap<{ hash256: string }> | Wrap<{ hash160: string }> | Wrap<{ older: number }> - | Wrap<{ after: number }>; + | Wrap<{ after: number }> + | Wrap<{ payload_drop: string }>; type TapTree = [TapTree, TapTree] | Miniscript; diff --git a/packages/wasm-utxo/js/ast/fromWasmNode.ts b/packages/wasm-utxo/js/ast/fromWasmNode.ts index 8040a50d884..8f19d0933ad 100644 --- a/packages/wasm-utxo/js/ast/fromWasmNode.ts +++ b/packages/wasm-utxo/js/ast/fromWasmNode.ts @@ -114,6 +114,8 @@ function fromUnknown(v: unknown): Node | Node[] { return node("multi", value); case "MultiA": return node("multi_a", value); + case "PayloadDrop": + return node("payload_drop", value); case "Tree": if (!Array.isArray(value) || value.length !== 2) { diff --git a/packages/wasm-utxo/test/sbtc.ts b/packages/wasm-utxo/test/sbtc.ts index 988dd71aaa5..54dc84f5062 100644 --- a/packages/wasm-utxo/test/sbtc.ts +++ b/packages/wasm-utxo/test/sbtc.ts @@ -1,6 +1,7 @@ import * as assert from "assert"; import * as crypto from "crypto"; import { Descriptor } from "../js/index.js"; +import { fromDescriptor, formatNode } from "../js/ast/index.js"; import { getDefaultXPubs, getUnspendableKey } from "../js/testutils/descriptor/descriptors.js"; // sBTC protocol uses two taproot script leaves: @@ -245,4 +246,29 @@ describe("sBTC taproot descriptor", function () { assert.strictEqual(reclaimLeaf.AndV[0].Drop.Older.relLockTime, 1); }); }); + + describe("fromDescriptor (wasm → JS AST)", function () { + it("does not throw on a descriptor containing payload_drop", () => { + assert.doesNotThrow(() => fromDescriptor(descriptor)); + }); + + it("emits a payload_drop node carrying the original hex payload", () => { + const ast = fromDescriptor(descriptor); + const formatted = formatNode(ast); + assert.ok( + formatted.includes( + "payload_drop(0000000000013880051ad206838b7981a116c334e8cb1b950afb73eb54a5)", + ), + `expected payload_drop() in formatted AST, got: ${formatted}`, + ); + }); + + it("round-trips the reclaim leaf via formatNode", () => { + const ast = fromDescriptor(descriptor) as unknown as { + tr: [string, [unknown, Parameters[0]]]; + }; + const reclaimAst = ast.tr[1][1]; + assert.strictEqual(formatNode(reclaimAst), RECLAIM_LEAF); + }); + }); });