@satora/escrow 0.0.1-alpha.0

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 (41) hide show
  1. package/dist/2-of-2/ark-contract.d.ts +29 -0
  2. package/dist/2-of-2/ark-contract.d.ts.map +1 -0
  3. package/dist/2-of-2/ark-contract.js +49 -0
  4. package/dist/2-of-2/ark-contract.js.map +1 -0
  5. package/dist/2-of-2/contract-handler.d.ts +26 -0
  6. package/dist/2-of-2/contract-handler.d.ts.map +1 -0
  7. package/dist/2-of-2/contract-handler.js +140 -0
  8. package/dist/2-of-2/contract-handler.js.map +1 -0
  9. package/dist/2-of-2/escrow-script.d.ts +57 -0
  10. package/dist/2-of-2/escrow-script.d.ts.map +1 -0
  11. package/dist/2-of-2/escrow-script.js +61 -0
  12. package/dist/2-of-2/escrow-script.js.map +1 -0
  13. package/dist/2-of-2/index.d.ts +7 -0
  14. package/dist/2-of-2/index.d.ts.map +1 -0
  15. package/dist/2-of-2/index.js +7 -0
  16. package/dist/2-of-2/index.js.map +1 -0
  17. package/dist/2-of-2/monitor.d.ts +67 -0
  18. package/dist/2-of-2/monitor.d.ts.map +1 -0
  19. package/dist/2-of-2/monitor.js +101 -0
  20. package/dist/2-of-2/monitor.js.map +1 -0
  21. package/dist/2-of-2/release.d.ts +80 -0
  22. package/dist/2-of-2/release.d.ts.map +1 -0
  23. package/dist/2-of-2/release.js +133 -0
  24. package/dist/2-of-2/release.js.map +1 -0
  25. package/dist/2-of-2/verify.d.ts +40 -0
  26. package/dist/2-of-2/verify.d.ts.map +1 -0
  27. package/dist/2-of-2/verify.js +100 -0
  28. package/dist/2-of-2/verify.js.map +1 -0
  29. package/dist/index.d.ts +4 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +4 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/payout-commitment.d.ts +12 -0
  34. package/dist/payout-commitment.d.ts.map +1 -0
  35. package/dist/payout-commitment.js +14 -0
  36. package/dist/payout-commitment.js.map +1 -0
  37. package/dist/sign.d.ts +19 -0
  38. package/dist/sign.d.ts.map +1 -0
  39. package/dist/sign.js +26 -0
  40. package/dist/sign.js.map +1 -0
  41. package/package.json +46 -0
