@hazbase/simplicity 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -152,6 +152,10 @@ export type DecodePsbtResult = {
152
152
  address?: string;
153
153
  hex?: string;
154
154
  };
155
+ scriptPubKey?: {
156
+ address?: string;
157
+ hex?: string;
158
+ };
155
159
  }>;
156
160
  };
157
161
  export type LiquidX402LwkWasmModule = {
@@ -219,6 +219,9 @@ async function prepareLiquidX402LwkWasmPayment(input) {
219
219
  if (requirements.extra.asset === "lbtc") {
220
220
  builder = builder.addLbtcRecipient(recipient, amount);
221
221
  }
222
+ else if (typeof recipient.isBlinded === "function" && recipient.isBlinded() === false && typeof builder.addExplicitRecipient === "function") {
223
+ builder = builder.addExplicitRecipient(recipient, amount, parseLwkAssetId(lwk, requirements.asset));
224
+ }
222
225
  else {
223
226
  builder = builder.addRecipient(recipient, amount, parseLwkAssetId(lwk, requirements.asset));
224
227
  }
@@ -270,12 +273,13 @@ async function verifyLiquidX402Payment(rpc, input) {
270
273
  return basic;
271
274
  }
272
275
  try {
276
+ const expectedTarget = await resolveExpectedLiquidOutputTarget(rpc, requirements);
273
277
  const decoded = await rpc.call("decodepsbt", [payload.psetBase64]);
274
278
  const actualSummaryHash = buildLiquidPsetSummaryHash(decoded, requirements);
275
279
  if (actualSummaryHash !== payload.summaryHash) {
276
280
  return { ...basic, isValid: false, invalidReason: "pset_summary_mismatch" };
277
281
  }
278
- if (!decodedContainsExpectedOutput(decoded, requirements)) {
282
+ if (!decodedContainsExpectedOutput(decoded, requirements, expectedTarget)) {
279
283
  return { ...basic, isValid: false, invalidReason: "pset_expected_output_missing" };
280
284
  }
281
285
  if (!decodedFeeWithinCap(decoded, requirements)) {
@@ -376,12 +380,56 @@ function buildLiquidPsetSummaryHash(_decoded, requirements) {
376
380
  maxFeeSat: req.extra.maxFeeSat ?? null,
377
381
  }));
378
382
  }
379
- function decodedContainsExpectedOutput(decoded, requirements) {
383
+ function decodedContainsExpectedOutput(decoded, requirements, expectedTarget = buildFallbackExpectedLiquidOutputTarget(requirements)) {
380
384
  const expectedAmount = atomicToDecimalNumber(requirements.maxAmountRequired, requirements.extra.decimals);
381
385
  return (decoded.outputs ?? []).some((output) => (String(output.asset ?? "").toLowerCase() === requirements.asset.toLowerCase() &&
382
386
  Math.round(Number(output.amount ?? 0) * 10 ** requirements.extra.decimals) ===
383
387
  Math.round(expectedAmount * 10 ** requirements.extra.decimals) &&
384
- String(output.script?.address ?? "") === requirements.payTo));
388
+ decodedOutputMatchesLiquidTarget(output, expectedTarget)));
389
+ }
390
+ async function resolveExpectedLiquidOutputTarget(rpc, requirements) {
391
+ const target = buildFallbackExpectedLiquidOutputTarget(requirements);
392
+ try {
393
+ const info = await rpc.call("validateaddress", [requirements.payTo]);
394
+ if (info?.isvalid !== false) {
395
+ addNormalizedAddress(target.addresses, info.address);
396
+ addNormalizedAddress(target.addresses, info.unconfidential);
397
+ addNormalizedAddress(target.addresses, info.confidential);
398
+ addNormalizedHex(target.scriptHexes, info.scriptPubKey);
399
+ }
400
+ }
401
+ catch {
402
+ // Older or mocked RPC clients may not expose validateaddress. Exact payTo
403
+ // matching remains safe for unconfidential outputs in that case.
404
+ }
405
+ return target;
406
+ }
407
+ function buildFallbackExpectedLiquidOutputTarget(requirements) {
408
+ const target = { addresses: new Set(), scriptHexes: new Set() };
409
+ addNormalizedAddress(target.addresses, requirements.payTo);
410
+ return target;
411
+ }
412
+ function decodedOutputMatchesLiquidTarget(output, expectedTarget) {
413
+ const address = output.script?.address ?? output.scriptPubKey?.address;
414
+ const scriptHex = output.script?.hex ?? output.scriptPubKey?.hex;
415
+ return (expectedTarget.addresses.has(normalizeAddressForComparison(address)) ||
416
+ expectedTarget.scriptHexes.has(normalizeHexForComparison(scriptHex)));
417
+ }
418
+ function addNormalizedAddress(target, value) {
419
+ const normalized = normalizeAddressForComparison(value);
420
+ if (normalized)
421
+ target.add(normalized);
422
+ }
423
+ function addNormalizedHex(target, value) {
424
+ const normalized = normalizeHexForComparison(value);
425
+ if (normalized)
426
+ target.add(normalized);
427
+ }
428
+ function normalizeAddressForComparison(value) {
429
+ return String(value ?? "").trim().toLowerCase();
430
+ }
431
+ function normalizeHexForComparison(value) {
432
+ return String(value ?? "").trim().toLowerCase().replace(/^0x/u, "");
385
433
  }
386
434
  function coerceRequirements(value) {
387
435
  if (!value || typeof value !== "object" || Array.isArray(value))
@@ -454,7 +502,17 @@ function ensureLwkWasmNodeTimerCompat() {
454
502
  }
455
503
  }
456
504
  function parseLwkAddress(lwk, address, network) {
457
- return typeof lwk.Address.parse === "function" ? lwk.Address.parse(address, network) : new lwk.Address(address);
505
+ if (typeof lwk.Address.parse === "function") {
506
+ try {
507
+ return lwk.Address.parse(address, network);
508
+ }
509
+ catch (error) {
510
+ const message = error instanceof Error ? error.message : String(error);
511
+ if (!message.includes("non-blinded"))
512
+ throw error;
513
+ }
514
+ }
515
+ return new lwk.Address(address);
458
516
  }
459
517
  function parseLwkAssetId(lwk, assetId) {
460
518
  return typeof lwk.AssetId.fromString === "function" ? lwk.AssetId.fromString(assetId) : new lwk.AssetId(assetId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hazbase/simplicity",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "An SDK for Simplicity on Liquid",
5
5
  "author": "IndieSquare Inc <info@hazbase.com>",
6
6
  "keywords": [