@dexterai/x402 1.9.3 → 2.0.0
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/dist/adapters/index.cjs +14 -6
- package/dist/adapters/index.d.cts +3 -5
- package/dist/adapters/index.d.ts +3 -5
- package/dist/adapters/index.js +14 -6
- package/dist/client/index.cjs +251 -29
- package/dist/client/index.d.cts +215 -37
- package/dist/client/index.d.ts +215 -37
- package/dist/client/index.js +248 -29
- package/dist/react/index.cjs +88 -33
- package/dist/react/index.d.cts +10 -5
- package/dist/react/index.d.ts +10 -5
- package/dist/react/index.js +88 -33
- package/dist/server/index.cjs +27 -4
- package/dist/server/index.d.cts +45 -13
- package/dist/server/index.d.ts +45 -13
- package/dist/server/index.js +27 -4
- package/dist/{sponsored-access-H1EX6zpi.d.ts → sponsored-access-BgEDLg_H.d.cts} +31 -2
- package/dist/{sponsored-access-BCB2CxdG.d.cts → sponsored-access-DjLEKhOV.d.ts} +31 -2
- package/dist/{types-BQvaF8lB.d.cts → types-CjLMR7qs.d.cts} +1 -1
- package/dist/{types-BQvaF8lB.d.ts → types-CjLMR7qs.d.ts} +1 -1
- package/dist/types-D1TGACsL.d.ts +245 -0
- package/dist/types-DWhpiOBD.d.cts +245 -0
- package/dist/utils/index.cjs +8 -7
- package/dist/utils/index.js +8 -7
- package/package.json +1 -1
- package/dist/adapters/index.cjs.map +0 -1
- package/dist/adapters/index.js.map +0 -1
- package/dist/client/index.cjs.map +0 -1
- package/dist/client/index.js.map +0 -1
- package/dist/react/index.cjs.map +0 -1
- package/dist/react/index.js.map +0 -1
- package/dist/server/index.cjs.map +0 -1
- package/dist/server/index.js.map +0 -1
- package/dist/solana-CfHuiW2H.d.cts +0 -132
- package/dist/solana-kZcwbUK9.d.ts +0 -132
- package/dist/types-DmqH9yD8.d.cts +0 -123
- package/dist/types-ENcnkof8.d.ts +0 -123
- package/dist/utils/index.cjs.map +0 -1
- package/dist/utils/index.js.map +0 -1
package/dist/adapters/index.cjs
CHANGED
|
@@ -131,8 +131,11 @@ var SolanaAdapter = class {
|
|
|
131
131
|
const account = await (0, import_spl_token.getAccount)(connection, ata, void 0, programId);
|
|
132
132
|
const decimals = accept.extra?.decimals ?? 6;
|
|
133
133
|
return Number(account.amount) / Math.pow(10, decimals);
|
|
134
|
-
} catch {
|
|
135
|
-
|
|
134
|
+
} catch (err) {
|
|
135
|
+
if (err && typeof err === "object" && "name" in err && (err.name === "TokenAccountNotFoundError" || err.name === "TokenInvalidAccountOwnerError")) {
|
|
136
|
+
return 0;
|
|
137
|
+
}
|
|
138
|
+
throw err;
|
|
136
139
|
}
|
|
137
140
|
}
|
|
138
141
|
async buildTransaction(accept, wallet, rpcUrl) {
|
|
@@ -360,15 +363,21 @@ var EvmAdapter = class {
|
|
|
360
363
|
]
|
|
361
364
|
})
|
|
362
365
|
});
|
|
366
|
+
if (!response.ok) {
|
|
367
|
+
throw new Error(`RPC request failed: ${response.status}`);
|
|
368
|
+
}
|
|
363
369
|
const result = await response.json();
|
|
364
|
-
if (result.error
|
|
370
|
+
if (result.error) {
|
|
371
|
+
throw new Error(`RPC error: ${JSON.stringify(result.error)}`);
|
|
372
|
+
}
|
|
373
|
+
if (!result.result || result.result === "0x") {
|
|
365
374
|
return 0;
|
|
366
375
|
}
|
|
367
376
|
const balance = BigInt(result.result);
|
|
368
377
|
const decimals = accept.extra?.decimals ?? 6;
|
|
369
378
|
return Number(balance) / Math.pow(10, decimals);
|
|
370
|
-
} catch {
|
|
371
|
-
|
|
379
|
+
} catch (err) {
|
|
380
|
+
throw err;
|
|
372
381
|
}
|
|
373
382
|
}
|
|
374
383
|
encodeBalanceOf(address) {
|
|
@@ -502,4 +511,3 @@ function findAdapter(adapters, network) {
|
|
|
502
511
|
isKnownUSDC,
|
|
503
512
|
isSolanaWallet
|
|
504
513
|
});
|
|
505
|
-
//# sourceMappingURL=index.cjs.map
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { C as ChainAdapter } from '../types-
|
|
2
|
-
export { A as AdapterConfig, B as BalanceInfo, G as GenericWallet,
|
|
3
|
-
import
|
|
4
|
-
export { A as ARBITRUM_ONE, k as AVALANCHE, B as BASE_MAINNET, j as BASE_SEPOLIA, n as ETHEREUM_MAINNET, E as EvmWallet, O as OPTIMISM, P as POLYGON, l as SKALE_BASE, m as SKALE_BASE_SEPOLIA, e as SOLANA_DEVNET, S as SOLANA_MAINNET, f as SOLANA_TESTNET, g as SolanaWallet, U as USDC_ADDRESSES, a as createEvmAdapter, c as createSolanaAdapter, h as isEvmWallet, i as isSolanaWallet } from '../solana-CfHuiW2H.cjs';
|
|
5
|
-
import '../types-BQvaF8lB.cjs';
|
|
1
|
+
import { e as SolanaAdapter, f as EvmAdapter, C as ChainAdapter } from '../types-DWhpiOBD.cjs';
|
|
2
|
+
export { m as ARBITRUM_ONE, n as AVALANCHE, A as AdapterConfig, B as BASE_MAINNET, l as BASE_SEPOLIA, d as BalanceInfo, q as ETHEREUM_MAINNET, E as EvmWallet, G as GenericWallet, O as OPTIMISM, P as POLYGON, o as SKALE_BASE, p as SKALE_BASE_SEPOLIA, h as SOLANA_DEVNET, b as SOLANA_MAINNET, j as SOLANA_TESTNET, g as SignedTransaction, S as SolanaWallet, U as USDC_ADDRESSES, W as WalletSet, a as createEvmAdapter, c as createSolanaAdapter, k as isEvmWallet, i as isSolanaWallet } from '../types-DWhpiOBD.cjs';
|
|
3
|
+
import '../types-CjLMR7qs.cjs';
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
6
|
* Check if an asset address is a known USDC contract (any chain).
|
package/dist/adapters/index.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { C as ChainAdapter } from '../types-
|
|
2
|
-
export { A as AdapterConfig, B as BalanceInfo, G as GenericWallet,
|
|
3
|
-
import
|
|
4
|
-
export { A as ARBITRUM_ONE, k as AVALANCHE, B as BASE_MAINNET, j as BASE_SEPOLIA, n as ETHEREUM_MAINNET, E as EvmWallet, O as OPTIMISM, P as POLYGON, l as SKALE_BASE, m as SKALE_BASE_SEPOLIA, e as SOLANA_DEVNET, S as SOLANA_MAINNET, f as SOLANA_TESTNET, g as SolanaWallet, U as USDC_ADDRESSES, a as createEvmAdapter, c as createSolanaAdapter, h as isEvmWallet, i as isSolanaWallet } from '../solana-kZcwbUK9.js';
|
|
5
|
-
import '../types-BQvaF8lB.js';
|
|
1
|
+
import { e as SolanaAdapter, f as EvmAdapter, C as ChainAdapter } from '../types-D1TGACsL.js';
|
|
2
|
+
export { m as ARBITRUM_ONE, n as AVALANCHE, A as AdapterConfig, B as BASE_MAINNET, l as BASE_SEPOLIA, d as BalanceInfo, q as ETHEREUM_MAINNET, E as EvmWallet, G as GenericWallet, O as OPTIMISM, P as POLYGON, o as SKALE_BASE, p as SKALE_BASE_SEPOLIA, h as SOLANA_DEVNET, b as SOLANA_MAINNET, j as SOLANA_TESTNET, g as SignedTransaction, S as SolanaWallet, U as USDC_ADDRESSES, W as WalletSet, a as createEvmAdapter, c as createSolanaAdapter, k as isEvmWallet, i as isSolanaWallet } from '../types-D1TGACsL.js';
|
|
3
|
+
import '../types-CjLMR7qs.js';
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
6
|
* Check if an asset address is a known USDC contract (any chain).
|
package/dist/adapters/index.js
CHANGED
|
@@ -87,8 +87,11 @@ var SolanaAdapter = class {
|
|
|
87
87
|
const account = await getAccount(connection, ata, void 0, programId);
|
|
88
88
|
const decimals = accept.extra?.decimals ?? 6;
|
|
89
89
|
return Number(account.amount) / Math.pow(10, decimals);
|
|
90
|
-
} catch {
|
|
91
|
-
|
|
90
|
+
} catch (err) {
|
|
91
|
+
if (err && typeof err === "object" && "name" in err && (err.name === "TokenAccountNotFoundError" || err.name === "TokenInvalidAccountOwnerError")) {
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
throw err;
|
|
92
95
|
}
|
|
93
96
|
}
|
|
94
97
|
async buildTransaction(accept, wallet, rpcUrl) {
|
|
@@ -316,15 +319,21 @@ var EvmAdapter = class {
|
|
|
316
319
|
]
|
|
317
320
|
})
|
|
318
321
|
});
|
|
322
|
+
if (!response.ok) {
|
|
323
|
+
throw new Error(`RPC request failed: ${response.status}`);
|
|
324
|
+
}
|
|
319
325
|
const result = await response.json();
|
|
320
|
-
if (result.error
|
|
326
|
+
if (result.error) {
|
|
327
|
+
throw new Error(`RPC error: ${JSON.stringify(result.error)}`);
|
|
328
|
+
}
|
|
329
|
+
if (!result.result || result.result === "0x") {
|
|
321
330
|
return 0;
|
|
322
331
|
}
|
|
323
332
|
const balance = BigInt(result.result);
|
|
324
333
|
const decimals = accept.extra?.decimals ?? 6;
|
|
325
334
|
return Number(balance) / Math.pow(10, decimals);
|
|
326
|
-
} catch {
|
|
327
|
-
|
|
335
|
+
} catch (err) {
|
|
336
|
+
throw err;
|
|
328
337
|
}
|
|
329
338
|
}
|
|
330
339
|
encodeBalanceOf(address) {
|
|
@@ -457,4 +466,3 @@ export {
|
|
|
457
466
|
isKnownUSDC,
|
|
458
467
|
isSolanaWallet
|
|
459
468
|
};
|
|
460
|
-
//# sourceMappingURL=index.js.map
|
package/dist/client/index.cjs
CHANGED
|
@@ -187,9 +187,11 @@ var client_exports = {};
|
|
|
187
187
|
__export(client_exports, {
|
|
188
188
|
BASE_MAINNET: () => BASE_MAINNET,
|
|
189
189
|
DEXTER_FACILITATOR_URL: () => DEXTER_FACILITATOR_URL,
|
|
190
|
+
KEYPAIR_SYMBOL: () => KEYPAIR_SYMBOL,
|
|
190
191
|
SOLANA_MAINNET: () => SOLANA_MAINNET,
|
|
191
192
|
USDC_MINT: () => USDC_MINT,
|
|
192
193
|
X402Error: () => X402Error,
|
|
194
|
+
createBudgetAccount: () => createBudgetAccount,
|
|
193
195
|
createEvmAdapter: () => createEvmAdapter,
|
|
194
196
|
createEvmKeypairWallet: () => createEvmKeypairWallet,
|
|
195
197
|
createKeypairWallet: () => createKeypairWallet,
|
|
@@ -201,6 +203,7 @@ __export(client_exports, {
|
|
|
201
203
|
getSponsoredRecommendations: () => getSponsoredRecommendations,
|
|
202
204
|
isEvmKeypairWallet: () => isEvmKeypairWallet,
|
|
203
205
|
isKeypairWallet: () => isKeypairWallet,
|
|
206
|
+
searchAPIs: () => searchAPIs,
|
|
204
207
|
wrapFetch: () => wrapFetch
|
|
205
208
|
});
|
|
206
209
|
module.exports = __toCommonJS(client_exports);
|
|
@@ -298,8 +301,11 @@ var SolanaAdapter = class {
|
|
|
298
301
|
const account = await (0, import_spl_token.getAccount)(connection, ata, void 0, programId);
|
|
299
302
|
const decimals = accept.extra?.decimals ?? 6;
|
|
300
303
|
return Number(account.amount) / Math.pow(10, decimals);
|
|
301
|
-
} catch {
|
|
302
|
-
|
|
304
|
+
} catch (err) {
|
|
305
|
+
if (err && typeof err === "object" && "name" in err && (err.name === "TokenAccountNotFoundError" || err.name === "TokenInvalidAccountOwnerError")) {
|
|
306
|
+
return 0;
|
|
307
|
+
}
|
|
308
|
+
throw err;
|
|
303
309
|
}
|
|
304
310
|
}
|
|
305
311
|
async buildTransaction(accept, wallet, rpcUrl) {
|
|
@@ -527,15 +533,21 @@ var EvmAdapter = class {
|
|
|
527
533
|
]
|
|
528
534
|
})
|
|
529
535
|
});
|
|
536
|
+
if (!response.ok) {
|
|
537
|
+
throw new Error(`RPC request failed: ${response.status}`);
|
|
538
|
+
}
|
|
530
539
|
const result = await response.json();
|
|
531
|
-
if (result.error
|
|
540
|
+
if (result.error) {
|
|
541
|
+
throw new Error(`RPC error: ${JSON.stringify(result.error)}`);
|
|
542
|
+
}
|
|
543
|
+
if (!result.result || result.result === "0x") {
|
|
532
544
|
return 0;
|
|
533
545
|
}
|
|
534
546
|
const balance = BigInt(result.result);
|
|
535
547
|
const decimals = accept.extra?.decimals ?? 6;
|
|
536
548
|
return Number(balance) / Math.pow(10, decimals);
|
|
537
|
-
} catch {
|
|
538
|
-
|
|
549
|
+
} catch (err) {
|
|
550
|
+
throw err;
|
|
539
551
|
}
|
|
540
552
|
}
|
|
541
553
|
encodeBalanceOf(address) {
|
|
@@ -651,10 +663,34 @@ function createX402Client(config) {
|
|
|
651
663
|
maxAmountAtomic,
|
|
652
664
|
fetch: customFetch = globalThis.fetch,
|
|
653
665
|
verbose = false,
|
|
654
|
-
accessPass: accessPassConfig
|
|
666
|
+
accessPass: accessPassConfig,
|
|
667
|
+
onPaymentRequired,
|
|
668
|
+
maxRetries = 0,
|
|
669
|
+
retryDelayMs = 500
|
|
655
670
|
} = config;
|
|
656
671
|
const log = verbose ? console.log.bind(console, "[x402]") : () => {
|
|
657
672
|
};
|
|
673
|
+
async function fetchWithRetry(input, init) {
|
|
674
|
+
let lastError;
|
|
675
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
676
|
+
try {
|
|
677
|
+
const response = await customFetch(input, init);
|
|
678
|
+
if (response.status >= 502 && response.status <= 504 && attempt < maxRetries) {
|
|
679
|
+
log(`Retry ${attempt + 1}/${maxRetries}: server returned ${response.status}`);
|
|
680
|
+
await new Promise((r) => setTimeout(r, retryDelayMs * Math.pow(2, attempt)));
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
return response;
|
|
684
|
+
} catch (err) {
|
|
685
|
+
lastError = err;
|
|
686
|
+
if (attempt < maxRetries) {
|
|
687
|
+
log(`Retry ${attempt + 1}/${maxRetries}: ${err instanceof Error ? err.message : "network error"}`);
|
|
688
|
+
await new Promise((r) => setTimeout(r, retryDelayMs * Math.pow(2, attempt)));
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
throw lastError;
|
|
693
|
+
}
|
|
658
694
|
const passCache = /* @__PURE__ */ new Map();
|
|
659
695
|
function getCachedPass(url) {
|
|
660
696
|
try {
|
|
@@ -764,13 +800,17 @@ function createX402Client(config) {
|
|
|
764
800
|
const paymentAmount = accept.amount ?? accept.maxAmountRequired;
|
|
765
801
|
if (!paymentAmount) return null;
|
|
766
802
|
const rpcUrl = getRpcUrl(accept.network, adapter);
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
803
|
+
try {
|
|
804
|
+
const balance = await adapter.getBalance(accept, wallet, rpcUrl);
|
|
805
|
+
const requiredAmount = Number(paymentAmount) / Math.pow(10, decimals);
|
|
806
|
+
if (balance < requiredAmount) {
|
|
807
|
+
throw new X402Error(
|
|
808
|
+
"insufficient_balance",
|
|
809
|
+
`Insufficient balance for access pass. Have $${balance.toFixed(4)}, need $${requiredAmount.toFixed(4)}`
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
} catch (err) {
|
|
813
|
+
if (err instanceof X402Error) throw err;
|
|
774
814
|
}
|
|
775
815
|
const signedTx = await adapter.buildTransaction(accept, wallet, rpcUrl);
|
|
776
816
|
let payload;
|
|
@@ -783,13 +823,19 @@ function createX402Client(config) {
|
|
|
783
823
|
let resolvedResource = requirements.resource;
|
|
784
824
|
if (typeof requirements.resource === "string") {
|
|
785
825
|
try {
|
|
786
|
-
|
|
826
|
+
const resolved = new URL(requirements.resource, originalUrl);
|
|
827
|
+
if (["http:", "https:"].includes(resolved.protocol)) {
|
|
828
|
+
resolvedResource = resolved.toString();
|
|
829
|
+
}
|
|
787
830
|
} catch {
|
|
788
831
|
}
|
|
789
832
|
} else if (requirements.resource && typeof requirements.resource === "object" && "url" in requirements.resource) {
|
|
790
833
|
const rObj = requirements.resource;
|
|
791
834
|
try {
|
|
792
|
-
|
|
835
|
+
const resolved = new URL(rObj.url, originalUrl);
|
|
836
|
+
if (["http:", "https:"].includes(resolved.protocol)) {
|
|
837
|
+
resolvedResource = { ...rObj, url: resolved.toString() };
|
|
838
|
+
}
|
|
793
839
|
} catch {
|
|
794
840
|
}
|
|
795
841
|
}
|
|
@@ -844,7 +890,7 @@ function createX402Client(config) {
|
|
|
844
890
|
}
|
|
845
891
|
}
|
|
846
892
|
}
|
|
847
|
-
const response = await
|
|
893
|
+
const response = await fetchWithRetry(input, init);
|
|
848
894
|
if (response.status !== 402) {
|
|
849
895
|
return response;
|
|
850
896
|
}
|
|
@@ -917,16 +963,27 @@ function createX402Client(config) {
|
|
|
917
963
|
}
|
|
918
964
|
const rpcUrl = getRpcUrl(accept.network, adapter);
|
|
919
965
|
log("Checking balance...");
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
966
|
+
try {
|
|
967
|
+
const balance = await adapter.getBalance(accept, wallet, rpcUrl);
|
|
968
|
+
const requiredAmount = Number(paymentAmount) / Math.pow(10, decimals);
|
|
969
|
+
if (balance < requiredAmount) {
|
|
970
|
+
const network = adapter.name === "EVM" ? "Base" : "Solana";
|
|
971
|
+
throw new X402Error(
|
|
972
|
+
"insufficient_balance",
|
|
973
|
+
`Insufficient USDC balance on ${network}. Have $${balance.toFixed(4)}, need $${requiredAmount.toFixed(4)}`
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
log(`Balance OK: $${balance.toFixed(4)} >= $${requiredAmount.toFixed(4)}`);
|
|
977
|
+
} catch (err) {
|
|
978
|
+
if (err instanceof X402Error) throw err;
|
|
979
|
+
log("Balance check failed (RPC error), proceeding with transaction attempt");
|
|
980
|
+
}
|
|
981
|
+
if (onPaymentRequired) {
|
|
982
|
+
const approved = await onPaymentRequired(accept);
|
|
983
|
+
if (!approved) {
|
|
984
|
+
throw new X402Error("payment_rejected", "Payment rejected by onPaymentRequired callback");
|
|
985
|
+
}
|
|
928
986
|
}
|
|
929
|
-
log(`Balance OK: $${balance.toFixed(4)} >= $${requiredAmount.toFixed(4)}`);
|
|
930
987
|
log("Building transaction...");
|
|
931
988
|
const signedTx = await adapter.buildTransaction(accept, wallet, rpcUrl);
|
|
932
989
|
log("Transaction signed");
|
|
@@ -968,7 +1025,7 @@ function createX402Client(config) {
|
|
|
968
1025
|
};
|
|
969
1026
|
const paymentSignatureHeader = btoa(JSON.stringify(paymentSignature));
|
|
970
1027
|
log("Retrying request with payment...");
|
|
971
|
-
const retryResponse = await
|
|
1028
|
+
const retryResponse = await fetchWithRetry(input, {
|
|
972
1029
|
...init,
|
|
973
1030
|
headers: {
|
|
974
1031
|
...init?.headers || {},
|
|
@@ -1012,6 +1069,7 @@ function createX402Client(config) {
|
|
|
1012
1069
|
|
|
1013
1070
|
// src/client/keypair-wallet.ts
|
|
1014
1071
|
var import_web32 = require("@solana/web3.js");
|
|
1072
|
+
var KEYPAIR_SYMBOL = /* @__PURE__ */ Symbol.for("x402:keypair");
|
|
1015
1073
|
async function createKeypairWallet(privateKey) {
|
|
1016
1074
|
let keypair;
|
|
1017
1075
|
if (typeof privateKey === "string") {
|
|
@@ -1066,7 +1124,9 @@ async function createKeypairWallet(privateKey) {
|
|
|
1066
1124
|
}
|
|
1067
1125
|
throw new Error("Unknown transaction type");
|
|
1068
1126
|
},
|
|
1127
|
+
[KEYPAIR_SYMBOL]: keypair,
|
|
1069
1128
|
keypair
|
|
1129
|
+
// deprecated — kept for backwards compat
|
|
1070
1130
|
};
|
|
1071
1131
|
}
|
|
1072
1132
|
function isKeypairWallet(wallet) {
|
|
@@ -1109,7 +1169,8 @@ function wrapFetch(fetchImpl, options) {
|
|
|
1109
1169
|
rpcUrls,
|
|
1110
1170
|
maxAmountAtomic,
|
|
1111
1171
|
verbose,
|
|
1112
|
-
accessPass
|
|
1172
|
+
accessPass,
|
|
1173
|
+
onPaymentRequired
|
|
1113
1174
|
} = options;
|
|
1114
1175
|
if (!walletPrivateKey && !evmPrivateKey) {
|
|
1115
1176
|
throw new Error("At least one wallet private key is required (walletPrivateKey or evmPrivateKey)");
|
|
@@ -1142,7 +1203,8 @@ function wrapFetch(fetchImpl, options) {
|
|
|
1142
1203
|
maxAmountAtomic,
|
|
1143
1204
|
fetch: fetchImpl,
|
|
1144
1205
|
verbose,
|
|
1145
|
-
accessPass
|
|
1206
|
+
accessPass,
|
|
1207
|
+
onPaymentRequired
|
|
1146
1208
|
};
|
|
1147
1209
|
const client = createX402Client(clientConfig);
|
|
1148
1210
|
const clientFetch = client.fetch.bind(client);
|
|
@@ -1155,6 +1217,164 @@ function wrapFetch(fetchImpl, options) {
|
|
|
1155
1217
|
return clientFetch;
|
|
1156
1218
|
}
|
|
1157
1219
|
|
|
1220
|
+
// src/client/discovery.ts
|
|
1221
|
+
var DEFAULT_MARKETPLACE = "https://x402.dexter.cash/api/facilitator/marketplace/resources";
|
|
1222
|
+
async function searchAPIs(options = {}) {
|
|
1223
|
+
const {
|
|
1224
|
+
query,
|
|
1225
|
+
category,
|
|
1226
|
+
network,
|
|
1227
|
+
maxPrice,
|
|
1228
|
+
verifiedOnly,
|
|
1229
|
+
sort = "marketplace",
|
|
1230
|
+
limit = 20,
|
|
1231
|
+
marketplaceUrl = DEFAULT_MARKETPLACE
|
|
1232
|
+
} = options;
|
|
1233
|
+
const params = new URLSearchParams();
|
|
1234
|
+
if (query) params.set("search", query);
|
|
1235
|
+
if (category) params.set("category", category);
|
|
1236
|
+
if (network) params.set("network", network);
|
|
1237
|
+
if (maxPrice !== void 0) params.set("maxPrice", String(maxPrice));
|
|
1238
|
+
if (verifiedOnly) params.set("verified", "true");
|
|
1239
|
+
params.set("sort", sort);
|
|
1240
|
+
params.set("order", "desc");
|
|
1241
|
+
params.set("limit", String(Math.min(limit, 50)));
|
|
1242
|
+
const url = `${marketplaceUrl}?${params.toString()}`;
|
|
1243
|
+
const response = await fetch(url, {
|
|
1244
|
+
headers: { "Accept": "application/json" },
|
|
1245
|
+
signal: AbortSignal.timeout(15e3)
|
|
1246
|
+
});
|
|
1247
|
+
if (!response.ok) {
|
|
1248
|
+
throw new Error(`Marketplace search failed: ${response.status}`);
|
|
1249
|
+
}
|
|
1250
|
+
const data = await response.json();
|
|
1251
|
+
if (!data.resources) return [];
|
|
1252
|
+
return data.resources.map((r) => ({
|
|
1253
|
+
name: r.displayName || r.resourceUrl,
|
|
1254
|
+
url: r.resourceUrl,
|
|
1255
|
+
method: r.method || "GET",
|
|
1256
|
+
price: r.priceLabel || (r.priceUsdc ? `$${r.priceUsdc.toFixed(4)}` : "free"),
|
|
1257
|
+
priceUsdc: r.priceUsdc ?? null,
|
|
1258
|
+
network: r.priceNetwork ?? null,
|
|
1259
|
+
description: r.description || "",
|
|
1260
|
+
category: r.category || "uncategorized",
|
|
1261
|
+
qualityScore: r.qualityScore ?? null,
|
|
1262
|
+
verified: r.verificationStatus === "pass",
|
|
1263
|
+
totalCalls: r.totalSettlements || 0,
|
|
1264
|
+
totalVolume: r.totalVolumeUsdc ? `$${r.totalVolumeUsdc.toLocaleString("en-US", { minimumFractionDigits: 2 })}` : null,
|
|
1265
|
+
seller: r.seller?.displayName ?? null,
|
|
1266
|
+
sellerReputation: r.reputationScore ?? null,
|
|
1267
|
+
authRequired: r.authRequired || false,
|
|
1268
|
+
lastActive: r.lastSettlementAt ?? null
|
|
1269
|
+
}));
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
// src/client/budget-account.ts
|
|
1273
|
+
function createBudgetAccount(config) {
|
|
1274
|
+
const { budget, allowedDomains, onPaymentRequired: userOnPayment, ...fetchOptions } = config;
|
|
1275
|
+
const totalBudget = parseFloat(budget.total);
|
|
1276
|
+
const perRequestMax = budget.perRequest ? parseFloat(budget.perRequest) : Infinity;
|
|
1277
|
+
const perHourMax = budget.perHour ? parseFloat(budget.perHour) : Infinity;
|
|
1278
|
+
if (isNaN(totalBudget) || totalBudget <= 0) {
|
|
1279
|
+
throw new Error("budget.total must be a positive number");
|
|
1280
|
+
}
|
|
1281
|
+
let ledger = [];
|
|
1282
|
+
let pendingAmount = 0;
|
|
1283
|
+
function getSpent() {
|
|
1284
|
+
return ledger.reduce((sum, r) => sum + r.amount, 0);
|
|
1285
|
+
}
|
|
1286
|
+
function getHourlySpend() {
|
|
1287
|
+
const cutoff = Date.now() - 36e5;
|
|
1288
|
+
return ledger.filter((r) => r.timestamp >= cutoff).reduce((sum, r) => sum + r.amount, 0);
|
|
1289
|
+
}
|
|
1290
|
+
const innerFetch = wrapFetch(fetch, {
|
|
1291
|
+
...fetchOptions,
|
|
1292
|
+
onPaymentRequired: async (accept) => {
|
|
1293
|
+
const decimals = accept.extra?.decimals ?? 6;
|
|
1294
|
+
const amountUsd = Number(accept.amount) / Math.pow(10, decimals);
|
|
1295
|
+
if (amountUsd > perRequestMax) {
|
|
1296
|
+
throw new X402Error(
|
|
1297
|
+
"amount_exceeds_max",
|
|
1298
|
+
`$${amountUsd.toFixed(4)} exceeds per-request limit of $${perRequestMax.toFixed(2)}`
|
|
1299
|
+
);
|
|
1300
|
+
}
|
|
1301
|
+
const spent = getSpent();
|
|
1302
|
+
if (spent + amountUsd > totalBudget) {
|
|
1303
|
+
throw new X402Error(
|
|
1304
|
+
"amount_exceeds_max",
|
|
1305
|
+
`Budget exceeded. Spent $${spent.toFixed(2)} of $${totalBudget.toFixed(2)}, payment: $${amountUsd.toFixed(4)}`
|
|
1306
|
+
);
|
|
1307
|
+
}
|
|
1308
|
+
const hourly = getHourlySpend();
|
|
1309
|
+
if (hourly + amountUsd > perHourMax) {
|
|
1310
|
+
throw new X402Error(
|
|
1311
|
+
"amount_exceeds_max",
|
|
1312
|
+
`Hourly limit ($${perHourMax.toFixed(2)}) exceeded. Spent $${hourly.toFixed(2)} this hour`
|
|
1313
|
+
);
|
|
1314
|
+
}
|
|
1315
|
+
pendingAmount = amountUsd;
|
|
1316
|
+
if (userOnPayment) return userOnPayment(accept);
|
|
1317
|
+
return true;
|
|
1318
|
+
}
|
|
1319
|
+
});
|
|
1320
|
+
const budgetFetch = (async (input, init) => {
|
|
1321
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
1322
|
+
let domain = "unknown";
|
|
1323
|
+
try {
|
|
1324
|
+
domain = new URL(url).hostname;
|
|
1325
|
+
} catch {
|
|
1326
|
+
}
|
|
1327
|
+
if (allowedDomains) {
|
|
1328
|
+
if (!allowedDomains.some((d) => domain === d || domain.endsWith(`.${d}`))) {
|
|
1329
|
+
throw new X402Error("payment_rejected", `Domain "${domain}" not in allowed domains`);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
pendingAmount = 0;
|
|
1333
|
+
const response = await innerFetch(input, init);
|
|
1334
|
+
if (pendingAmount > 0) {
|
|
1335
|
+
let network = "unknown";
|
|
1336
|
+
const paymentHeader = response.headers.get("PAYMENT-RESPONSE");
|
|
1337
|
+
if (paymentHeader) {
|
|
1338
|
+
try {
|
|
1339
|
+
const decoded = JSON.parse(atob(paymentHeader));
|
|
1340
|
+
network = decoded.network || network;
|
|
1341
|
+
} catch {
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
ledger.push({ amount: pendingAmount, domain, network, timestamp: Date.now() });
|
|
1345
|
+
pendingAmount = 0;
|
|
1346
|
+
}
|
|
1347
|
+
return response;
|
|
1348
|
+
});
|
|
1349
|
+
return {
|
|
1350
|
+
fetch: budgetFetch,
|
|
1351
|
+
get spent() {
|
|
1352
|
+
return `$${getSpent().toFixed(2)}`;
|
|
1353
|
+
},
|
|
1354
|
+
get remaining() {
|
|
1355
|
+
return `$${(totalBudget - getSpent()).toFixed(2)}`;
|
|
1356
|
+
},
|
|
1357
|
+
get payments() {
|
|
1358
|
+
return ledger.length;
|
|
1359
|
+
},
|
|
1360
|
+
get spentAmount() {
|
|
1361
|
+
return getSpent();
|
|
1362
|
+
},
|
|
1363
|
+
get remainingAmount() {
|
|
1364
|
+
return totalBudget - getSpent();
|
|
1365
|
+
},
|
|
1366
|
+
get ledger() {
|
|
1367
|
+
return ledger;
|
|
1368
|
+
},
|
|
1369
|
+
get hourlySpend() {
|
|
1370
|
+
return getHourlySpend();
|
|
1371
|
+
},
|
|
1372
|
+
reset() {
|
|
1373
|
+
ledger = [];
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1158
1378
|
// src/client/sponsored-access.ts
|
|
1159
1379
|
function getSponsoredAccessInfo(response) {
|
|
1160
1380
|
const receipt = getPaymentReceipt(response);
|
|
@@ -1180,9 +1400,11 @@ async function fireImpressionBeacon(response) {
|
|
|
1180
1400
|
0 && (module.exports = {
|
|
1181
1401
|
BASE_MAINNET,
|
|
1182
1402
|
DEXTER_FACILITATOR_URL,
|
|
1403
|
+
KEYPAIR_SYMBOL,
|
|
1183
1404
|
SOLANA_MAINNET,
|
|
1184
1405
|
USDC_MINT,
|
|
1185
1406
|
X402Error,
|
|
1407
|
+
createBudgetAccount,
|
|
1186
1408
|
createEvmAdapter,
|
|
1187
1409
|
createEvmKeypairWallet,
|
|
1188
1410
|
createKeypairWallet,
|
|
@@ -1194,6 +1416,6 @@ async function fireImpressionBeacon(response) {
|
|
|
1194
1416
|
getSponsoredRecommendations,
|
|
1195
1417
|
isEvmKeypairWallet,
|
|
1196
1418
|
isKeypairWallet,
|
|
1419
|
+
searchAPIs,
|
|
1197
1420
|
wrapFetch
|
|
1198
1421
|
});
|
|
1199
|
-
//# sourceMappingURL=index.cjs.map
|