@@ -0,0 +1,29 @@
1
+ import { type Contract, type ContractState, type CreateContractParams, type Network } from "@arkade-os/sdk";
2
+ import { type EscrowScriptOptions } from "./escrow-script.js";
3
+ /** Optional descriptive fields carried alongside a registered contract. */
4
+ export interface EscrowContractMeta {
5
+ label?: string;
6
+ state?: ContractState;
7
+ metadata?: Record<string, unknown>;
8
+ }
9
+ /**
10
+ * Build {@link CreateContractParams} for a 2-of-2 escrow: derives the pkScript
11
+ * and funding address from the escrow parameters, ready to hand to
12
+ * `ContractManager.createContract`.
13
+ */
14
+ export declare function escrowCreateContractParams(options: EscrowScriptOptions, network: Network, meta?: EscrowContractMeta): CreateContractParams;
15
+ /**
16
+ * Encode a 2-of-2 escrow as a NArk-compatible `arkcontract=` string for the
17
+ * server→client handoff. Carries the escrow parameters only — the receiver
18
+ * re-derives the script and address via {@link decodeEscrowArkContract}.
19
+ */
20
+ export declare function encodeEscrowArkContract(options: EscrowScriptOptions): string;
21
+ /**
22
+ * Decode an escrow `arkcontract=` string into a full {@link Contract},
23
+ * re-deriving the pkScript and funding address from the embedded parameters.
24
+ *
25
+ * Registers the escrow handler if needed, so the caller does not have to.
26
+ * `aspPubKey` is the ASP x-only key the funding address is built from.
27
+ */
28
+ export declare function decodeEscrowArkContract(encoded: string, aspPubKey: Uint8Array, network: Network, meta?: EscrowContractMeta): Contract;
29
+ //# sourceMappingURL=ark-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ark-contract.d.ts","sourceRoot":"","sources":["../../src/2-of-2/ark-contract.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,oBAAoB,EAGzB,KAAK,OAAO,EACb,MAAM,gBAAgB,CAAC;AAOxB,OAAO,EAAE,KAAK,mBAAmB,EAAoB,MAAM,oBAAoB,CAAC;AAEhF,2EAA2E;AAC3E,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,mBAAmB,EAC5B,OAAO,EAAE,OAAO,EAChB,IAAI,GAAE,kBAAuB,GAC5B,oBAAoB,CAWtB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAM5E;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,UAAU,EACrB,OAAO,EAAE,OAAO,EAChB,IAAI,GAAE,kBAAuB,GAC5B,QAAQ,CAOV"}
@@ -0,0 +1,49 @@
1
+ import { contractFromArkContractWithAddress, encodeArkContract, } from "@arkade-os/sdk";
2
+ import { hex } from "@scure/base";
3
+ import { ESCROW_2OF2_CONTRACT_TYPE, EscrowContractHandler, registerEscrowContractHandler, } from "./contract-handler.js";
4
+ import { EscrowVtxoScript } from "./escrow-script.js";
5
+ /**
6
+ * Build {@link CreateContractParams} for a 2-of-2 escrow: derives the pkScript
7
+ * and funding address from the escrow parameters, ready to hand to
8
+ * `ContractManager.createContract`.
9
+ */
10
+ export function escrowCreateContractParams(options, network, meta = {}) {
11
+ const script = new EscrowVtxoScript(options);
12
+ return {
13
+ label: meta.label,
14
+ type: ESCROW_2OF2_CONTRACT_TYPE,
15
+ params: EscrowContractHandler.serializeParams(options),
16
+ script: hex.encode(script.pkScript),
17
+ address: script.arkAddress(network),
18
+ state: meta.state,
19
+ metadata: meta.metadata,
20
+ };
21
+ }
22
+ /**
23
+ * Encode a 2-of-2 escrow as a NArk-compatible `arkcontract=` string for the
24
+ * server→client handoff. Carries the escrow parameters only — the receiver
25
+ * re-derives the script and address via {@link decodeEscrowArkContract}.
26
+ */
27
+ export function encodeEscrowArkContract(options) {
28
+ // `encodeArkContract` reads only `type` and `params`.
29
+ return encodeArkContract({
30
+ type: ESCROW_2OF2_CONTRACT_TYPE,
31
+ params: EscrowContractHandler.serializeParams(options),
32
+ });
33
+ }
34
+ /**
35
+ * Decode an escrow `arkcontract=` string into a full {@link Contract},
36
+ * re-deriving the pkScript and funding address from the embedded parameters.
37
+ *
38
+ * Registers the escrow handler if needed, so the caller does not have to.
39
+ * `aspPubKey` is the ASP x-only key the funding address is built from.
40
+ */
41
+ export function decodeEscrowArkContract(encoded, aspPubKey, network, meta = {}) {
42
+ registerEscrowContractHandler();
43
+ return contractFromArkContractWithAddress(encoded, aspPubKey, network.hrp, {
44
+ label: meta.label,
45
+ state: meta.state,
46
+ metadata: meta.metadata,
47
+ });
48
+ }
49
+ //# sourceMappingURL=ark-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ark-contract.js","sourceRoot":"","sources":["../../src/2-of-2/ark-contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,kCAAkC,EAClC,iBAAiB,GAElB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,6BAA6B,GAC9B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAA4B,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAShF;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAA4B,EAC5B,OAAgB,EAChB,OAA2B,EAAE;IAE7B,MAAM,MAAM,GAAG,IAAI,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC7C,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,yBAAyB;QAC/B,MAAM,EAAE,qBAAqB,CAAC,eAAe,CAAC,OAAO,CAAC;QACtD,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;QACnC,OAAO,EAAE,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC;QACnC,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACxB,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAA4B;IAClE,sDAAsD;IACtD,OAAO,iBAAiB,CAAC;QACvB,IAAI,EAAE,yBAAyB;QAC/B,MAAM,EAAE,qBAAqB,CAAC,eAAe,CAAC,OAAO,CAAC;KAC3C,CAAC,CAAC;AACjB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CACrC,OAAe,EACf,SAAqB,EACrB,OAAgB,EAChB,OAA2B,EAAE;IAE7B,6BAA6B,EAAE,CAAC;IAChC,OAAO,kCAAkC,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,GAAG,EAAE;QACzE,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACxB,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { type ContractHandler } from "@arkade-os/sdk";
2
+ import { type EscrowScriptOptions, EscrowVtxoScript } from "./escrow-script.js";
3
+ /** Contract type identifier for the cooperative 2-of-2 escrow. */
4
+ export declare const ESCROW_2OF2_CONTRACT_TYPE = "escrow-2of2";
5
+ /** Role a wallet can play in a 2-of-2 escrow contract. */
6
+ export type EscrowRole = "seller" | "arbiter";
7
+ /**
8
+ * ContractHandler for the cooperative 2-of-2 escrow ({@link EscrowVtxoScript}).
9
+ *
10
+ * Spending paths:
11
+ * - cooperative (leaf A): 3-of-3 [seller, arbiter, asp]. Available whenever
12
+ * the server cooperates. Returned for ANY role — the witness is completed
13
+ * by the multi-party signing choreography (see `signEscrowArkTx`), not by a
14
+ * single wallet. This mirrors how the SDK's VHTLC handler returns its
15
+ * multi-party `refund`/`claim` leaves.
16
+ * - escape (leaf B): arbiter-only, after the CSV. The seller has no
17
+ * unilateral path by design, so `selectPath` returns null for the seller
18
+ * once collaboration is unavailable.
19
+ */
20
+ export declare const EscrowContractHandler: ContractHandler<EscrowScriptOptions, EscrowVtxoScript>;
21
+ /**
22
+ * Register {@link EscrowContractHandler} in the SDK's global handler registry.
23
+ * Idempotent: a no-op if a handler for the type is already registered.
24
+ */
25
+ export declare function registerEscrowContractHandler(): void;
26
+ //# sourceMappingURL=contract-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract-handler.d.ts","sourceRoot":"","sources":["../../src/2-of-2/contract-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EAMrB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,KAAK,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEhF,kEAAkE;AAClE,eAAO,MAAM,yBAAyB,gBAAgB,CAAC;AAEvD,0DAA0D;AAC1D,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;AAiE9C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,EAAE,eAAe,CACjD,mBAAmB,EACnB,gBAAgB,CA8DjB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,6BAA6B,IAAI,IAAI,CAIpD"}
@@ -0,0 +1,140 @@
1
+ import { contractHandlers, sequenceToTimelock, timelockToSequence, } from "@arkade-os/sdk";
2
+ import { hex } from "@scure/base";
3
+ import { EscrowVtxoScript } from "./escrow-script.js";
4
+ /** Contract type identifier for the cooperative 2-of-2 escrow. */
5
+ export const ESCROW_2OF2_CONTRACT_TYPE = "escrow-2of2";
6
+ /**
7
+ * Resolve the wallet's role by matching its x-only pubkey (hex) against the
8
+ * contract's seller/arbiter params. An explicit `context.role` wins.
9
+ *
10
+ * The buyer holds no key in the script, so it is never a role here.
11
+ */
12
+ function resolveEscrowRole(contract, context) {
13
+ if (context.role === "seller" || context.role === "arbiter") {
14
+ return context.role;
15
+ }
16
+ const walletKey = context.walletPubKey?.toLowerCase();
17
+ if (!walletKey)
18
+ return undefined;
19
+ if (walletKey === contract.params.sellerPubKey?.toLowerCase()) {
20
+ return "seller";
21
+ }
22
+ if (walletKey === contract.params.arbiterPubKey?.toLowerCase()) {
23
+ return "arbiter";
24
+ }
25
+ return undefined;
26
+ }
27
+ /**
28
+ * CSV maturity check for the escape leaf: is the relative (BIP-68) timelock
29
+ * satisfied for the vtxo under evaluation?
30
+ *
31
+ * TODO: drop this local copy and import the SDK's `isCsvSpendable` once
32
+ * https://github.com/arkade-os/ts-sdk/pull/541 lands and ships in a release —
33
+ * that PR exports this exact helper. Until then we mirror it here.
34
+ */
35
+ function isCsvSpendable(context, sequence) {
36
+ if (sequence === undefined)
37
+ return true;
38
+ if (!context.vtxo)
39
+ return false;
40
+ const timelock = sequenceToTimelock(sequence);
41
+ if (timelock.type === "blocks") {
42
+ if (context.blockHeight === undefined ||
43
+ context.vtxo.status.block_height === undefined) {
44
+ return false;
45
+ }
46
+ return (context.blockHeight - context.vtxo.status.block_height >=
47
+ Number(timelock.value));
48
+ }
49
+ const blockTime = context.vtxo.status.block_time;
50
+ if (blockTime === undefined)
51
+ return false;
52
+ return context.currentTime / 1000 - blockTime >= Number(timelock.value);
53
+ }
54
+ /** nSequence (BIP-68) for the escape leaf, as stored in the contract params. */
55
+ function escapeSequence(contract) {
56
+ return contract.params.exitTimelock
57
+ ? Number(contract.params.exitTimelock)
58
+ : undefined;
59
+ }
60
+ /**
61
+ * ContractHandler for the cooperative 2-of-2 escrow ({@link EscrowVtxoScript}).
62
+ *
63
+ * Spending paths:
64
+ * - cooperative (leaf A): 3-of-3 [seller, arbiter, asp]. Available whenever
65
+ * the server cooperates. Returned for ANY role — the witness is completed
66
+ * by the multi-party signing choreography (see `signEscrowArkTx`), not by a
67
+ * single wallet. This mirrors how the SDK's VHTLC handler returns its
68
+ * multi-party `refund`/`claim` leaves.
69
+ * - escape (leaf B): arbiter-only, after the CSV. The seller has no
70
+ * unilateral path by design, so `selectPath` returns null for the seller
71
+ * once collaboration is unavailable.
72
+ */
73
+ export const EscrowContractHandler = {
74
+ type: ESCROW_2OF2_CONTRACT_TYPE,
75
+ createScript(params) {
76
+ return new EscrowVtxoScript(this.deserializeParams(params));
77
+ },
78
+ serializeParams(params) {
79
+ return {
80
+ sellerPubKey: hex.encode(params.sellerPubKey),
81
+ arbiterPubKey: hex.encode(params.arbiterPubKey),
82
+ aspPubKey: hex.encode(params.aspPubKey),
83
+ exitTimelock: timelockToSequence(params.exitTimelock).toString(),
84
+ };
85
+ },
86
+ deserializeParams(params) {
87
+ return {
88
+ sellerPubKey: hex.decode(params.sellerPubKey),
89
+ arbiterPubKey: hex.decode(params.arbiterPubKey),
90
+ aspPubKey: hex.decode(params.aspPubKey),
91
+ exitTimelock: sequenceToTimelock(Number(params.exitTimelock)),
92
+ };
93
+ },
94
+ selectPath(script, contract, context) {
95
+ if (context.collaborative) {
96
+ return { leaf: script.cooperativeLeaf() };
97
+ }
98
+ // Unilateral: only the arbiter, only after the CSV elapses.
99
+ if (resolveEscrowRole(contract, context) !== "arbiter")
100
+ return null;
101
+ const sequence = escapeSequence(contract);
102
+ if (!isCsvSpendable(context, sequence))
103
+ return null;
104
+ return { leaf: script.escapeLeaf(), sequence };
105
+ },
106
+ getAllSpendingPaths(script, contract, context) {
107
+ const paths = [{ leaf: script.cooperativeLeaf() }];
108
+ // The escape leaf only exists for the arbiter (CSV checked at tx time).
109
+ if (resolveEscrowRole(contract, context) === "arbiter") {
110
+ paths.push({
111
+ leaf: script.escapeLeaf(),
112
+ sequence: escapeSequence(contract),
113
+ });
114
+ }
115
+ return paths;
116
+ },
117
+ getSpendablePaths(script, contract, context) {
118
+ const paths = [];
119
+ if (context.collaborative) {
120
+ paths.push({ leaf: script.cooperativeLeaf() });
121
+ }
122
+ if (resolveEscrowRole(contract, context) === "arbiter") {
123
+ const sequence = escapeSequence(contract);
124
+ if (isCsvSpendable(context, sequence)) {
125
+ paths.push({ leaf: script.escapeLeaf(), sequence });
126
+ }
127
+ }
128
+ return paths;
129
+ },
130
+ };
131
+ /**
132
+ * Register {@link EscrowContractHandler} in the SDK's global handler registry.
133
+ * Idempotent: a no-op if a handler for the type is already registered.
134
+ */
135
+ export function registerEscrowContractHandler() {
136
+ if (!contractHandlers.has(ESCROW_2OF2_CONTRACT_TYPE)) {
137
+ contractHandlers.register(EscrowContractHandler);
138
+ }
139
+ }
140
+ //# sourceMappingURL=contract-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract-handler.js","sourceRoot":"","sources":["../../src/2-of-2/contract-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import { type Network, type RelativeTimelock, VtxoScript } from "@arkade-os/sdk";
2
+ export interface EscrowScriptOptions {
3
+ /** x-only Schnorr pubkey, 32 bytes. The funding party (seller). */
4
+ sellerPubKey: Uint8Array;
5
+ /** x-only Schnorr pubkey, 32 bytes. The escrow arbiter / cosigner. */
6
+ arbiterPubKey: Uint8Array;
7
+ /** x-only Schnorr pubkey, 32 bytes. Arkade ASP's signer key. */
8
+ aspPubKey: Uint8Array;
9
+ /**
10
+ * CSV timelock for the arbiter-only unilateral escape leaf — the
11
+ * ASP-mandated unilateral-exit closure. Must be ≥ the ASP's
12
+ * `unilateralExitDelay` (~2 days on mutinynet, ~30 days on
13
+ * mainnet) or `submitTx` rejects the script with
14
+ * INVALID_VTXO_SCRIPT "exit delay is too short".
15
+ */
16
+ exitTimelock: RelativeTimelock;
17
+ }
18
+ /**
19
+ * Two-leaf VtxoScript for a cooperative 2-of-2 escrow on Ark.
20
+ *
21
+ * A — cooperative release : 3-of-3 [seller, arbiter, asp] (no CSV)
22
+ * B — arbiter unilateral exit : [arbiter] alone after a long CSV
23
+ *
24
+ * The two policy parties are the seller and the arbiter; both must sign
25
+ * the cooperative release. The Arkade ASP is added to leaf A as required
26
+ * by Ark's round/forfeit semantics — the ASP must cosign every
27
+ * cooperative VTXO spend — so at the script level leaf A is 3-of-3 even
28
+ * though only two parties hold policy.
29
+ *
30
+ * The seller has NO unilateral exit. Seller safety relies on a
31
+ * pre-signed cooperative refund ark-tx (created at funding time, held by
32
+ * the arbiter). After the CSV elapses, only the arbiter can sweep.
33
+ */
34
+ export declare class EscrowVtxoScript extends VtxoScript {
35
+ readonly options: EscrowScriptOptions;
36
+ constructor(options: EscrowScriptOptions);
37
+ /** The cooperative-release tapleaf (3-of-3). Index 0. */
38
+ cooperativeLeaf(): import("@arkade-os/sdk").TapLeafScript;
39
+ /** The arbiter-only unilateral exit tapleaf (long CSV). Index 1. */
40
+ escapeLeaf(): import("@arkade-os/sdk").TapLeafScript;
41
+ /**
42
+ * SDK-conformant alias for {@link cooperativeLeaf}.
43
+ *
44
+ * The Arkade wallet/contract machinery (`deriveContractTapscripts`) expects
45
+ * every contract VtxoScript to expose `forfeit()` as its collaborative
46
+ * forfeit/intent leaf. For this escrow that is the cooperative 3-of-3 leaf.
47
+ */
48
+ forfeit(): import("@arkade-os/sdk").TapLeafScript;
49
+ /**
50
+ * SDK-conformant alias for {@link escapeLeaf}, matching the `DefaultVtxo`
51
+ * `exit()` convention for the unilateral (CSV) leaf.
52
+ */
53
+ exit(): import("@arkade-os/sdk").TapLeafScript;
54
+ /** Encoded Ark address (bech32m) for funding this escrow. */
55
+ arkAddress(network: Network): string;
56
+ }
57
+ //# sourceMappingURL=escrow-script.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escrow-script.d.ts","sourceRoot":"","sources":["../../src/2-of-2/escrow-script.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,OAAO,EACZ,KAAK,gBAAgB,EACrB,UAAU,EACX,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,YAAY,EAAE,UAAU,CAAC;IACzB,sEAAsE;IACtE,aAAa,EAAE,UAAU,CAAC;IAC1B,gEAAgE;IAChE,SAAS,EAAE,UAAU,CAAC;IACtB;;;;;;OAMG;IACH,YAAY,EAAE,gBAAgB,CAAC;CAChC;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,gBAAiB,SAAQ,UAAU;IAC9C,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;gBAE1B,OAAO,EAAE,mBAAmB;IAexC,yDAAyD;IACzD,eAAe;IAIf,oEAAoE;IACpE,UAAU;IAIV;;;;;;OAMG;IACH,OAAO;IAIP;;;OAGG;IACH,IAAI;IAIJ,6DAA6D;IAC7D,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM;CAGrC"}
@@ -0,0 +1,61 @@
1
+ import { CSVMultisigTapscript, MultisigTapscript, VtxoScript, } from "@arkade-os/sdk";
2
+ /**
3
+ * Two-leaf VtxoScript for a cooperative 2-of-2 escrow on Ark.
4
+ *
5
+ * A — cooperative release : 3-of-3 [seller, arbiter, asp] (no CSV)
6
+ * B — arbiter unilateral exit : [arbiter] alone after a long CSV
7
+ *
8
+ * The two policy parties are the seller and the arbiter; both must sign
9
+ * the cooperative release. The Arkade ASP is added to leaf A as required
10
+ * by Ark's round/forfeit semantics — the ASP must cosign every
11
+ * cooperative VTXO spend — so at the script level leaf A is 3-of-3 even
12
+ * though only two parties hold policy.
13
+ *
14
+ * The seller has NO unilateral exit. Seller safety relies on a
15
+ * pre-signed cooperative refund ark-tx (created at funding time, held by
16
+ * the arbiter). After the CSV elapses, only the arbiter can sweep.
17
+ */
18
+ export class EscrowVtxoScript extends VtxoScript {
19
+ options;
20
+ constructor(options) {
21
+ const cooperativeLeaf = MultisigTapscript.encode({
22
+ pubkeys: [options.sellerPubKey, options.arbiterPubKey, options.aspPubKey],
23
+ });
24
+ const escapeLeaf = CSVMultisigTapscript.encode({
25
+ pubkeys: [options.arbiterPubKey],
26
+ timelock: options.exitTimelock,
27
+ });
28
+ super([cooperativeLeaf.script, escapeLeaf.script]);
29
+ this.options = options;
30
+ }
31
+ /** The cooperative-release tapleaf (3-of-3). Index 0. */
32
+ cooperativeLeaf() {
33
+ return this.leaves[0];
34
+ }
35
+ /** The arbiter-only unilateral exit tapleaf (long CSV). Index 1. */
36
+ escapeLeaf() {
37
+ return this.leaves[1];
38
+ }
39
+ /**
40
+ * SDK-conformant alias for {@link cooperativeLeaf}.
41
+ *
42
+ * The Arkade wallet/contract machinery (`deriveContractTapscripts`) expects
43
+ * every contract VtxoScript to expose `forfeit()` as its collaborative
44
+ * forfeit/intent leaf. For this escrow that is the cooperative 3-of-3 leaf.
45
+ */
46
+ forfeit() {
47
+ return this.cooperativeLeaf();
48
+ }
49
+ /**
50
+ * SDK-conformant alias for {@link escapeLeaf}, matching the `DefaultVtxo`
51
+ * `exit()` convention for the unilateral (CSV) leaf.
52
+ */
53
+ exit() {
54
+ return this.escapeLeaf();
55
+ }
56
+ /** Encoded Ark address (bech32m) for funding this escrow. */
57
+ arkAddress(network) {
58
+ return this.address(network.hrp, this.options.aspPubKey).encode();
59
+ }
60
+ }
61
+ //# sourceMappingURL=escrow-script.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escrow-script.js","sourceRoot":"","sources":["../../src/2-of-2/escrow-script.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EAGjB,UAAU,GACX,MAAM,gBAAgB,CAAC;AAmBxB;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,gBAAiB,SAAQ,UAAU;IACrC,OAAO,CAAsB;IAEtC,YAAY,OAA4B;QACtC,MAAM,eAAe,GAAG,iBAAiB,CAAC,MAAM,CAAC;YAC/C,OAAO,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,SAAS,CAAC;SAC1E,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,oBAAoB,CAAC,MAAM,CAAC;YAC7C,OAAO,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC;YAChC,QAAQ,EAAE,OAAO,CAAC,YAAY;SAC/B,CAAC,CAAC;QAEH,KAAK,CAAC,CAAC,eAAe,CAAC,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QAEnD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,yDAAyD;IACzD,eAAe;QACb,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACxB,CAAC;IAED,oEAAoE;IACpE,UAAU;QACR,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACxB,CAAC;IAED;;;;;;OAMG;IACH,OAAO;QACL,OAAO,IAAI,CAAC,eAAe,EAAE,CAAC;IAChC,CAAC;IAED;;;OAGG;IACH,IAAI;QACF,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC;IAC3B,CAAC;IAED,6DAA6D;IAC7D,UAAU,CAAC,OAAgB;QACzB,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC;IACpE,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ export * from "./ark-contract.js";
2
+ export * from "./contract-handler.js";
3
+ export * from "./escrow-script.js";
4
+ export * from "./monitor.js";
5
+ export * from "./release.js";
6
+ export * from "./verify.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/2-of-2/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
@@ -0,0 +1,7 @@
1
+ export * from "./ark-contract.js";
2
+ export * from "./contract-handler.js";
3
+ export * from "./escrow-script.js";
4
+ export * from "./monitor.js";
5
+ export * from "./release.js";
6
+ export * from "./verify.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/2-of-2/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
@@ -0,0 +1,67 @@
1
+ import { type Contract, type ContractManagerConfig, type ContractVtxo, type IContractManager, type Network } from "@arkade-os/sdk";
2
+ import { type EscrowContractMeta } from "./ark-contract.js";
3
+ import type { EscrowScriptOptions } from "./escrow-script.js";
4
+ /** The subset of `IContractManager` the monitor relies on. */
5
+ export type EscrowManagerLike = Pick<IContractManager, "createContract" | "getContractsWithVtxos" | "onContractEvent" | "deleteContract" | "dispose">;
6
+ /** Funding observed at an escrow address. */
7
+ export interface EscrowFundedEvent {
8
+ contract: Contract;
9
+ vtxos: ContractVtxo[];
10
+ /** Sum of the received vtxo values, in sats. */
11
+ totalSats: number;
12
+ }
13
+ /** The escrow VTXO was spent (cooperative release or arbiter escape). */
14
+ export interface EscrowReleasedEvent {
15
+ contract: Contract;
16
+ vtxos: ContractVtxo[];
17
+ }
18
+ /**
19
+ * A thin facade over the Arkade {@link ContractManager} scoped to 2-of-2
20
+ * escrows. It registers the escrow handler, owns the manager lifecycle, and
21
+ * exposes escrow-shaped helpers so a consumer never touches the handler
22
+ * registry, the watcher, or the repositories directly.
23
+ *
24
+ * Watching, persistence, reconnection, and failsafe polling are all handled
25
+ * by the underlying manager.
26
+ */
27
+ export declare class EscrowMonitor {
28
+ private readonly manager;
29
+ private constructor();
30
+ /**
31
+ * Construct a monitor, creating and starting a real {@link ContractManager}
32
+ * from the given providers and repositories.
33
+ */
34
+ static create(config: ContractManagerConfig): Promise<EscrowMonitor>;
35
+ /**
36
+ * Wrap an existing manager (or a test double). Registers the escrow handler.
37
+ */
38
+ static fromManager(manager: EscrowManagerLike): EscrowMonitor;
39
+ /**
40
+ * Register an escrow (built from its parameters) for monitoring. Returns the
41
+ * persisted {@link Contract}; its `address` is where the seller funds.
42
+ */
43
+ watch(options: EscrowScriptOptions, network: Network, meta?: EscrowContractMeta): Promise<Contract>;
44
+ /**
45
+ * Register an escrow received as an `arkcontract=` string (the server→client
46
+ * handoff) for monitoring.
47
+ */
48
+ watchArkContract(encoded: string, aspPubKey: Uint8Array, network: Network, meta?: EscrowContractMeta): Promise<Contract>;
49
+ /** Stop monitoring an escrow by its pkScript hex. */
50
+ unwatch(script: string): Promise<void>;
51
+ /** List all monitored escrows together with their current virtual outputs. */
52
+ listEscrows(): Promise<EscrowFundedEvent[]>;
53
+ /**
54
+ * Fire `cb` when funds land at an escrow address (the FUNDED signal). Only
55
+ * fires for escrow contracts. Returns an unsubscribe function.
56
+ */
57
+ onFunded(cb: (event: EscrowFundedEvent) => void): () => void;
58
+ /**
59
+ * Fire `cb` when an escrow VTXO is spent (the RELEASED signal — cooperative
60
+ * release or arbiter escape). Only fires for escrow contracts. Returns an
61
+ * unsubscribe function.
62
+ */
63
+ onReleased(cb: (event: EscrowReleasedEvent) => void): () => void;
64
+ /** Release the manager's resources (stop watching, clear listeners). */
65
+ dispose(): void;
66
+ }
67
+ //# sourceMappingURL=monitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitor.d.ts","sourceRoot":"","sources":["../../src/2-of-2/monitor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EAEb,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,OAAO,EACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,mBAAmB,CAAC;AAK3B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,8DAA8D;AAC9D,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAClC,gBAAgB,EACd,gBAAgB,GAChB,uBAAuB,GACvB,iBAAiB,GACjB,gBAAgB,GAChB,SAAS,CACZ,CAAC;AAEF,6CAA6C;AAC7C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,yEAAyE;AACzE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAMD;;;;;;;;GAQG;AACH,qBAAa,aAAa;IACJ,OAAO,CAAC,QAAQ,CAAC,OAAO;IAA5C,OAAO;IAEP;;;OAGG;WACU,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,aAAa,CAAC;IAM1E;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,iBAAiB,GAAG,aAAa;IAK7D;;;OAGG;IACH,KAAK,CACH,OAAO,EAAE,mBAAmB,EAC5B,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,QAAQ,CAAC;IAMpB;;;OAGG;IACH,gBAAgB,CACd,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,UAAU,EACrB,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,QAAQ,CAAC;IAUpB,qDAAqD;IACrD,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItC,8EAA8E;IACxE,WAAW,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAWjD;;;OAGG;IACH,QAAQ,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAAG,MAAM,IAAI;IAe5D;;;;OAIG;IACH,UAAU,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,GAAG,MAAM,IAAI;IAWhE,wEAAwE;IACxE,OAAO,IAAI,IAAI;CAGhB"}
@@ -0,0 +1,101 @@
1
+ import { ContractManager, } from "@arkade-os/sdk";
2
+ import { decodeEscrowArkContract, escrowCreateContractParams, } from "./ark-contract.js";
3
+ import { ESCROW_2OF2_CONTRACT_TYPE, registerEscrowContractHandler, } from "./contract-handler.js";
4
+ function totalSats(vtxos) {
5
+ return vtxos.reduce((acc, v) => acc + v.value, 0);
6
+ }
7
+ /**
8
+ * A thin facade over the Arkade {@link ContractManager} scoped to 2-of-2
9
+ * escrows. It registers the escrow handler, owns the manager lifecycle, and
10
+ * exposes escrow-shaped helpers so a consumer never touches the handler
11
+ * registry, the watcher, or the repositories directly.
12
+ *
13
+ * Watching, persistence, reconnection, and failsafe polling are all handled
14
+ * by the underlying manager.
15
+ */
16
+ export class EscrowMonitor {
17
+ manager;
18
+ constructor(manager) {
19
+ this.manager = manager;
20
+ }
21
+ /**
22
+ * Construct a monitor, creating and starting a real {@link ContractManager}
23
+ * from the given providers and repositories.
24
+ */
25
+ static async create(config) {
26
+ registerEscrowContractHandler();
27
+ const manager = await ContractManager.create(config);
28
+ return new EscrowMonitor(manager);
29
+ }
30
+ /**
31
+ * Wrap an existing manager (or a test double). Registers the escrow handler.
32
+ */
33
+ static fromManager(manager) {
34
+ registerEscrowContractHandler();
35
+ return new EscrowMonitor(manager);
36
+ }
37
+ /**
38
+ * Register an escrow (built from its parameters) for monitoring. Returns the
39
+ * persisted {@link Contract}; its `address` is where the seller funds.
40
+ */
41
+ watch(options, network, meta) {
42
+ return this.manager.createContract(escrowCreateContractParams(options, network, meta));
43
+ }
44
+ /**
45
+ * Register an escrow received as an `arkcontract=` string (the server→client
46
+ * handoff) for monitoring.
47
+ */
48
+ watchArkContract(encoded, aspPubKey, network, meta) {
49
+ const { createdAt: _createdAt, ...params } = decodeEscrowArkContract(encoded, aspPubKey, network, meta);
50
+ return this.manager.createContract(params);
51
+ }
52
+ /** Stop monitoring an escrow by its pkScript hex. */
53
+ unwatch(script) {
54
+ return this.manager.deleteContract(script);
55
+ }
56
+ /** List all monitored escrows together with their current virtual outputs. */
57
+ async listEscrows() {
58
+ const all = await this.manager.getContractsWithVtxos({
59
+ type: ESCROW_2OF2_CONTRACT_TYPE,
60
+ });
61
+ return all.map(({ contract, vtxos }) => ({
62
+ contract,
63
+ vtxos,
64
+ totalSats: totalSats(vtxos),
65
+ }));
66
+ }
67
+ /**
68
+ * Fire `cb` when funds land at an escrow address (the FUNDED signal). Only
69
+ * fires for escrow contracts. Returns an unsubscribe function.
70
+ */
71
+ onFunded(cb) {
72
+ return this.manager.onContractEvent((event) => {
73
+ if (event.type === "vtxo_received" &&
74
+ event.contract.type === ESCROW_2OF2_CONTRACT_TYPE) {
75
+ cb({
76
+ contract: event.contract,
77
+ vtxos: event.vtxos,
78
+ totalSats: totalSats(event.vtxos),
79
+ });
80
+ }
81
+ });
82
+ }
83
+ /**
84
+ * Fire `cb` when an escrow VTXO is spent (the RELEASED signal — cooperative
85
+ * release or arbiter escape). Only fires for escrow contracts. Returns an
86
+ * unsubscribe function.
87
+ */
88
+ onReleased(cb) {
89
+ return this.manager.onContractEvent((event) => {
90
+ if (event.type === "vtxo_spent" &&
91
+ event.contract.type === ESCROW_2OF2_CONTRACT_TYPE) {
92
+ cb({ contract: event.contract, vtxos: event.vtxos });
93
+ }
94
+ });
95
+ }
96
+ /** Release the manager's resources (stop watching, clear listeners). */
97
+ dispose() {
98
+ this.manager.dispose();
99
+ }
100
+ }
101
+ //# sourceMappingURL=monitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitor.js","sourceRoot":"","sources":["../../src/2-of-2/monitor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import { type ArkInfo, type ArkProvider, CSVMultisigTapscript, type RelativeTimelock, Transaction } from "@arkade-os/sdk";
2
+ import type { EscrowVtxoScript } from "./escrow-script.js";
3
+ /**
4
+ * ASP-derived parameters needed to build and submit an escrow release.
5
+ *
6
+ * Derive once at startup from `ArkProvider.getInfo()` via
7
+ * {@link escrowArkConfigFromInfo}; these values are fixed by the ASP and
8
+ * cannot be chosen by the escrow parties.
9
+ */
10
+ export interface EscrowArkConfig {
11
+ /** ASP x-only pubkey (32 bytes). */
12
+ aspPubKey: Uint8Array;
13
+ /** ASP-mandated unilateral-exit timelock (the escrow escape-leaf CSV). */
14
+ exitTimelock: RelativeTimelock;
15
+ /** CSV+multisig tapscript used as the second leaf of every checkpoint VTXO. */
16
+ serverUnrollScript: CSVMultisigTapscript.Type;
17
+ /** ASP dust threshold (sats). Sub-dust outputs use an OP_RETURN-shaped script. */
18
+ dust: bigint;
19
+ }
20
+ /**
21
+ * Derive {@link EscrowArkConfig} from the ASP's `getInfo()` response.
22
+ *
23
+ * Mirrors the connect step every party performs: x-only the signer key,
24
+ * decode the checkpoint tapscript, and map the unilateral-exit delay to a
25
+ * relative timelock.
26
+ */
27
+ export declare function escrowArkConfigFromInfo(info: ArkInfo): EscrowArkConfig;
28
+ /** The escrow funding VTXO being spent by the release. */
29
+ export interface EscrowFundingOutpoint {
30
+ txid: string;
31
+ vout: number;
32
+ valueSats: number;
33
+ }
34
+ /** The two release outputs: buyer payout and escrow fee. */
35
+ export interface EscrowReleaseOutputs {
36
+ /** Buyer's payout Ark address (committed to at take time). */
37
+ buyerArkAddress: string;
38
+ /** Sats paid to the buyer. */
39
+ buyerAmountSats: number;
40
+ /** Fee-collection Ark address. */
41
+ feeArkAddress: string;
42
+ /** Sats paid to the fee output. */
43
+ feeSats: number;
44
+ }
45
+ export interface BuiltEscrowRelease {
46
+ arkTx: Transaction;
47
+ checkpoints: Transaction[];
48
+ }
49
+ /**
50
+ * Build the cooperative release ark-tx and its checkpoint(s) spending the
51
+ * escrow funding VTXO to `buyer` and `fee` outputs via the cooperative leaf.
52
+ *
53
+ * Deterministic: identical inputs produce identical PSBT bytes and txids, so
54
+ * the arbiter can rebuild on a later round instead of persisting state.
55
+ */
56
+ export declare function buildEscrowReleaseTx(escrow: EscrowVtxoScript, funding: EscrowFundingOutpoint, outputs: EscrowReleaseOutputs, config: EscrowArkConfig): BuiltEscrowRelease;
57
+ /**
58
+ * Sign input 0 of the ark-tx and every checkpoint with `secretKey`, in place.
59
+ *
60
+ * Defaults to a deterministic auxRand (see above). Used by the arbiter, who
61
+ * signs across rounds; a single-shot signer (the seller) can use
62
+ * {@link signEscrowArkTx} on the base64 PSBTs instead.
63
+ */
64
+ export declare function signEscrowReleaseInPlace(built: BuiltEscrowRelease, secretKey: Uint8Array, auxRand?: Uint8Array): void;
65
+ /**
66
+ * Submit the fully-signed ark-tx with UNSIGNED checkpoints to the ASP, merge
67
+ * the user (seller+arbiter) checkpoint sigs into the ASP-signed responses, and
68
+ * finalize. Returns the arkTxid.
69
+ *
70
+ * The submit/finalize split lets the ASP reject a malformed ark-tx before any
71
+ * checkpoint signature is spent. Crash recovery between submit and finalize is
72
+ * the caller's responsibility.
73
+ *
74
+ * @param provider ASP provider used to submit and finalize the ark-tx
75
+ * @param fullySignedArkTx ark-tx carrying both arbiter and seller tapscript sigs
76
+ * @param userSignedCheckpoints checkpoints with arbiter+seller sigs (kept aside)
77
+ * @param unsignedCheckpoints fresh-from-build checkpoints (no signatures)
78
+ */
79
+ export declare function submitAndFinalizeEscrowRelease(provider: ArkProvider, fullySignedArkTx: Transaction, userSignedCheckpoints: Transaction[], unsignedCheckpoints: Transaction[]): Promise<string>;
80
+ //# sourceMappingURL=release.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release.d.ts","sourceRoot":"","sources":["../../src/2-of-2/release.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,OAAO,EACZ,KAAK,WAAW,EAGhB,oBAAoB,EAEpB,KAAK,gBAAgB,EACrB,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,SAAS,EAAE,UAAU,CAAC;IACtB,0EAA0E;IAC1E,YAAY,EAAE,gBAAgB,CAAC;IAC/B,+EAA+E;IAC/E,kBAAkB,EAAE,oBAAoB,CAAC,IAAI,CAAC;IAC9C,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,GAAG,eAAe,CAYtE;AAED,0DAA0D;AAC1D,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,4DAA4D;AAC5D,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,eAAe,EAAE,MAAM,CAAC;IACxB,8BAA8B;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,kCAAkC;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,WAAW,CAAC;IACnB,WAAW,EAAE,WAAW,EAAE,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,oBAAoB,EAC7B,MAAM,EAAE,eAAe,GACtB,kBAAkB,CA8BpB;AAUD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,kBAAkB,EACzB,SAAS,EAAE,UAAU,EACrB,OAAO,GAAE,UAAmC,GAC3C,IAAI,CAKN;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,8BAA8B,CAClD,QAAQ,EAAE,WAAW,EACrB,gBAAgB,EAAE,WAAW,EAC7B,qBAAqB,EAAE,WAAW,EAAE,EACpC,mBAAmB,EAAE,WAAW,EAAE,GACjC,OAAO,CAAC,MAAM,CAAC,CAkBjB"}
@@ -0,0 +1,133 @@
1
+ import { ArkAddress, buildOffchainTx, CSVMultisigTapscript, combineTapscriptSigs, Transaction, } from "@arkade-os/sdk";
2
+ import { base64, hex } from "@scure/base";
3
+ /**
4
+ * Derive {@link EscrowArkConfig} from the ASP's `getInfo()` response.
5
+ *
6
+ * Mirrors the connect step every party performs: x-only the signer key,
7
+ * decode the checkpoint tapscript, and map the unilateral-exit delay to a
8
+ * relative timelock.
9
+ */
10
+ export function escrowArkConfigFromInfo(info) {
11
+ if (!info.checkpointTapscript) {
12
+ throw new Error("ASP info is missing checkpointTapscript");
13
+ }
14
+ return {
15
+ aspPubKey: toXOnly(hex.decode(info.signerPubkey)),
16
+ exitTimelock: delayToTimelock(info.unilateralExitDelay),
17
+ serverUnrollScript: CSVMultisigTapscript.decode(hex.decode(info.checkpointTapscript)),
18
+ dust: info.dust,
19
+ };
20
+ }
21
+ /**
22
+ * Build the cooperative release ark-tx and its checkpoint(s) spending the
23
+ * escrow funding VTXO to `buyer` and `fee` outputs via the cooperative leaf.
24
+ *
25
+ * Deterministic: identical inputs produce identical PSBT bytes and txids, so
26
+ * the arbiter can rebuild on a later round instead of persisting state.
27
+ */
28
+ export function buildEscrowReleaseTx(escrow, funding, outputs, config) {
29
+ const arkInput = {
30
+ txid: funding.txid,
31
+ vout: funding.vout,
32
+ value: funding.valueSats,
33
+ tapLeafScript: escrow.cooperativeLeaf(),
34
+ tapTree: escrow.encode(),
35
+ };
36
+ const buyerAmount = BigInt(outputs.buyerAmountSats);
37
+ const feeAmount = BigInt(outputs.feeSats);
38
+ const buyerAddress = ArkAddress.decode(outputs.buyerArkAddress);
39
+ const feeAddress = ArkAddress.decode(outputs.feeArkAddress);
40
+ const { arkTx, checkpoints } = buildOffchainTx([arkInput], [
41
+ {
42
+ script: pkScriptFor(buyerAddress, buyerAmount, config.dust),
43
+ amount: buyerAmount,
44
+ },
45
+ {
46
+ script: pkScriptFor(feeAddress, feeAmount, config.dust),
47
+ amount: feeAmount,
48
+ },
49
+ ], config.serverUnrollScript);
50
+ return { arkTx, checkpoints };
51
+ }
52
+ /**
53
+ * Deterministic auxRand so signing the same PSBT twice yields the same
54
+ * signature. Required because the arbiter rebuilds and re-signs the release
55
+ * across rounds, and `combineTapscriptSigs` rejects a second, differing
56
+ * signature at the same (pubkey, leafHash) slot.
57
+ */
58
+ const DETERMINISTIC_AUX_RAND = new Uint8Array(32);
59
+ /**
60
+ * Sign input 0 of the ark-tx and every checkpoint with `secretKey`, in place.
61
+ *
62
+ * Defaults to a deterministic auxRand (see above). Used by the arbiter, who
63
+ * signs across rounds; a single-shot signer (the seller) can use
64
+ * {@link signEscrowArkTx} on the base64 PSBTs instead.
65
+ */
66
+ export function signEscrowReleaseInPlace(built, secretKey, auxRand = DETERMINISTIC_AUX_RAND) {
67
+ built.arkTx.signIdx(secretKey, 0, undefined, auxRand);
68
+ for (const checkpoint of built.checkpoints) {
69
+ checkpoint.signIdx(secretKey, 0, undefined, auxRand);
70
+ }
71
+ }
72
+ /**
73
+ * Submit the fully-signed ark-tx with UNSIGNED checkpoints to the ASP, merge
74
+ * the user (seller+arbiter) checkpoint sigs into the ASP-signed responses, and
75
+ * finalize. Returns the arkTxid.
76
+ *
77
+ * The submit/finalize split lets the ASP reject a malformed ark-tx before any
78
+ * checkpoint signature is spent. Crash recovery between submit and finalize is
79
+ * the caller's responsibility.
80
+ *
81
+ * @param provider ASP provider used to submit and finalize the ark-tx
82
+ * @param fullySignedArkTx ark-tx carrying both arbiter and seller tapscript sigs
83
+ * @param userSignedCheckpoints checkpoints with arbiter+seller sigs (kept aside)
84
+ * @param unsignedCheckpoints fresh-from-build checkpoints (no signatures)
85
+ */
86
+ export async function submitAndFinalizeEscrowRelease(provider, fullySignedArkTx, userSignedCheckpoints, unsignedCheckpoints) {
87
+ const { arkTxid, signedCheckpointTxs } = await provider.submitTx(base64.encode(fullySignedArkTx.toPSBT()), unsignedCheckpoints.map((c) => base64.encode(c.toPSBT())));
88
+ const finalCheckpoints = signedCheckpointTxs.map((c, i) => {
89
+ const aspSigned = Transaction.fromPSBT(base64.decode(c));
90
+ const userSigned = userSignedCheckpoints[i];
91
+ if (!userSigned) {
92
+ throw new Error(`missing user-signed checkpoint at index ${i}`);
93
+ }
94
+ combineTapscriptSigs(userSigned, aspSigned);
95
+ return base64.encode(aspSigned.toPSBT());
96
+ });
97
+ await provider.finalizeTx(arkTxid, finalCheckpoints);
98
+ return arkTxid;
99
+ }
100
+ /**
101
+ * The ASP rejects any non-OP_RETURN output below `dust`. Ark addresses expose
102
+ * a {@link ArkAddress.subdustPkScript} encoding the destination as an
103
+ * OP_RETURN-shaped script for amounts the recipient still wants but that are
104
+ * sub-dust on L1 (e.g. a 1-sat fee on a small trade).
105
+ */
106
+ function pkScriptFor(address, amount, dust) {
107
+ return amount < dust ? address.subdustPkScript : address.pkScript;
108
+ }
109
+ /**
110
+ * BIP-68: a relative-timelock value below 512 is encoded as a block height;
111
+ * otherwise it is a 512-second-granularity time value (rounded up to the next
112
+ * valid multiple). ASP-reported values are already valid.
113
+ */
114
+ function delayToTimelock(delay) {
115
+ if (delay < 512n) {
116
+ return { value: delay, type: "blocks" };
117
+ }
118
+ const rounded = ((delay + 511n) / 512n) * 512n;
119
+ return { value: rounded, type: "seconds" };
120
+ }
121
+ /**
122
+ * Drop the sign byte from a 33-byte compressed secp256k1 pubkey to get the
123
+ * 32-byte x-only form used by BIP-340 / tapscripts. The ASP returns its
124
+ * `signerPubkey` compressed.
125
+ */
126
+ function toXOnly(pubkey) {
127
+ if (pubkey.length === 32)
128
+ return pubkey;
129
+ if (pubkey.length === 33)
130
+ return pubkey.subarray(1);
131
+ throw new Error(`unexpected pubkey length ${pubkey.length}`);
132
+ }
133
+ //# sourceMappingURL=release.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release.js","sourceRoot":"","sources":["../../src/2-of-2/release.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { type Transaction } from "@arkade-os/sdk";
2
+ export interface ReleaseArkTxExpectations {
3
+ /** The escrow VTXO outpoint that funds the release. */
4
+ escrowOutpoint: {
5
+ txid: string;
6
+ vout: number;
7
+ };
8
+ /** Buyer's payout Ark address (committed to during take). */
9
+ buyerArkAddress: string;
10
+ /** Buyer payout amount in sats. */
11
+ buyerAmountSats: bigint;
12
+ /** Escrow fee Ark address. */
13
+ feeArkAddress: string;
14
+ /** Escrow fee amount in sats. */
15
+ feeAmountSats: bigint;
16
+ }
17
+ /** The release as the seller receives it: the ark-tx and its checkpoint(s). */
18
+ export interface ReleaseToVerify {
19
+ arkTx: Transaction;
20
+ checkpoints: Transaction[];
21
+ }
22
+ export declare class ReleaseArkTxValidationError extends Error {
23
+ constructor(message: string);
24
+ }
25
+ /**
26
+ * Seller-side check before signing the cooperative release.
27
+ *
28
+ * An Arkade offchain spend is a two-tx chain: a **checkpoint** spends the
29
+ * funding VTXO, and the **ark-tx** spends the checkpoint and pays the final
30
+ * outputs (plus a zero-value P2A fee-bump anchor). This verifies the whole
31
+ * chain the seller is about to sign:
32
+ * - the checkpoint spends the escrow funding outpoint,
33
+ * - the ark-tx spends that checkpoint,
34
+ * - the ark-tx pays the agreed buyer and fee amounts — and nothing else
35
+ * except the anchor (no rogue payout).
36
+ *
37
+ * Throws on any mismatch. Caller must NOT sign if this throws.
38
+ */
39
+ export declare function verifyReleaseArkTx(release: ReleaseToVerify, expected: ReleaseArkTxExpectations): void;
40
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/2-of-2/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGnE,MAAM,WAAW,wBAAwB;IACvC,uDAAuD;IACvD,cAAc,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,6DAA6D;IAC7D,eAAe,EAAE,MAAM,CAAC;IACxB,mCAAmC;IACnC,eAAe,EAAE,MAAM,CAAC;IACxB,8BAA8B;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,WAAW,CAAC;IACnB,WAAW,EAAE,WAAW,EAAE,CAAC;CAC5B;AAED,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,wBAAwB,GACjC,IAAI,CA2FN"}
@@ -0,0 +1,100 @@
1
+ import { ArkAddress, P2A } from "@arkade-os/sdk";
2
+ import { hex } from "@scure/base";
3
+ export class ReleaseArkTxValidationError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = "ReleaseArkTxValidationError";
7
+ }
8
+ }
9
+ /**
10
+ * Seller-side check before signing the cooperative release.
11
+ *
12
+ * An Arkade offchain spend is a two-tx chain: a **checkpoint** spends the
13
+ * funding VTXO, and the **ark-tx** spends the checkpoint and pays the final
14
+ * outputs (plus a zero-value P2A fee-bump anchor). This verifies the whole
15
+ * chain the seller is about to sign:
16
+ * - the checkpoint spends the escrow funding outpoint,
17
+ * - the ark-tx spends that checkpoint,
18
+ * - the ark-tx pays the agreed buyer and fee amounts — and nothing else
19
+ * except the anchor (no rogue payout).
20
+ *
21
+ * Throws on any mismatch. Caller must NOT sign if this throws.
22
+ */
23
+ export function verifyReleaseArkTx(release, expected) {
24
+ const { arkTx, checkpoints } = release;
25
+ // A single funding VTXO yields exactly one checkpoint.
26
+ if (checkpoints.length !== 1) {
27
+ throw new ReleaseArkTxValidationError(`expected exactly 1 checkpoint, got ${checkpoints.length}`);
28
+ }
29
+ const checkpoint = checkpoints[0];
30
+ // (1) The checkpoint must spend the escrow funding outpoint.
31
+ const cpIn = checkpoint.getInput(0);
32
+ if (!cpIn.txid || cpIn.index === undefined) {
33
+ throw new ReleaseArkTxValidationError("checkpoint input 0 missing prevout");
34
+ }
35
+ const cpInTxid = hex.encode(cpIn.txid);
36
+ if (cpInTxid !== expected.escrowOutpoint.txid ||
37
+ cpIn.index !== expected.escrowOutpoint.vout) {
38
+ throw new ReleaseArkTxValidationError(`checkpoint input ${cpInTxid}:${cpIn.index} does not spend funding ${expected.escrowOutpoint.txid}:${expected.escrowOutpoint.vout}`);
39
+ }
40
+ // (2) The ark-tx must spend that checkpoint.
41
+ if (arkTx.inputsLength !== 1) {
42
+ throw new ReleaseArkTxValidationError(`expected exactly 1 ark-tx input, got ${arkTx.inputsLength}`);
43
+ }
44
+ const arkIn = arkTx.getInput(0);
45
+ if (!arkIn.txid) {
46
+ throw new ReleaseArkTxValidationError("ark-tx input 0 missing prevout");
47
+ }
48
+ const arkInTxid = hex.encode(arkIn.txid);
49
+ if (arkInTxid !== checkpoint.id) {
50
+ throw new ReleaseArkTxValidationError(`ark-tx input ${arkInTxid} does not spend checkpoint ${checkpoint.id}`);
51
+ }
52
+ // (3) The ark-tx must pay buyer + fee, and nothing else but the anchor.
53
+ const buyerAddress = ArkAddress.decode(expected.buyerArkAddress);
54
+ const feeAddress = ArkAddress.decode(expected.feeArkAddress);
55
+ const anchorScriptHex = hex.encode(P2A.script);
56
+ let foundBuyer = false;
57
+ let foundFee = false;
58
+ for (let i = 0; i < arkTx.outputsLength; i++) {
59
+ const output = arkTx.getOutput(i);
60
+ if (!output.script || output.amount === undefined) {
61
+ throw new ReleaseArkTxValidationError(`ark-tx output ${i} missing script or amount`);
62
+ }
63
+ if (matchesAddress(output.script, buyerAddress) &&
64
+ output.amount === expected.buyerAmountSats) {
65
+ foundBuyer = true;
66
+ }
67
+ else if (matchesAddress(output.script, feeAddress) &&
68
+ output.amount === expected.feeAmountSats) {
69
+ foundFee = true;
70
+ }
71
+ else if (hex.encode(output.script) === anchorScriptHex &&
72
+ output.amount === P2A.amount) {
73
+ // Zero-value P2A fee-bump anchor — expected.
74
+ }
75
+ else {
76
+ throw new ReleaseArkTxValidationError(`unexpected ark-tx output ${i}: ${output.amount} sats to ${hex.encode(output.script)}`);
77
+ }
78
+ }
79
+ if (!foundBuyer) {
80
+ throw new ReleaseArkTxValidationError(`no output paying ${expected.buyerAmountSats} sats to buyer ${expected.buyerArkAddress}`);
81
+ }
82
+ if (!foundFee) {
83
+ throw new ReleaseArkTxValidationError(`no output paying ${expected.feeAmountSats} sats to fee ${expected.feeArkAddress}`);
84
+ }
85
+ }
86
+ /** Match a pkScript against an Ark address, allowing its sub-dust form. */
87
+ function matchesAddress(script, address) {
88
+ return (bytesEqual(script, address.pkScript) ||
89
+ bytesEqual(script, address.subdustPkScript));
90
+ }
91
+ function bytesEqual(a, b) {
92
+ if (a.length !== b.length)
93
+ return false;
94
+ for (let i = 0; i < a.length; i++) {
95
+ if (a[i] !== b[i])
96
+ return false;
97
+ }
98
+ return true;
99
+ }
100
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/2-of-2/verify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from "./2-of-2/index.js";
2
+ export * from "./payout-commitment.js";
3
+ export * from "./sign.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./2-of-2/index.js";
2
+ export * from "./payout-commitment.js";
3
+ export * from "./sign.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,WAAW,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Buyer's commitment to a payout Ark address.
3
+ *
4
+ * The buyer's destination is an Ark address (bech32m wrapping a server
5
+ * pubkey + vtxo taproot key), not an L1 P2WPKH/P2TR address, so an L1
6
+ * BIP-322 flow does not apply directly. Instead the buyer Schnorr-signs a
7
+ * deterministic message tying the offer, the destination address, and the
8
+ * x-only pubkey embedded in that destination — binding them to the payout
9
+ * destination used in the cooperative release.
10
+ */
11
+ export declare function payoutCommitmentMessage(offerId: string, payoutArkAddress: string): string;
12
+ //# sourceMappingURL=payout-commitment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payout-commitment.d.ts","sourceRoot":"","sources":["../src/payout-commitment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,GACvB,MAAM,CAER"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Buyer's commitment to a payout Ark address.
3
+ *
4
+ * The buyer's destination is an Ark address (bech32m wrapping a server
5
+ * pubkey + vtxo taproot key), not an L1 P2WPKH/P2TR address, so an L1
6
+ * BIP-322 flow does not apply directly. Instead the buyer Schnorr-signs a
7
+ * deterministic message tying the offer, the destination address, and the
8
+ * x-only pubkey embedded in that destination — binding them to the payout
9
+ * destination used in the cooperative release.
10
+ */
11
+ export function payoutCommitmentMessage(offerId, payoutArkAddress) {
12
+ return `escrow:take:${offerId}:${payoutArkAddress}`;
13
+ }
14
+ //# sourceMappingURL=payout-commitment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payout-commitment.js","sourceRoot":"","sources":["../src/payout-commitment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,UAAU,uBAAuB,CACrC,OAAe,EACf,gBAAwB;IAExB,OAAO,eAAe,OAAO,IAAI,gBAAgB,EAAE,CAAC;AACtD,CAAC"}
package/dist/sign.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ export interface SignedEscrowTx {
2
+ signedPsbt: string;
3
+ txid: string;
4
+ }
5
+ /**
6
+ * Sign input 0 of an Arkade ark-tx PSBT with `secretKey`.
7
+ *
8
+ * Equivalent to `signEscrowArkTx` from `@lendasat/lendaswap-sdk-pure`,
9
+ * lifted into this SDK so consumers don't have to pull in Lendaswap's
10
+ * EVM dependency stack (zerodev/viem) just to sign one tapscript leaf.
11
+ *
12
+ * The PSBT must already carry the cooperative tapleaf script, control
13
+ * block, and the sighash type on input 0. The arbiter (server) attaches
14
+ * those when it builds the release tx. Does not finalize.
15
+ */
16
+ export declare function signEscrowArkTx(psbtB64: string, secretKey: Uint8Array): SignedEscrowTx;
17
+ /** Same as {@link signEscrowArkTx} but for an array of checkpoint PSBTs. */
18
+ export declare function signEscrowCheckpoints(psbtB64s: string[], secretKey: Uint8Array): string[];
19
+ //# sourceMappingURL=sign.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign.d.ts","sourceRoot":"","sources":["../src/sign.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,UAAU,GACpB,cAAc,CAOhB;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAAE,EAClB,SAAS,EAAE,UAAU,GACpB,MAAM,EAAE,CAEV"}
package/dist/sign.js ADDED
@@ -0,0 +1,26 @@
1
+ import { Transaction } from "@arkade-os/sdk";
2
+ import { base64 } from "@scure/base";
3
+ /**
4
+ * Sign input 0 of an Arkade ark-tx PSBT with `secretKey`.
5
+ *
6
+ * Equivalent to `signEscrowArkTx` from `@lendasat/lendaswap-sdk-pure`,
7
+ * lifted into this SDK so consumers don't have to pull in Lendaswap's
8
+ * EVM dependency stack (zerodev/viem) just to sign one tapscript leaf.
9
+ *
10
+ * The PSBT must already carry the cooperative tapleaf script, control
11
+ * block, and the sighash type on input 0. The arbiter (server) attaches
12
+ * those when it builds the release tx. Does not finalize.
13
+ */
14
+ export function signEscrowArkTx(psbtB64, secretKey) {
15
+ const tx = Transaction.fromPSBT(base64.decode(psbtB64));
16
+ tx.signIdx(secretKey, 0);
17
+ return {
18
+ signedPsbt: base64.encode(tx.toPSBT()),
19
+ txid: tx.id,
20
+ };
21
+ }
22
+ /** Same as {@link signEscrowArkTx} but for an array of checkpoint PSBTs. */
23
+ export function signEscrowCheckpoints(psbtB64s, secretKey) {
24
+ return psbtB64s.map((b) => signEscrowArkTx(b, secretKey).signedPsbt);
25
+ }
26
+ //# sourceMappingURL=sign.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign.js","sourceRoot":"","sources":["../src/sign.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAOrC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAe,EACf,SAAqB;IAErB,MAAM,EAAE,GAAG,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACxD,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IACzB,OAAO;QACL,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC;QACtC,IAAI,EAAE,EAAE,CAAC,EAAE;KACZ,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,qBAAqB,CACnC,QAAkB,EAClB,SAAqB;IAErB,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,UAAU,CAAC,CAAC;AACvE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@satora/escrow",
3
+ "version": "0.0.1-alpha.0",
4
+ "description": "Escrow primitives for Arkade",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./2-of-2": {
15
+ "types": "./dist/2-of-2/index.d.ts",
16
+ "import": "./dist/2-of-2/index.js"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "dependencies": {
23
+ "@arkade-os/sdk": "^0.4.32",
24
+ "@scure/base": "^2.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "@biomejs/biome": "2.3.15",
28
+ "@noble/curves": "^2.0.0",
29
+ "@types/node": "^22.0.0",
30
+ "typescript": "^5.7.0",
31
+ "vitest": "^3.0.0"
32
+ },
33
+ "license": "MIT",
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.build.json",
39
+ "check-types": "tsc -p tsconfig.json --noEmit",
40
+ "test": "vitest",
41
+ "test:run": "vitest run",
42
+ "lint": "biome check .",
43
+ "lint:fix": "biome check --write .",
44
+ "clean": "rm -rf dist"
45
+ }
46
+ }