@hazbase/simplicity 0.4.3 → 0.4.4
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 +9 -0
- package/dist/client/SimplicityClient.d.ts +1 -0
- package/dist/client/SimplicityClient.js +1 -0
- package/dist/x402/index.d.ts +21 -1
- package/dist/x402/index.js +143 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -175,6 +175,15 @@ testnet issuer or deployment uses a different USDt asset id, set
|
|
|
175
175
|
payment requirements will preserve that asset id instead of replacing it with
|
|
176
176
|
the SDK's default registry id.
|
|
177
177
|
|
|
178
|
+
For policy-locked Liquid RWA positions, redemption can be represented as a
|
|
179
|
+
Liquid x402 request with `extra.redemptionSource.type:
|
|
180
|
+
"policy_locked_position"`. Wallets can use
|
|
181
|
+
`buildLiquidPolicyLockedRedemptionPaymentFromProposal(...)` when a service has
|
|
182
|
+
already prepared the Simplicity policy-spend PSET proposal. The helper binds the
|
|
183
|
+
custom RWA asset, policy position, vault output, expiry, and summary hash into
|
|
184
|
+
the `X-PAYMENT` payload, and rejects proposals that still require holder-side
|
|
185
|
+
Simplicity witness construction.
|
|
186
|
+
|
|
178
187
|
Typical entrypoints:
|
|
179
188
|
- `sdk.rwaDvp.definePurchase(...)`
|
|
180
189
|
- `sdk.rwaDvp.buildPaymentRequirements(...)`
|
|
@@ -24,6 +24,7 @@ export declare class SimplicityClient {
|
|
|
24
24
|
decodePayment: typeof liquidX402.decodeLiquidXPayment;
|
|
25
25
|
preparePsetPayment: (input: liquidX402.LiquidX402PreparePsetPaymentInput) => Promise<liquidX402.LiquidX402PreparePsetPaymentResult>;
|
|
26
26
|
buildPaymentFromPset: typeof liquidX402.buildLiquidX402PaymentFromPset;
|
|
27
|
+
buildPolicyLockedRedemptionPaymentFromProposal: typeof liquidX402.buildLiquidPolicyLockedRedemptionPaymentFromProposal;
|
|
27
28
|
prepareLwkWasmPayment: typeof liquidX402.prepareLiquidX402LwkWasmPayment;
|
|
28
29
|
deriveLwkWasmAddress: typeof liquidX402.deriveLiquidX402LwkWasmAddress;
|
|
29
30
|
verify: (input: liquidX402.LiquidX402VerifyPaymentInput) => Promise<liquidX402.LiquidX402VerifyPaymentResult>;
|
|
@@ -74,6 +74,7 @@ class SimplicityClient {
|
|
|
74
74
|
decodePayment: liquidX402.decodeLiquidXPayment,
|
|
75
75
|
preparePsetPayment: async (input) => liquidX402.prepareLiquidX402PsetPayment(this.rpc, input),
|
|
76
76
|
buildPaymentFromPset: liquidX402.buildLiquidX402PaymentFromPset,
|
|
77
|
+
buildPolicyLockedRedemptionPaymentFromProposal: liquidX402.buildLiquidPolicyLockedRedemptionPaymentFromProposal,
|
|
77
78
|
prepareLwkWasmPayment: liquidX402.prepareLiquidX402LwkWasmPayment,
|
|
78
79
|
deriveLwkWasmAddress: liquidX402.deriveLiquidX402LwkWasmAddress,
|
|
79
80
|
verify: async (input) => liquidX402.verifyLiquidX402Payment(this.rpc, input),
|
package/dist/x402/index.d.ts
CHANGED
|
@@ -55,7 +55,7 @@ export interface LiquidX402PaymentPayload {
|
|
|
55
55
|
scheme: typeof LIQUID_X402_SCHEME;
|
|
56
56
|
network: LiquidX402Network;
|
|
57
57
|
paymentRequestId: string;
|
|
58
|
-
asset: LiquidX402AssetKey;
|
|
58
|
+
asset: LiquidX402AssetKey | "custom" | string;
|
|
59
59
|
assetId: string;
|
|
60
60
|
amountAtomic: string;
|
|
61
61
|
payTo: string;
|
|
@@ -84,6 +84,25 @@ export interface LiquidX402BuildPaymentFromPsetInput {
|
|
|
84
84
|
payer?: string;
|
|
85
85
|
summaryHash?: string;
|
|
86
86
|
}
|
|
87
|
+
export interface LiquidPolicyLockedRedemptionSpendProposal {
|
|
88
|
+
mode?: "service_prepared_policy_spend_v1" | string;
|
|
89
|
+
psetBase64?: string;
|
|
90
|
+
summaryHash?: string;
|
|
91
|
+
holderSignatureRequired?: boolean;
|
|
92
|
+
metadata?: Record<string, unknown>;
|
|
93
|
+
}
|
|
94
|
+
export interface LiquidPolicyLockedRedemptionPaymentFromProposalInput {
|
|
95
|
+
requirements: LiquidX402PaymentRequirements | Record<string, unknown>;
|
|
96
|
+
proposal?: LiquidPolicyLockedRedemptionSpendProposal | Record<string, unknown>;
|
|
97
|
+
proposalPsetBase64?: string;
|
|
98
|
+
summaryHash?: string;
|
|
99
|
+
payer?: string;
|
|
100
|
+
metadata?: Record<string, unknown>;
|
|
101
|
+
}
|
|
102
|
+
export type LiquidPolicyLockedRedemptionPaymentFromProposalResult = LiquidX402PreparePsetPaymentResult & {
|
|
103
|
+
proposalMode?: string;
|
|
104
|
+
redemptionSource: Record<string, unknown>;
|
|
105
|
+
};
|
|
87
106
|
export interface LiquidX402PrepareLwkWasmPaymentInput {
|
|
88
107
|
requirements: LiquidX402PaymentRequirements | Record<string, unknown>;
|
|
89
108
|
mnemonic: string;
|
|
@@ -186,6 +205,7 @@ export declare function encodeLiquidXPayment(payload: LiquidX402PaymentPayload):
|
|
|
186
205
|
export declare function decodeLiquidXPayment(raw: string): LiquidX402PaymentPayload | null;
|
|
187
206
|
export declare function prepareLiquidX402PsetPayment(rpc: ElementsRpcClient, input: LiquidX402PreparePsetPaymentInput): Promise<LiquidX402PreparePsetPaymentResult>;
|
|
188
207
|
export declare function buildLiquidX402PaymentFromPset(input: LiquidX402BuildPaymentFromPsetInput): LiquidX402PreparePsetPaymentResult;
|
|
208
|
+
export declare function buildLiquidPolicyLockedRedemptionPaymentFromProposal(input: LiquidPolicyLockedRedemptionPaymentFromProposalInput): LiquidPolicyLockedRedemptionPaymentFromProposalResult;
|
|
189
209
|
export declare function prepareLiquidX402LwkWasmPayment(input: LiquidX402PrepareLwkWasmPaymentInput): Promise<LiquidX402PrepareLwkWasmPaymentResult>;
|
|
190
210
|
export declare function deriveLiquidX402LwkWasmAddress(input: LiquidX402DeriveLwkWasmAddressInput): Promise<LiquidX402DeriveLwkWasmAddressResult>;
|
|
191
211
|
export declare function verifyLiquidX402Payment(rpc: ElementsRpcClient | null, input: LiquidX402VerifyPaymentInput): Promise<LiquidX402VerifyPaymentResult>;
|
package/dist/x402/index.js
CHANGED
|
@@ -22,6 +22,7 @@ exports.encodeLiquidXPayment = encodeLiquidXPayment;
|
|
|
22
22
|
exports.decodeLiquidXPayment = decodeLiquidXPayment;
|
|
23
23
|
exports.prepareLiquidX402PsetPayment = prepareLiquidX402PsetPayment;
|
|
24
24
|
exports.buildLiquidX402PaymentFromPset = buildLiquidX402PaymentFromPset;
|
|
25
|
+
exports.buildLiquidPolicyLockedRedemptionPaymentFromProposal = buildLiquidPolicyLockedRedemptionPaymentFromProposal;
|
|
25
26
|
exports.prepareLiquidX402LwkWasmPayment = prepareLiquidX402LwkWasmPayment;
|
|
26
27
|
exports.deriveLiquidX402LwkWasmAddress = deriveLiquidX402LwkWasmAddress;
|
|
27
28
|
exports.verifyLiquidX402Payment = verifyLiquidX402Payment;
|
|
@@ -208,6 +209,32 @@ function buildLiquidX402PaymentFromPset(input) {
|
|
|
208
209
|
summaryHash,
|
|
209
210
|
};
|
|
210
211
|
}
|
|
212
|
+
function buildLiquidPolicyLockedRedemptionPaymentFromProposal(input) {
|
|
213
|
+
const requirements = coercePolicyLockedRedemptionRequirements(input.requirements);
|
|
214
|
+
const proposal = coercePolicyLockedRedemptionProposal(input.proposal ?? getRecordValue(input.requirements, "policySpendProposal") ?? getExtraRecord(input.requirements).policySpendProposal, input.proposalPsetBase64);
|
|
215
|
+
const summaryHash = input.summaryHash ?? proposal.summaryHash ?? requirements.summaryHash ?? buildLiquidPolicyLockedRedemptionSummaryHash(requirements);
|
|
216
|
+
const paymentPayload = {
|
|
217
|
+
scheme: exports.LIQUID_X402_SCHEME,
|
|
218
|
+
network: requirements.network,
|
|
219
|
+
paymentRequestId: requirements.paymentRequestId,
|
|
220
|
+
asset: requirements.assetKey,
|
|
221
|
+
assetId: requirements.assetId,
|
|
222
|
+
amountAtomic: requirements.amountAtomic,
|
|
223
|
+
payTo: requirements.payTo,
|
|
224
|
+
psetBase64: proposal.psetBase64,
|
|
225
|
+
summaryHash,
|
|
226
|
+
...(input.payer ? { payer: input.payer } : {}),
|
|
227
|
+
expiresAt: requirements.expiresAt,
|
|
228
|
+
};
|
|
229
|
+
return {
|
|
230
|
+
paymentPayload,
|
|
231
|
+
xPayment: encodeLiquidXPayment(paymentPayload),
|
|
232
|
+
psetBase64: proposal.psetBase64,
|
|
233
|
+
summaryHash,
|
|
234
|
+
...(proposal.mode ? { proposalMode: proposal.mode } : {}),
|
|
235
|
+
redemptionSource: requirements.redemptionSource,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
211
238
|
async function prepareLiquidX402LwkWasmPayment(input) {
|
|
212
239
|
const requirements = coerceRequirements(input.requirements);
|
|
213
240
|
ensureLwkWasmNodeTimerCompat();
|
|
@@ -453,6 +480,108 @@ function mempoolRejectReason(result) {
|
|
|
453
480
|
const raw = result;
|
|
454
481
|
return String(raw["reject-reason"] ?? raw.reject_reason ?? raw.error ?? "mempool_rejected").trim() || "mempool_rejected";
|
|
455
482
|
}
|
|
483
|
+
function coercePolicyLockedRedemptionRequirements(value) {
|
|
484
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
485
|
+
throw new Error("requirements must be an object");
|
|
486
|
+
const raw = value;
|
|
487
|
+
const extra = getExtraRecord(raw);
|
|
488
|
+
const network = normalizeNetwork(raw.network);
|
|
489
|
+
const paymentRequestId = stringValue(extra.paymentRequestId ?? raw.paymentRequestId, "paymentRequestId");
|
|
490
|
+
const payTo = stringValue(raw.payTo ?? extra.recipient ?? extra.payTo, "payTo");
|
|
491
|
+
const assetId = stringValue(raw.assetId ?? extra.assetId ?? raw.asset, "assetId");
|
|
492
|
+
const amountAtomic = normalizeAmountAtomic((raw.maxAmountRequired ?? extra.amount ?? raw.amountAtomic));
|
|
493
|
+
const assetKey = normalizePaymentAssetKey(extra.asset ?? raw.assetKey ?? "custom");
|
|
494
|
+
const expiresAt = stringValue(extra.expiresAt ?? raw.expiresAt, "expiresAt");
|
|
495
|
+
const redemptionSource = coercePolicyLockedRedemptionSource(raw.redemptionSource ?? extra.redemptionSource);
|
|
496
|
+
const feeAssetId = String(extra.feeAssetId ?? exports.LIQUID_X402_ASSETS[network].lbtc.assetId);
|
|
497
|
+
return {
|
|
498
|
+
network,
|
|
499
|
+
paymentRequestId,
|
|
500
|
+
resource: String(raw.resource ?? ""),
|
|
501
|
+
description: String(raw.description ?? ""),
|
|
502
|
+
mimeType: String(raw.mimeType ?? ""),
|
|
503
|
+
payTo,
|
|
504
|
+
maxTimeoutSeconds: normalizeTimeout(raw.maxTimeoutSeconds),
|
|
505
|
+
assetKey,
|
|
506
|
+
assetId,
|
|
507
|
+
amountAtomic,
|
|
508
|
+
decimals: Number(extra.decimals ?? raw.decimals ?? 0),
|
|
509
|
+
expiresAt,
|
|
510
|
+
feeAsset: "lbtc",
|
|
511
|
+
feeAssetId,
|
|
512
|
+
...(extra.maxFeeSat !== undefined ? { maxFeeSat: normalizeAmountAtomic(extra.maxFeeSat) } : {}),
|
|
513
|
+
...(typeof raw.x402SummaryHash === "string" ? { summaryHash: raw.x402SummaryHash } : {}),
|
|
514
|
+
...(typeof raw.summaryHash === "string" ? { summaryHash: raw.summaryHash } : {}),
|
|
515
|
+
...(typeof extra.x402SummaryHash === "string" ? { summaryHash: extra.x402SummaryHash } : {}),
|
|
516
|
+
...(typeof extra.summaryHash === "string" ? { summaryHash: extra.summaryHash } : {}),
|
|
517
|
+
redemptionSource,
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
function coercePolicyLockedRedemptionSource(value) {
|
|
521
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
522
|
+
throw new Error("redemptionSource is required for policy-locked redemption");
|
|
523
|
+
}
|
|
524
|
+
const source = value;
|
|
525
|
+
const type = String(source.type ?? "").trim();
|
|
526
|
+
if (type !== "policy_locked_position") {
|
|
527
|
+
throw new Error("redemptionSource.type must be policy_locked_position");
|
|
528
|
+
}
|
|
529
|
+
return source;
|
|
530
|
+
}
|
|
531
|
+
function coercePolicyLockedRedemptionProposal(value, proposalPsetBase64) {
|
|
532
|
+
const raw = value && typeof value === "object" && !Array.isArray(value)
|
|
533
|
+
? value
|
|
534
|
+
: {};
|
|
535
|
+
if (raw.holderSignatureRequired === true) {
|
|
536
|
+
throw new Error("policy-locked redemption proposal still requires holder-side Simplicity signing; provide a service-prepared spend proposal");
|
|
537
|
+
}
|
|
538
|
+
const psetBase64 = String(proposalPsetBase64 ?? raw.psetBase64 ?? "").trim();
|
|
539
|
+
if (!psetBase64)
|
|
540
|
+
throw new Error("policy spend proposal psetBase64 is required");
|
|
541
|
+
return {
|
|
542
|
+
psetBase64,
|
|
543
|
+
...(raw.mode ? { mode: String(raw.mode) } : {}),
|
|
544
|
+
...(raw.summaryHash ? { summaryHash: String(raw.summaryHash) } : {}),
|
|
545
|
+
...(raw.holderSignatureRequired !== undefined ? { holderSignatureRequired: raw.holderSignatureRequired === true } : {}),
|
|
546
|
+
...(raw.metadata && typeof raw.metadata === "object" && !Array.isArray(raw.metadata)
|
|
547
|
+
? { metadata: raw.metadata }
|
|
548
|
+
: {}),
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
function buildLiquidPolicyLockedRedemptionSummaryHash(requirements) {
|
|
552
|
+
return sha256Hex(stableStringify({
|
|
553
|
+
scheme: exports.LIQUID_X402_SCHEME,
|
|
554
|
+
network: requirements.network,
|
|
555
|
+
paymentRequestId: requirements.paymentRequestId,
|
|
556
|
+
asset: requirements.assetKey,
|
|
557
|
+
assetId: requirements.assetId,
|
|
558
|
+
amountAtomic: requirements.amountAtomic,
|
|
559
|
+
payTo: requirements.payTo,
|
|
560
|
+
expectedOutput: {
|
|
561
|
+
amountAtomic: requirements.amountAtomic,
|
|
562
|
+
assetId: requirements.assetId,
|
|
563
|
+
payTo: requirements.payTo,
|
|
564
|
+
},
|
|
565
|
+
feeAsset: requirements.feeAsset,
|
|
566
|
+
feeAssetId: requirements.feeAssetId ?? exports.LIQUID_X402_ASSETS[requirements.network].lbtc.assetId,
|
|
567
|
+
maxFeeSat: requirements.maxFeeSat ?? null,
|
|
568
|
+
}));
|
|
569
|
+
}
|
|
570
|
+
function getRecordValue(value, key) {
|
|
571
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
572
|
+
return undefined;
|
|
573
|
+
return value[key];
|
|
574
|
+
}
|
|
575
|
+
function getExtraRecord(value) {
|
|
576
|
+
const extra = getRecordValue(value, "extra");
|
|
577
|
+
return extra && typeof extra === "object" && !Array.isArray(extra) ? extra : {};
|
|
578
|
+
}
|
|
579
|
+
function stringValue(value, name) {
|
|
580
|
+
const result = String(value ?? "").trim();
|
|
581
|
+
if (!result)
|
|
582
|
+
throw new Error(`${name} is required`);
|
|
583
|
+
return result;
|
|
584
|
+
}
|
|
456
585
|
function coerceRequirements(value) {
|
|
457
586
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
458
587
|
throw new Error("requirements must be an object");
|
|
@@ -563,7 +692,7 @@ function coercePaymentPayload(value) {
|
|
|
563
692
|
scheme: exports.LIQUID_X402_SCHEME,
|
|
564
693
|
network: normalizeNetwork(raw.network),
|
|
565
694
|
paymentRequestId: String(raw.paymentRequestId ?? ""),
|
|
566
|
-
asset:
|
|
695
|
+
asset: normalizePaymentAssetKey(String(raw.asset ?? "")),
|
|
567
696
|
assetId: String(raw.assetId ?? ""),
|
|
568
697
|
amountAtomic: normalizeAmountAtomic(raw.amountAtomic),
|
|
569
698
|
payTo: String(raw.payTo ?? ""),
|
|
@@ -579,6 +708,19 @@ function normalizeNetwork(input) {
|
|
|
579
708
|
return value;
|
|
580
709
|
throw new Error(`unsupported Liquid x402 network: ${String(input)}`);
|
|
581
710
|
}
|
|
711
|
+
function normalizePaymentAssetKey(input) {
|
|
712
|
+
const value = String(input ?? "").trim().toLowerCase();
|
|
713
|
+
if (value === "custom")
|
|
714
|
+
return "custom";
|
|
715
|
+
try {
|
|
716
|
+
return normalizeAssetKey(value);
|
|
717
|
+
}
|
|
718
|
+
catch {
|
|
719
|
+
if (/^[a-z0-9_-]{1,64}$/u.test(value) || /^[0-9a-f]{64}$/u.test(value))
|
|
720
|
+
return value;
|
|
721
|
+
throw new Error(`unsupported Liquid x402 asset: ${String(input)}`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
582
724
|
function normalizeAssetKey(input) {
|
|
583
725
|
const value = String(input ?? "").trim().toLowerCase();
|
|
584
726
|
if (value === "lbtc" || value === "bitcoin" || value === LIQUID_TESTNET_LBTC_ASSET_ID || value === LIQUID_MAINNET_LBTC_ASSET_ID) {
|