@hazbase/simplicity 0.4.1 → 0.4.3
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 +116 -2
- package/dist/client/SimplicityClient.d.ts +5 -0
- package/dist/client/SimplicityClient.js +5 -0
- package/dist/core/executor.d.ts +3 -1
- package/dist/core/executor.js +307 -8
- package/dist/core/rpc.js +3 -1
- package/dist/core/toolchain.d.ts +24 -0
- package/dist/core/toolchain.js +106 -2
- package/dist/core/types.d.ts +44 -0
- package/dist/docs/definitions/rwa-dvp-escrow.simf +128 -0
- package/dist/domain/rwaDvp.d.ts +76 -1
- package/dist/domain/rwaDvp.js +427 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7 -2
- package/dist/x402/atomicDvp.d.ts +197 -0
- package/dist/x402/atomicDvp.js +482 -0
- package/dist/x402/index.d.ts +1 -0
- package/dist/x402/index.js +15 -0
- package/package.json +1 -1
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
export declare const LIQUID_ATOMIC_DVP_PSET_SCHEME: "rwa-liquid-atomic-dvp-pset";
|
|
2
|
+
export declare const LIQUID_ATOMIC_DVP_PSET_MODE: "service_cosign_atomic_pset_v1";
|
|
3
|
+
export type LiquidAtomicDvpNetwork = "liquidtestnet" | "liquidv1";
|
|
4
|
+
export interface LiquidAtomicDvpOutputRequirement {
|
|
5
|
+
assetId: string;
|
|
6
|
+
amountAtomic: string;
|
|
7
|
+
recipient: string;
|
|
8
|
+
}
|
|
9
|
+
export interface LiquidAtomicDvpServiceSigner {
|
|
10
|
+
type: "operator_wallet" | "simplicity";
|
|
11
|
+
xonly?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface LiquidAtomicDvpRequirementsInput {
|
|
14
|
+
network?: LiquidAtomicDvpNetwork;
|
|
15
|
+
paymentRequestId: string;
|
|
16
|
+
resource: string;
|
|
17
|
+
termsHash: string;
|
|
18
|
+
policyHash?: string | null;
|
|
19
|
+
expiresAt: string | Date;
|
|
20
|
+
maxTimeoutSeconds?: number;
|
|
21
|
+
description?: string;
|
|
22
|
+
mimeType?: string;
|
|
23
|
+
payment: {
|
|
24
|
+
assetId: string;
|
|
25
|
+
amountAtomic: string | number | bigint;
|
|
26
|
+
recipient: string;
|
|
27
|
+
};
|
|
28
|
+
delivery: {
|
|
29
|
+
assetId: string;
|
|
30
|
+
amountAtomic: string | number | bigint;
|
|
31
|
+
recipient: string;
|
|
32
|
+
};
|
|
33
|
+
maxFeeSat?: string | number | bigint;
|
|
34
|
+
serviceSigner?: LiquidAtomicDvpServiceSigner | null;
|
|
35
|
+
}
|
|
36
|
+
export interface LiquidAtomicDvpRequirements {
|
|
37
|
+
scheme: typeof LIQUID_ATOMIC_DVP_PSET_SCHEME;
|
|
38
|
+
mode: typeof LIQUID_ATOMIC_DVP_PSET_MODE;
|
|
39
|
+
network: LiquidAtomicDvpNetwork;
|
|
40
|
+
paymentRequestId: string;
|
|
41
|
+
resource: string;
|
|
42
|
+
description: string;
|
|
43
|
+
mimeType: string;
|
|
44
|
+
expiresAt: string;
|
|
45
|
+
maxTimeoutSeconds: number;
|
|
46
|
+
termsHash: string;
|
|
47
|
+
policyHash: string | null;
|
|
48
|
+
summaryHash: string;
|
|
49
|
+
maxFeeSat: string | null;
|
|
50
|
+
outputs: {
|
|
51
|
+
paymentToTreasury: LiquidAtomicDvpOutputRequirement;
|
|
52
|
+
rwaToBuyer: LiquidAtomicDvpOutputRequirement;
|
|
53
|
+
};
|
|
54
|
+
serviceSigner: LiquidAtomicDvpServiceSigner | null;
|
|
55
|
+
extra: {
|
|
56
|
+
settlementMode: typeof LIQUID_ATOMIC_DVP_PSET_MODE;
|
|
57
|
+
payment: LiquidAtomicDvpOutputRequirement;
|
|
58
|
+
delivery: LiquidAtomicDvpOutputRequirement;
|
|
59
|
+
feeAsset: "lbtc";
|
|
60
|
+
maxFeeSat: string | null;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export interface LiquidAtomicDvpPaymentPayload {
|
|
64
|
+
scheme: typeof LIQUID_ATOMIC_DVP_PSET_SCHEME;
|
|
65
|
+
mode?: typeof LIQUID_ATOMIC_DVP_PSET_MODE;
|
|
66
|
+
network: LiquidAtomicDvpNetwork;
|
|
67
|
+
paymentRequestId: string;
|
|
68
|
+
psetBase64: string;
|
|
69
|
+
summaryHash: string;
|
|
70
|
+
expiresAt?: string;
|
|
71
|
+
payer?: string;
|
|
72
|
+
metadata?: Record<string, unknown>;
|
|
73
|
+
}
|
|
74
|
+
export interface LiquidAtomicDvpLwkWasmModule {
|
|
75
|
+
Address: {
|
|
76
|
+
new (value: string): any;
|
|
77
|
+
parse?: (value: string, network: any) => any;
|
|
78
|
+
};
|
|
79
|
+
AssetId: {
|
|
80
|
+
new (value: string): any;
|
|
81
|
+
fromString?: (value: string) => any;
|
|
82
|
+
};
|
|
83
|
+
EsploraClient: new (network: any, url: string, waterfalls: boolean, concurrency: number, utxoOnly: boolean) => any;
|
|
84
|
+
Mnemonic: new (value: string) => any;
|
|
85
|
+
Network: {
|
|
86
|
+
mainnet: () => any;
|
|
87
|
+
testnet: () => any;
|
|
88
|
+
};
|
|
89
|
+
OutPoint: {
|
|
90
|
+
new (value: string): any;
|
|
91
|
+
};
|
|
92
|
+
Pset: {
|
|
93
|
+
new (value: string): any;
|
|
94
|
+
};
|
|
95
|
+
Signer: new (mnemonic: any, network: any) => any;
|
|
96
|
+
Transaction?: {
|
|
97
|
+
new (value: string): any;
|
|
98
|
+
fromString?: (value: string) => any;
|
|
99
|
+
};
|
|
100
|
+
UnvalidatedLiquidexProposal: {
|
|
101
|
+
fromPset: (pset: any) => any;
|
|
102
|
+
new?: (value: string) => any;
|
|
103
|
+
};
|
|
104
|
+
Wollet: new (network: any, descriptor: any) => any;
|
|
105
|
+
}
|
|
106
|
+
export interface LiquidAtomicDvpPrepareMakerProposalInput {
|
|
107
|
+
requirements: LiquidAtomicDvpRequirements | Record<string, unknown>;
|
|
108
|
+
mnemonic: string;
|
|
109
|
+
deliveryOutpoint?: string;
|
|
110
|
+
esploraUrl?: string;
|
|
111
|
+
waterfalls?: boolean;
|
|
112
|
+
concurrency?: number;
|
|
113
|
+
utxoOnly?: boolean;
|
|
114
|
+
scan?: boolean;
|
|
115
|
+
scanToIndex?: number;
|
|
116
|
+
feeRate?: number;
|
|
117
|
+
sign?: boolean;
|
|
118
|
+
lwk?: LiquidAtomicDvpLwkWasmModule;
|
|
119
|
+
importLwk?: () => Promise<LiquidAtomicDvpLwkWasmModule>;
|
|
120
|
+
}
|
|
121
|
+
export interface LiquidAtomicDvpPrepareMakerProposalResult {
|
|
122
|
+
proposalPsetBase64: string;
|
|
123
|
+
proposal: string;
|
|
124
|
+
deliveryOutpoint: string;
|
|
125
|
+
deliveryAssetId: string;
|
|
126
|
+
deliveryAmountAtomic: string;
|
|
127
|
+
paymentAssetId: string;
|
|
128
|
+
paymentAmountAtomic: string;
|
|
129
|
+
paymentRecipient: string;
|
|
130
|
+
summaryHash: string;
|
|
131
|
+
descriptor: string;
|
|
132
|
+
dwid?: string;
|
|
133
|
+
}
|
|
134
|
+
export interface LiquidAtomicDvpTakeProposalInput {
|
|
135
|
+
requirements: LiquidAtomicDvpRequirements | Record<string, unknown>;
|
|
136
|
+
mnemonic: string;
|
|
137
|
+
proposal?: string;
|
|
138
|
+
proposalPsetBase64?: string;
|
|
139
|
+
proposalTxHex?: string;
|
|
140
|
+
payer?: string;
|
|
141
|
+
esploraUrl?: string;
|
|
142
|
+
waterfalls?: boolean;
|
|
143
|
+
concurrency?: number;
|
|
144
|
+
utxoOnly?: boolean;
|
|
145
|
+
scan?: boolean;
|
|
146
|
+
scanToIndex?: number;
|
|
147
|
+
feeRate?: number;
|
|
148
|
+
finalize?: boolean;
|
|
149
|
+
lwk?: LiquidAtomicDvpLwkWasmModule;
|
|
150
|
+
importLwk?: () => Promise<LiquidAtomicDvpLwkWasmModule>;
|
|
151
|
+
}
|
|
152
|
+
export type LiquidAtomicDvpTakeProposalResult = ReturnType<typeof buildLiquidAtomicDvpPaymentFromPset> & {
|
|
153
|
+
descriptor: string;
|
|
154
|
+
dwid?: string;
|
|
155
|
+
proposalInput: LiquidAtomicDvpOutputRequirement;
|
|
156
|
+
proposalOutput: LiquidAtomicDvpOutputRequirement;
|
|
157
|
+
};
|
|
158
|
+
export interface LiquidAtomicDvpVerifyPayloadResult {
|
|
159
|
+
isValid: boolean;
|
|
160
|
+
invalidReason?: string;
|
|
161
|
+
network?: LiquidAtomicDvpNetwork;
|
|
162
|
+
paymentRequestId?: string;
|
|
163
|
+
summaryHash?: string;
|
|
164
|
+
}
|
|
165
|
+
export declare function buildLiquidAtomicDvpRequirements(input: LiquidAtomicDvpRequirementsInput): LiquidAtomicDvpRequirements;
|
|
166
|
+
export declare function prepareLiquidAtomicDvpLwkWasmMakerProposal(input: LiquidAtomicDvpPrepareMakerProposalInput): Promise<LiquidAtomicDvpPrepareMakerProposalResult>;
|
|
167
|
+
export declare function prepareLiquidAtomicDvpLwkWasmTakerPayment(input: LiquidAtomicDvpTakeProposalInput): Promise<LiquidAtomicDvpTakeProposalResult>;
|
|
168
|
+
export declare function buildLiquidAtomicDvpSummaryHash(input: {
|
|
169
|
+
network: LiquidAtomicDvpNetwork;
|
|
170
|
+
paymentRequestId: string;
|
|
171
|
+
termsHash: string;
|
|
172
|
+
policyHash: string | null;
|
|
173
|
+
expiresAt: string;
|
|
174
|
+
maxFeeSat: string | null;
|
|
175
|
+
payment: LiquidAtomicDvpOutputRequirement;
|
|
176
|
+
delivery: LiquidAtomicDvpOutputRequirement;
|
|
177
|
+
serviceSigner: LiquidAtomicDvpServiceSigner | null;
|
|
178
|
+
}): string;
|
|
179
|
+
export declare function encodeLiquidAtomicDvpPayment(payload: LiquidAtomicDvpPaymentPayload): string;
|
|
180
|
+
export declare function decodeLiquidAtomicDvpPayment(raw: string): LiquidAtomicDvpPaymentPayload | null;
|
|
181
|
+
export declare function buildLiquidAtomicDvpPaymentFromPset(input: {
|
|
182
|
+
requirements: LiquidAtomicDvpRequirements | Record<string, unknown>;
|
|
183
|
+
psetBase64: string;
|
|
184
|
+
payer?: string;
|
|
185
|
+
metadata?: Record<string, unknown>;
|
|
186
|
+
}): {
|
|
187
|
+
paymentPayload: LiquidAtomicDvpPaymentPayload;
|
|
188
|
+
xPayment: string;
|
|
189
|
+
psetBase64: string;
|
|
190
|
+
summaryHash: string;
|
|
191
|
+
};
|
|
192
|
+
export declare function verifyLiquidAtomicDvpPayment(input: {
|
|
193
|
+
requirements: LiquidAtomicDvpRequirements | Record<string, unknown>;
|
|
194
|
+
paymentPayload: LiquidAtomicDvpPaymentPayload | Record<string, unknown>;
|
|
195
|
+
now?: Date;
|
|
196
|
+
}): LiquidAtomicDvpVerifyPayloadResult;
|
|
197
|
+
export declare function coerceAtomicRequirements(value: LiquidAtomicDvpRequirements | Record<string, unknown>): LiquidAtomicDvpRequirements;
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LIQUID_ATOMIC_DVP_PSET_MODE = exports.LIQUID_ATOMIC_DVP_PSET_SCHEME = void 0;
|
|
4
|
+
exports.buildLiquidAtomicDvpRequirements = buildLiquidAtomicDvpRequirements;
|
|
5
|
+
exports.prepareLiquidAtomicDvpLwkWasmMakerProposal = prepareLiquidAtomicDvpLwkWasmMakerProposal;
|
|
6
|
+
exports.prepareLiquidAtomicDvpLwkWasmTakerPayment = prepareLiquidAtomicDvpLwkWasmTakerPayment;
|
|
7
|
+
exports.buildLiquidAtomicDvpSummaryHash = buildLiquidAtomicDvpSummaryHash;
|
|
8
|
+
exports.encodeLiquidAtomicDvpPayment = encodeLiquidAtomicDvpPayment;
|
|
9
|
+
exports.decodeLiquidAtomicDvpPayment = decodeLiquidAtomicDvpPayment;
|
|
10
|
+
exports.buildLiquidAtomicDvpPaymentFromPset = buildLiquidAtomicDvpPaymentFromPset;
|
|
11
|
+
exports.verifyLiquidAtomicDvpPayment = verifyLiquidAtomicDvpPayment;
|
|
12
|
+
exports.coerceAtomicRequirements = coerceAtomicRequirements;
|
|
13
|
+
const node_crypto_1 = require("node:crypto");
|
|
14
|
+
const summary_1 = require("../core/summary");
|
|
15
|
+
exports.LIQUID_ATOMIC_DVP_PSET_SCHEME = "rwa-liquid-atomic-dvp-pset";
|
|
16
|
+
exports.LIQUID_ATOMIC_DVP_PSET_MODE = "service_cosign_atomic_pset_v1";
|
|
17
|
+
;
|
|
18
|
+
function buildLiquidAtomicDvpRequirements(input) {
|
|
19
|
+
const network = normalizeAtomicNetwork(input.network ?? "liquidtestnet");
|
|
20
|
+
const paymentRequestId = requiredString(input.paymentRequestId, "paymentRequestId");
|
|
21
|
+
const resource = requiredString(input.resource, "resource");
|
|
22
|
+
const termsHash = requiredString(input.termsHash, "termsHash");
|
|
23
|
+
const policyHash = input.policyHash ?? null;
|
|
24
|
+
const expiresAt = normalizeDateTime(input.expiresAt, "expiresAt");
|
|
25
|
+
const maxTimeoutSeconds = normalizeTimeout(input.maxTimeoutSeconds);
|
|
26
|
+
const payment = normalizeOutput(input.payment, "payment");
|
|
27
|
+
const delivery = normalizeOutput(input.delivery, "delivery");
|
|
28
|
+
const maxFeeSat = input.maxFeeSat === undefined ? null : normalizeInteger(input.maxFeeSat, "maxFeeSat");
|
|
29
|
+
const serviceSigner = normalizeServiceSigner(input.serviceSigner);
|
|
30
|
+
const summaryHash = buildLiquidAtomicDvpSummaryHash({
|
|
31
|
+
network,
|
|
32
|
+
paymentRequestId,
|
|
33
|
+
termsHash,
|
|
34
|
+
policyHash,
|
|
35
|
+
expiresAt,
|
|
36
|
+
maxFeeSat,
|
|
37
|
+
payment,
|
|
38
|
+
delivery,
|
|
39
|
+
serviceSigner,
|
|
40
|
+
});
|
|
41
|
+
return {
|
|
42
|
+
scheme: exports.LIQUID_ATOMIC_DVP_PSET_SCHEME,
|
|
43
|
+
mode: exports.LIQUID_ATOMIC_DVP_PSET_MODE,
|
|
44
|
+
network,
|
|
45
|
+
paymentRequestId,
|
|
46
|
+
resource,
|
|
47
|
+
description: String(input.description ?? "").trim() || "Hazbase RWA atomic Liquid DvP payment",
|
|
48
|
+
mimeType: String(input.mimeType ?? "").trim() || "application/json",
|
|
49
|
+
expiresAt,
|
|
50
|
+
maxTimeoutSeconds,
|
|
51
|
+
termsHash,
|
|
52
|
+
policyHash,
|
|
53
|
+
summaryHash,
|
|
54
|
+
maxFeeSat,
|
|
55
|
+
outputs: {
|
|
56
|
+
paymentToTreasury: payment,
|
|
57
|
+
rwaToBuyer: delivery,
|
|
58
|
+
},
|
|
59
|
+
serviceSigner,
|
|
60
|
+
extra: {
|
|
61
|
+
settlementMode: exports.LIQUID_ATOMIC_DVP_PSET_MODE,
|
|
62
|
+
payment,
|
|
63
|
+
delivery,
|
|
64
|
+
feeAsset: "lbtc",
|
|
65
|
+
maxFeeSat,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
async function prepareLiquidAtomicDvpLwkWasmMakerProposal(input) {
|
|
70
|
+
const requirements = coerceAtomicRequirements(input.requirements);
|
|
71
|
+
ensureLwkWasmNodeTimerCompat();
|
|
72
|
+
const lwk = input.lwk ?? await loadLwkWasm(input.importLwk);
|
|
73
|
+
const { network, signer, descriptor, wollet } = await buildLwkContext(lwk, requirements.network, input.mnemonic, input);
|
|
74
|
+
const delivery = requirements.outputs.rwaToBuyer;
|
|
75
|
+
const payment = requirements.outputs.paymentToTreasury;
|
|
76
|
+
const deliveryUtxo = selectExactDeliveryUtxo(wollet, delivery, input.deliveryOutpoint);
|
|
77
|
+
const paymentRecipient = parseLwkAddress(lwk, payment.recipient, network);
|
|
78
|
+
let builder = network.txBuilder();
|
|
79
|
+
if (input.feeRate !== undefined)
|
|
80
|
+
builder = builder.feeRate(input.feeRate);
|
|
81
|
+
builder = builder.liquidexMake(deliveryUtxo.outpoint, paymentRecipient, BigInt(payment.amountAtomic), parseLwkAssetId(lwk, payment.assetId));
|
|
82
|
+
const unsigned = builder.finish(wollet);
|
|
83
|
+
const proposalPset = input.sign === false ? unsigned : signer.sign(unsigned);
|
|
84
|
+
const proposalPsetBase64 = proposalPset.toString();
|
|
85
|
+
const proposal = lwk.UnvalidatedLiquidexProposal.fromPset(proposalPset).toString();
|
|
86
|
+
return {
|
|
87
|
+
proposalPsetBase64,
|
|
88
|
+
proposal,
|
|
89
|
+
deliveryOutpoint: deliveryUtxo.outpointText,
|
|
90
|
+
deliveryAssetId: delivery.assetId,
|
|
91
|
+
deliveryAmountAtomic: delivery.amountAtomic,
|
|
92
|
+
paymentAssetId: payment.assetId,
|
|
93
|
+
paymentAmountAtomic: payment.amountAtomic,
|
|
94
|
+
paymentRecipient: payment.recipient,
|
|
95
|
+
summaryHash: requirements.summaryHash,
|
|
96
|
+
descriptor: descriptor.toString(),
|
|
97
|
+
...(typeof wollet.dwid === "function" ? { dwid: wollet.dwid() } : {}),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async function prepareLiquidAtomicDvpLwkWasmTakerPayment(input) {
|
|
101
|
+
const requirements = coerceAtomicRequirements(input.requirements);
|
|
102
|
+
ensureLwkWasmNodeTimerCompat();
|
|
103
|
+
const lwk = input.lwk ?? await loadLwkWasm(input.importLwk);
|
|
104
|
+
const { network, signer, descriptor, wollet } = await buildLwkContext(lwk, requirements.network, input.mnemonic, input);
|
|
105
|
+
const proposal = parseLiquidexProposal(lwk, input.proposalPsetBase64 ?? input.proposal);
|
|
106
|
+
const validated = input.proposalTxHex
|
|
107
|
+
? proposal.validate(parseLwkTransaction(lwk, input.proposalTxHex))
|
|
108
|
+
: proposal.insecureValidate();
|
|
109
|
+
const proposalInput = assetAmountRequirement(validated.input());
|
|
110
|
+
const proposalOutput = assetAmountRequirement(validated.output());
|
|
111
|
+
assertOutputMatches(proposalInput, requirements.outputs.rwaToBuyer, "Liquidex maker input");
|
|
112
|
+
assertOutputMatches(proposalOutput, requirements.outputs.paymentToTreasury, "Liquidex maker output");
|
|
113
|
+
let builder = network.txBuilder();
|
|
114
|
+
if (input.feeRate !== undefined)
|
|
115
|
+
builder = builder.feeRate(input.feeRate);
|
|
116
|
+
builder = builder.liquidexTake([validated]);
|
|
117
|
+
const unsigned = builder.finish(wollet);
|
|
118
|
+
const signed = signer.sign(unsigned);
|
|
119
|
+
const pset = input.finalize === false ? signed : wollet.finalize(signed);
|
|
120
|
+
const payment = buildLiquidAtomicDvpPaymentFromPset({
|
|
121
|
+
requirements,
|
|
122
|
+
psetBase64: pset.toString(),
|
|
123
|
+
payer: input.payer,
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
...payment,
|
|
127
|
+
descriptor: descriptor.toString(),
|
|
128
|
+
...(typeof wollet.dwid === "function" ? { dwid: wollet.dwid() } : {}),
|
|
129
|
+
proposalInput,
|
|
130
|
+
proposalOutput,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function buildLiquidAtomicDvpSummaryHash(input) {
|
|
134
|
+
return sha256Hex((0, summary_1.stableStringify)({
|
|
135
|
+
scheme: exports.LIQUID_ATOMIC_DVP_PSET_SCHEME,
|
|
136
|
+
mode: exports.LIQUID_ATOMIC_DVP_PSET_MODE,
|
|
137
|
+
network: input.network,
|
|
138
|
+
paymentRequestId: input.paymentRequestId,
|
|
139
|
+
termsHash: input.termsHash,
|
|
140
|
+
policyHash: input.policyHash,
|
|
141
|
+
expiresAt: input.expiresAt,
|
|
142
|
+
maxFeeSat: input.maxFeeSat,
|
|
143
|
+
outputs: {
|
|
144
|
+
paymentToTreasury: input.payment,
|
|
145
|
+
rwaToBuyer: input.delivery,
|
|
146
|
+
},
|
|
147
|
+
serviceSigner: input.serviceSigner
|
|
148
|
+
? {
|
|
149
|
+
type: input.serviceSigner.type,
|
|
150
|
+
xonly: input.serviceSigner.xonly ?? null,
|
|
151
|
+
}
|
|
152
|
+
: null,
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
function encodeLiquidAtomicDvpPayment(payload) {
|
|
156
|
+
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
157
|
+
}
|
|
158
|
+
function decodeLiquidAtomicDvpPayment(raw) {
|
|
159
|
+
const value = String(raw ?? "").trim();
|
|
160
|
+
if (!value)
|
|
161
|
+
return null;
|
|
162
|
+
try {
|
|
163
|
+
const parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
|
|
164
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
165
|
+
return null;
|
|
166
|
+
if (String(parsed.scheme ?? "") !== exports.LIQUID_ATOMIC_DVP_PSET_SCHEME)
|
|
167
|
+
return null;
|
|
168
|
+
return coerceAtomicPayload(parsed);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function buildLiquidAtomicDvpPaymentFromPset(input) {
|
|
175
|
+
const requirements = coerceAtomicRequirements(input.requirements);
|
|
176
|
+
const psetBase64 = requiredString(input.psetBase64, "psetBase64");
|
|
177
|
+
const paymentPayload = {
|
|
178
|
+
scheme: exports.LIQUID_ATOMIC_DVP_PSET_SCHEME,
|
|
179
|
+
mode: exports.LIQUID_ATOMIC_DVP_PSET_MODE,
|
|
180
|
+
network: requirements.network,
|
|
181
|
+
paymentRequestId: requirements.paymentRequestId,
|
|
182
|
+
psetBase64,
|
|
183
|
+
summaryHash: requirements.summaryHash,
|
|
184
|
+
expiresAt: requirements.expiresAt,
|
|
185
|
+
...(input.payer ? { payer: input.payer } : {}),
|
|
186
|
+
...(input.metadata ? { metadata: input.metadata } : {}),
|
|
187
|
+
};
|
|
188
|
+
return {
|
|
189
|
+
paymentPayload,
|
|
190
|
+
xPayment: encodeLiquidAtomicDvpPayment(paymentPayload),
|
|
191
|
+
psetBase64,
|
|
192
|
+
summaryHash: requirements.summaryHash,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function verifyLiquidAtomicDvpPayment(input) {
|
|
196
|
+
const requirements = coerceAtomicRequirements(input.requirements);
|
|
197
|
+
const payload = coerceAtomicPayload(input.paymentPayload);
|
|
198
|
+
const now = input.now ?? new Date();
|
|
199
|
+
const base = {
|
|
200
|
+
network: payload.network,
|
|
201
|
+
paymentRequestId: payload.paymentRequestId,
|
|
202
|
+
summaryHash: payload.summaryHash,
|
|
203
|
+
};
|
|
204
|
+
if (payload.scheme !== exports.LIQUID_ATOMIC_DVP_PSET_SCHEME) {
|
|
205
|
+
return { ...base, isValid: false, invalidReason: "scheme_mismatch" };
|
|
206
|
+
}
|
|
207
|
+
if (payload.network !== requirements.network)
|
|
208
|
+
return { ...base, isValid: false, invalidReason: "network_mismatch" };
|
|
209
|
+
if (payload.paymentRequestId !== requirements.paymentRequestId) {
|
|
210
|
+
return { ...base, isValid: false, invalidReason: "payment_request_id_mismatch" };
|
|
211
|
+
}
|
|
212
|
+
if (normalizeHexHash(payload.summaryHash) !== normalizeHexHash(requirements.summaryHash)) {
|
|
213
|
+
return { ...base, isValid: false, invalidReason: "summary_hash_mismatch" };
|
|
214
|
+
}
|
|
215
|
+
if (!payload.psetBase64)
|
|
216
|
+
return { ...base, isValid: false, invalidReason: "pset_missing" };
|
|
217
|
+
if (!looksBase64(payload.psetBase64))
|
|
218
|
+
return { ...base, isValid: false, invalidReason: "pset_invalid_base64" };
|
|
219
|
+
if (new Date(payload.expiresAt ?? requirements.expiresAt).getTime() <= now.getTime()) {
|
|
220
|
+
return { ...base, isValid: false, invalidReason: "payment_expired" };
|
|
221
|
+
}
|
|
222
|
+
return { ...base, isValid: true };
|
|
223
|
+
}
|
|
224
|
+
function coerceAtomicRequirements(value) {
|
|
225
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
226
|
+
throw new Error("atomic DvP requirements must be an object");
|
|
227
|
+
}
|
|
228
|
+
const raw = value;
|
|
229
|
+
const outputs = raw.outputs && typeof raw.outputs === "object" && !Array.isArray(raw.outputs) ? raw.outputs : {};
|
|
230
|
+
const extra = raw.extra && typeof raw.extra === "object" && !Array.isArray(raw.extra) ? raw.extra : {};
|
|
231
|
+
const payment = normalizeOutput(outputs.paymentToTreasury ?? extra.payment, "paymentToTreasury");
|
|
232
|
+
const delivery = normalizeOutput(outputs.rwaToBuyer ?? extra.delivery, "rwaToBuyer");
|
|
233
|
+
const network = normalizeAtomicNetwork(raw.network);
|
|
234
|
+
const policyHash = typeof raw.policyHash === "string" && raw.policyHash.trim() ? raw.policyHash.trim() : null;
|
|
235
|
+
const maxFeeSat = raw.maxFeeSat === null || raw.maxFeeSat === undefined ? null : normalizeInteger(raw.maxFeeSat, "maxFeeSat");
|
|
236
|
+
const serviceSigner = normalizeServiceSigner(raw.serviceSigner);
|
|
237
|
+
const fallbackSummaryHash = buildLiquidAtomicDvpSummaryHash({
|
|
238
|
+
network,
|
|
239
|
+
paymentRequestId: requiredString(raw.paymentRequestId, "paymentRequestId"),
|
|
240
|
+
termsHash: requiredString(raw.termsHash, "termsHash"),
|
|
241
|
+
policyHash,
|
|
242
|
+
expiresAt: normalizeDateTime(raw.expiresAt, "expiresAt"),
|
|
243
|
+
maxFeeSat,
|
|
244
|
+
payment,
|
|
245
|
+
delivery,
|
|
246
|
+
serviceSigner,
|
|
247
|
+
});
|
|
248
|
+
return {
|
|
249
|
+
scheme: exports.LIQUID_ATOMIC_DVP_PSET_SCHEME,
|
|
250
|
+
mode: exports.LIQUID_ATOMIC_DVP_PSET_MODE,
|
|
251
|
+
network,
|
|
252
|
+
paymentRequestId: requiredString(raw.paymentRequestId, "paymentRequestId"),
|
|
253
|
+
resource: requiredString(raw.resource, "resource"),
|
|
254
|
+
description: String(raw.description ?? "").trim() || "Hazbase RWA atomic Liquid DvP payment",
|
|
255
|
+
mimeType: String(raw.mimeType ?? "").trim() || "application/json",
|
|
256
|
+
expiresAt: normalizeDateTime(raw.expiresAt, "expiresAt"),
|
|
257
|
+
maxTimeoutSeconds: normalizeTimeout(raw.maxTimeoutSeconds),
|
|
258
|
+
termsHash: requiredString(raw.termsHash, "termsHash"),
|
|
259
|
+
policyHash,
|
|
260
|
+
summaryHash: typeof raw.summaryHash === "string" && raw.summaryHash.trim() ? raw.summaryHash.trim() : fallbackSummaryHash,
|
|
261
|
+
maxFeeSat,
|
|
262
|
+
outputs: {
|
|
263
|
+
paymentToTreasury: payment,
|
|
264
|
+
rwaToBuyer: delivery,
|
|
265
|
+
},
|
|
266
|
+
serviceSigner,
|
|
267
|
+
extra: {
|
|
268
|
+
settlementMode: exports.LIQUID_ATOMIC_DVP_PSET_MODE,
|
|
269
|
+
payment,
|
|
270
|
+
delivery,
|
|
271
|
+
feeAsset: "lbtc",
|
|
272
|
+
maxFeeSat,
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function coerceAtomicPayload(value) {
|
|
277
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
278
|
+
throw new Error("atomic DvP payment payload must be an object");
|
|
279
|
+
}
|
|
280
|
+
const raw = value;
|
|
281
|
+
return {
|
|
282
|
+
scheme: String(raw.scheme ?? exports.LIQUID_ATOMIC_DVP_PSET_SCHEME),
|
|
283
|
+
mode: raw.mode === exports.LIQUID_ATOMIC_DVP_PSET_MODE ? exports.LIQUID_ATOMIC_DVP_PSET_MODE : undefined,
|
|
284
|
+
network: normalizeAtomicNetwork(raw.network),
|
|
285
|
+
paymentRequestId: requiredString(raw.paymentRequestId, "paymentRequestId"),
|
|
286
|
+
psetBase64: requiredString(raw.psetBase64, "psetBase64"),
|
|
287
|
+
summaryHash: requiredString(raw.summaryHash, "summaryHash"),
|
|
288
|
+
expiresAt: typeof raw.expiresAt === "string" ? raw.expiresAt : undefined,
|
|
289
|
+
...(typeof raw.payer === "string" && raw.payer.trim() ? { payer: raw.payer.trim() } : {}),
|
|
290
|
+
...(raw.metadata && typeof raw.metadata === "object" && !Array.isArray(raw.metadata)
|
|
291
|
+
? { metadata: raw.metadata }
|
|
292
|
+
: {}),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
async function loadLwkWasm(importer) {
|
|
296
|
+
ensureLwkWasmNodeTimerCompat();
|
|
297
|
+
if (importer)
|
|
298
|
+
return importer();
|
|
299
|
+
const dynamicImport = new Function("specifier", "return import(specifier)");
|
|
300
|
+
const failures = [];
|
|
301
|
+
for (const specifier of ["lwk_wasm", "lwk_node"]) {
|
|
302
|
+
try {
|
|
303
|
+
const imported = await dynamicImport(specifier);
|
|
304
|
+
return imported.default ?? imported;
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
failures.push(`${specifier}: ${error instanceof Error ? error.message : String(error)}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
throw new Error("lwk_wasm or lwk_node is required for atomic Liquid DvP PSETs. " +
|
|
311
|
+
`Import failed: ${failures.join("; ")}`);
|
|
312
|
+
}
|
|
313
|
+
function ensureLwkWasmNodeTimerCompat() {
|
|
314
|
+
const scope = globalThis;
|
|
315
|
+
if (typeof scope.window === "undefined" && typeof scope.setTimeout === "function") {
|
|
316
|
+
scope.window = scope;
|
|
317
|
+
}
|
|
318
|
+
if (typeof scope.Window === "undefined" && typeof scope.window !== "undefined") {
|
|
319
|
+
scope.Window = Object;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
async function buildLwkContext(lwk, networkKey, mnemonicText, options) {
|
|
323
|
+
const network = networkKey === "liquidv1" ? lwk.Network.mainnet() : lwk.Network.testnet();
|
|
324
|
+
const mnemonic = new lwk.Mnemonic(mnemonicText);
|
|
325
|
+
const signer = new lwk.Signer(mnemonic, network);
|
|
326
|
+
const descriptor = signer.wpkhSlip77Descriptor();
|
|
327
|
+
const wollet = new lwk.Wollet(network, descriptor);
|
|
328
|
+
if (options.scan !== false) {
|
|
329
|
+
const client = options.esploraUrl
|
|
330
|
+
? new lwk.EsploraClient(network, options.esploraUrl, options.waterfalls ?? false, options.concurrency ?? 4, options.utxoOnly ?? false)
|
|
331
|
+
: network.defaultEsploraClient();
|
|
332
|
+
const update = options.scanToIndex !== undefined
|
|
333
|
+
? await client.fullScanToIndex(wollet, options.scanToIndex)
|
|
334
|
+
: await client.fullScan(wollet);
|
|
335
|
+
if (update)
|
|
336
|
+
wollet.applyUpdate(update);
|
|
337
|
+
}
|
|
338
|
+
return { network, signer, descriptor, wollet };
|
|
339
|
+
}
|
|
340
|
+
function parseLwkAddress(lwk, address, network) {
|
|
341
|
+
if (typeof lwk.Address.parse === "function") {
|
|
342
|
+
try {
|
|
343
|
+
return lwk.Address.parse(address, network);
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
347
|
+
if (!message.includes("non-blinded"))
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return new lwk.Address(address);
|
|
352
|
+
}
|
|
353
|
+
function parseLwkAssetId(lwk, assetId) {
|
|
354
|
+
return typeof lwk.AssetId.fromString === "function" ? lwk.AssetId.fromString(assetId) : new lwk.AssetId(assetId);
|
|
355
|
+
}
|
|
356
|
+
function parseLwkTransaction(lwk, txHex) {
|
|
357
|
+
if (lwk.Transaction?.fromString)
|
|
358
|
+
return lwk.Transaction.fromString(txHex);
|
|
359
|
+
if (lwk.Transaction)
|
|
360
|
+
return new lwk.Transaction(txHex);
|
|
361
|
+
throw new Error("LWK Transaction support is required to validate a Liquidex proposal against a transaction.");
|
|
362
|
+
}
|
|
363
|
+
function parseLiquidexProposal(lwk, value) {
|
|
364
|
+
const encoded = String(value ?? "").trim();
|
|
365
|
+
if (!encoded)
|
|
366
|
+
throw new Error("Liquidex proposal PSET is required");
|
|
367
|
+
try {
|
|
368
|
+
return lwk.UnvalidatedLiquidexProposal.fromPset(new lwk.Pset(encoded));
|
|
369
|
+
}
|
|
370
|
+
catch (psetError) {
|
|
371
|
+
if (typeof lwk.UnvalidatedLiquidexProposal.new === "function") {
|
|
372
|
+
try {
|
|
373
|
+
return lwk.UnvalidatedLiquidexProposal.new(encoded);
|
|
374
|
+
}
|
|
375
|
+
catch { }
|
|
376
|
+
}
|
|
377
|
+
throw psetError;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function selectExactDeliveryUtxo(wollet, delivery, requestedOutpoint) {
|
|
381
|
+
const utxos = typeof wollet.utxos === "function" ? wollet.utxos() : [];
|
|
382
|
+
for (const utxo of utxos) {
|
|
383
|
+
const outpoint = utxo.outpoint();
|
|
384
|
+
const outpointText = outpointString(outpoint);
|
|
385
|
+
if (requestedOutpoint && outpointText !== requestedOutpoint)
|
|
386
|
+
continue;
|
|
387
|
+
const secrets = utxo.unblinded();
|
|
388
|
+
const assetId = secrets?.asset?.().toString?.();
|
|
389
|
+
const amountAtomic = secrets?.value?.().toString?.();
|
|
390
|
+
if (assetId === delivery.assetId && amountAtomic === delivery.amountAtomic) {
|
|
391
|
+
return { outpoint, outpointText };
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (requestedOutpoint) {
|
|
395
|
+
throw new Error(`Delivery UTXO ${requestedOutpoint} is not an exact match for the atomic DvP delivery output.`);
|
|
396
|
+
}
|
|
397
|
+
throw new Error("No exact Liquid RWA UTXO is available for the atomic DvP delivery output.");
|
|
398
|
+
}
|
|
399
|
+
function outpointString(outpoint) {
|
|
400
|
+
try {
|
|
401
|
+
return `${outpoint.txid().toString()}:${outpoint.vout()}`;
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
return String(outpoint);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
function assetAmountRequirement(value) {
|
|
408
|
+
return {
|
|
409
|
+
assetId: value.asset().toString(),
|
|
410
|
+
amountAtomic: value.amount().toString(),
|
|
411
|
+
recipient: "",
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
function assertOutputMatches(actual, expected, label) {
|
|
415
|
+
if (actual.assetId !== expected.assetId || actual.amountAtomic !== expected.amountAtomic) {
|
|
416
|
+
throw new Error(`${label} does not match atomic DvP requirements: ` +
|
|
417
|
+
`expected ${expected.amountAtomic} ${expected.assetId}, got ${actual.amountAtomic} ${actual.assetId}`);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
function normalizeAtomicNetwork(value) {
|
|
421
|
+
if (value === "liquidv1")
|
|
422
|
+
return "liquidv1";
|
|
423
|
+
if (value === "liquidtestnet" || value === undefined || value === null || value === "")
|
|
424
|
+
return "liquidtestnet";
|
|
425
|
+
throw new Error(`unsupported Liquid atomic DvP network: ${String(value)}`);
|
|
426
|
+
}
|
|
427
|
+
function normalizeOutput(value, name) {
|
|
428
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
429
|
+
throw new Error(`${name} output requirement is required`);
|
|
430
|
+
}
|
|
431
|
+
const raw = value;
|
|
432
|
+
return {
|
|
433
|
+
assetId: requiredString(raw.assetId, `${name}.assetId`),
|
|
434
|
+
amountAtomic: normalizeInteger(raw.amountAtomic, `${name}.amountAtomic`),
|
|
435
|
+
recipient: requiredString(raw.recipient, `${name}.recipient`),
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
function normalizeServiceSigner(value) {
|
|
439
|
+
if (value === null || value === undefined)
|
|
440
|
+
return null;
|
|
441
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
442
|
+
return null;
|
|
443
|
+
const raw = value;
|
|
444
|
+
const type = raw.type === "simplicity" ? "simplicity" : raw.type === "operator_wallet" ? "operator_wallet" : null;
|
|
445
|
+
if (!type)
|
|
446
|
+
return null;
|
|
447
|
+
const xonly = typeof raw.xonly === "string" && raw.xonly.trim() ? raw.xonly.trim() : undefined;
|
|
448
|
+
return { type, ...(xonly ? { xonly } : {}) };
|
|
449
|
+
}
|
|
450
|
+
function normalizeInteger(value, name) {
|
|
451
|
+
const text = String(value ?? "").trim();
|
|
452
|
+
if (!/^\d+$/u.test(text))
|
|
453
|
+
throw new Error(`${name} must be a non-negative integer string`);
|
|
454
|
+
return BigInt(text).toString();
|
|
455
|
+
}
|
|
456
|
+
function normalizeDateTime(value, name) {
|
|
457
|
+
const date = value instanceof Date ? value : new Date(String(value ?? ""));
|
|
458
|
+
if (!Number.isFinite(date.getTime()))
|
|
459
|
+
throw new Error(`${name} must be a valid date`);
|
|
460
|
+
return date.toISOString();
|
|
461
|
+
}
|
|
462
|
+
function normalizeTimeout(value) {
|
|
463
|
+
const timeout = Number(value ?? 60);
|
|
464
|
+
if (!Number.isFinite(timeout) || timeout <= 0)
|
|
465
|
+
return 60;
|
|
466
|
+
return Math.floor(timeout);
|
|
467
|
+
}
|
|
468
|
+
function requiredString(value, name) {
|
|
469
|
+
const text = String(value ?? "").trim();
|
|
470
|
+
if (!text)
|
|
471
|
+
throw new Error(`${name} is required`);
|
|
472
|
+
return text;
|
|
473
|
+
}
|
|
474
|
+
function looksBase64(value) {
|
|
475
|
+
return /^[A-Za-z0-9+/]+={0,2}$/u.test(value) || /^[A-Za-z0-9_-]+={0,2}$/u.test(value);
|
|
476
|
+
}
|
|
477
|
+
function sha256Hex(value) {
|
|
478
|
+
return (0, node_crypto_1.createHash)("sha256").update(value, "utf8").digest("hex");
|
|
479
|
+
}
|
|
480
|
+
function normalizeHexHash(value) {
|
|
481
|
+
return value.trim().toLowerCase().replace(/^0x/u, "");
|
|
482
|
+
}
|
package/dist/x402/index.d.ts
CHANGED