@relai-fi/x402 0.5.29 → 0.5.31

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.
@@ -768,6 +768,88 @@ function createX402Client(config) {
768
768
  }
769
769
  return null;
770
770
  }
771
+ function getBridgeExtension(requirements) {
772
+ const ext = requirements?.extensions?.bridge;
773
+ if (!ext?.info?.settleEndpoint || !Array.isArray(ext.info.supportedSourceChains)) return null;
774
+ return ext.info;
775
+ }
776
+ function selectBridgeSource(bridge) {
777
+ const sourceChains = bridge.supportedSourceChains || [];
778
+ const sourceAssets = bridge.supportedSourceAssets || [];
779
+ for (const caip2 of sourceChains) {
780
+ if (isSolana(caip2) && hasSolanaWallet) {
781
+ const asset = sourceAssets.find((a) => !a.startsWith("0x")) || "";
782
+ return { chain: "solana", network: caip2, asset };
783
+ }
784
+ if (isEvm(caip2) && effectiveWallets.evm) {
785
+ const asset = sourceAssets.find((a) => a.startsWith("0x")) || "";
786
+ return { chain: "evm", network: caip2, asset };
787
+ }
788
+ }
789
+ return null;
790
+ }
791
+ async function executeBridgePayment(bridge, accepts, requirements, url) {
792
+ const source = selectBridgeSource(bridge);
793
+ if (!source) {
794
+ throw new Error("[relai-x402] bridge extension found but no wallet matches supported source chains");
795
+ }
796
+ const targetAccept = accepts[0];
797
+ const amount = targetAccept.amount || targetAccept.maxAmountRequired;
798
+ log(`Bridge: ${source.network} \u2192 ${targetAccept.network}, amount=${amount}`);
799
+ if (maxAmountAtomic && BigInt(amount) > BigInt(maxAmountAtomic)) {
800
+ throw new Error(`[relai-x402] Amount ${amount} exceeds max ${maxAmountAtomic}`);
801
+ }
802
+ let sourcePaymentHeader;
803
+ if (source.chain === "solana") {
804
+ if (!bridge.payTo) {
805
+ throw new Error("[relai-x402] bridge.info.payTo is required for Solana source payments");
806
+ }
807
+ const sourceAccept = {
808
+ scheme: "exact",
809
+ network: source.network,
810
+ asset: source.asset,
811
+ payTo: bridge.payTo,
812
+ amount: targetAccept.amount || targetAccept.maxAmountRequired,
813
+ extra: {
814
+ ...bridge.feePayerSvm ? { feePayer: bridge.feePayerSvm } : {},
815
+ decimals: 6
816
+ }
817
+ };
818
+ sourcePaymentHeader = await buildSolanaPayment(sourceAccept, requirements, url);
819
+ } else {
820
+ const evmNetwork = normalizeNetwork(source.network);
821
+ const usePermit = evmNetwork && PERMIT_NETWORKS.has(evmNetwork);
822
+ const sourceAccept = {
823
+ ...targetAccept,
824
+ network: source.network,
825
+ asset: source.asset || targetAccept.asset,
826
+ ...bridge.payTo ? { payTo: bridge.payTo } : {}
827
+ };
828
+ sourcePaymentHeader = usePermit ? await buildEvmPermitPayment(sourceAccept, requirements, url) : await buildEvmPayment(sourceAccept, requirements, url);
829
+ }
830
+ const bridgeRes = await fetch(bridge.settleEndpoint, {
831
+ method: "POST",
832
+ headers: { "Content-Type": "application/json" },
833
+ body: JSON.stringify({
834
+ sourcePayment: sourcePaymentHeader,
835
+ sourceChain: source.network,
836
+ targetAccept,
837
+ requirements,
838
+ resource: url,
839
+ paymentFacilitator: bridge.paymentFacilitator || null
840
+ })
841
+ });
842
+ if (!bridgeRes.ok) {
843
+ const err = await bridgeRes.json().catch(() => ({}));
844
+ throw new Error(`[relai-x402] bridge settle failed: ${err.error || bridgeRes.status}`);
845
+ }
846
+ const bridgeData = await bridgeRes.json();
847
+ if (!bridgeData.xPayment) {
848
+ throw new Error("[relai-x402] bridge endpoint did not return xPayment header");
849
+ }
850
+ log(`Bridge settled: sourceTx=${bridgeData.sourceTxId}, targetTx=${bridgeData.targetTxId}`);
851
+ return bridgeData.xPayment;
852
+ }
771
853
  async function evmRpcCall(rpcUrl, to, data) {
772
854
  const res = await fetch(rpcUrl, {
773
855
  method: "POST",
@@ -1212,6 +1294,16 @@ function createX402Client(config) {
1212
1294
  if (!accepts.length) throw new Error("[relai-x402] No payment options in 402 response");
1213
1295
  const selected = selectAccept(accepts);
1214
1296
  if (!selected) {
1297
+ const bridge = getBridgeExtension(requirements);
1298
+ if (bridge && selectBridgeSource(bridge)) {
1299
+ log("No direct wallet match \u2014 attempting bridge extension flow");
1300
+ const paymentHeader = await executeBridgePayment(bridge, accepts, requirements, url);
1301
+ log("Retrying with X-PAYMENT header (bridge)");
1302
+ return fetch(input, {
1303
+ ...requestInitWithHeaders,
1304
+ headers: { ...requestHeaders, "X-PAYMENT": paymentHeader }
1305
+ });
1306
+ }
1215
1307
  throw new Error(buildNoWalletError(accepts, false));
1216
1308
  }
1217
1309
  const { accept, chain } = selected;