@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.
- package/README.md +40 -0
- package/dist/client.cjs +92 -0
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +92 -0
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +92 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +92 -0
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +92 -0
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +92 -0
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
package/dist/react/index.cjs
CHANGED
|
@@ -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;
|