@ardrive/turbo-sdk 1.42.0-alpha.5 → 1.42.0-alpha.7

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.
Files changed (46) hide show
  1. package/lib/cjs/cli/cli.js +5 -0
  2. package/lib/cjs/cli/commands/arns.js +40 -0
  3. package/lib/cjs/cli/options.js +18 -1
  4. package/lib/cjs/common/http.js +16 -2
  5. package/lib/cjs/common/payment.js +103 -0
  6. package/lib/cjs/common/signer.js +12 -3
  7. package/lib/cjs/common/token/index.js +0 -11
  8. package/lib/cjs/common/turbo.js +23 -0
  9. package/lib/cjs/common/upload.js +3 -2
  10. package/lib/cjs/types.js +18 -3
  11. package/lib/cjs/utils/common.js +0 -4
  12. package/lib/cjs/utils/errors.js +20 -1
  13. package/lib/esm/cli/cli.js +7 -2
  14. package/lib/esm/cli/commands/arns.js +40 -1
  15. package/lib/esm/cli/options.js +17 -0
  16. package/lib/esm/common/http.js +15 -1
  17. package/lib/esm/common/payment.js +105 -2
  18. package/lib/esm/common/signer.js +13 -4
  19. package/lib/esm/common/token/index.js +1 -12
  20. package/lib/esm/common/turbo.js +23 -0
  21. package/lib/esm/common/upload.js +4 -3
  22. package/lib/esm/types.js +17 -2
  23. package/lib/esm/utils/common.js +0 -4
  24. package/lib/esm/utils/errors.js +18 -0
  25. package/lib/types/cli/commands/arns.d.ts +11 -2
  26. package/lib/types/cli/commands/arns.d.ts.map +1 -1
  27. package/lib/types/cli/options.d.ts +26 -0
  28. package/lib/types/cli/options.d.ts.map +1 -1
  29. package/lib/types/cli/types.d.ts +6 -0
  30. package/lib/types/cli/types.d.ts.map +1 -1
  31. package/lib/types/common/http.d.ts +12 -0
  32. package/lib/types/common/http.d.ts.map +1 -1
  33. package/lib/types/common/payment.d.ts +32 -1
  34. package/lib/types/common/payment.d.ts.map +1 -1
  35. package/lib/types/common/signer.d.ts +10 -0
  36. package/lib/types/common/signer.d.ts.map +1 -1
  37. package/lib/types/common/token/index.d.ts.map +1 -1
  38. package/lib/types/common/turbo.d.ts +20 -1
  39. package/lib/types/common/turbo.d.ts.map +1 -1
  40. package/lib/types/common/upload.d.ts.map +1 -1
  41. package/lib/types/types.d.ts +110 -1
  42. package/lib/types/types.d.ts.map +1 -1
  43. package/lib/types/utils/common.d.ts.map +1 -1
  44. package/lib/types/utils/errors.d.ts +15 -0
  45. package/lib/types/utils/errors.d.ts.map +1 -1
  46. package/package.json +1 -1
@@ -102,6 +102,11 @@ const utils_js_1 = require("./utils.js");
102
102
  .description('Get the Turbo Credit (winc + mARIO) price to buy, extend, increase undernames, or upgrade an ArNS name'), options_js_1.arnsPriceOptions).action(async (_commandOptions, command) => {
103
103
  await (0, utils_js_1.runCommand)(command, arns_js_1.arnsPrice);
104
104
  });
105
+ (0, utils_js_1.applyOptions)(commander_1.program
106
+ .command('arns-fiat-quote')
107
+ .description('Quote an ArNS purchase paid by credit card (Stripe) - no credits needed'), options_js_1.arnsFiatQuoteOptions).action(async (_commandOptions, command) => {
108
+ await (0, utils_js_1.runCommand)(command, arns_js_1.arnsFiatQuote);
109
+ });
105
110
  (0, utils_js_1.applyOptions)(commander_1.program
106
111
  .command('buy-arns-name')
107
112
  .description('Buy an ArNS name (lease or permabuy) with Turbo Credits'), options_js_1.buyArNSNameOptions).action(async (_commandOptions, command) => {
@@ -6,6 +6,7 @@ exports.typeFromOptions = typeFromOptions;
6
6
  exports.paidByFromArNSOptions = paidByFromArNSOptions;
7
7
  exports.arnsPriceParamsFromOptions = arnsPriceParamsFromOptions;
8
8
  exports.arnsPrice = arnsPrice;
9
+ exports.arnsFiatQuote = arnsFiatQuote;
9
10
  exports.buyArNSName = buyArNSName;
10
11
  exports.extendArNSLease = extendArNSLease;
11
12
  exports.increaseArNSUndernames = increaseArNSUndernames;
@@ -146,6 +147,45 @@ async function arnsPrice(options, turbo) {
146
147
  mARIO,
147
148
  }, null, 2));
148
149
  }
150
+ async function arnsFiatQuote(options, turbo) {
151
+ const params = arnsPriceParamsFromOptions(options);
152
+ const address = options.address;
153
+ if (address === undefined) {
154
+ throw new Error('A destination --address is required for a fiat ArNS quote.');
155
+ }
156
+ const client = turbo ?? factory_js_1.TurboFactory.unauthenticated((0, utils_js_1.configFromOptions)(options));
157
+ try {
158
+ const { purchaseQuote, paymentSession, adjustments, fees } = await client.getArNSFiatPurchaseQuote({
159
+ ...params,
160
+ address,
161
+ currency: (options.currency ?? 'usd'),
162
+ method: options.method,
163
+ promoCodes: options.promoCode,
164
+ });
165
+ console.log(JSON.stringify({
166
+ name: purchaseQuote.name,
167
+ intent: purchaseQuote.intent,
168
+ nonce: purchaseQuote.nonce,
169
+ paymentAmount: purchaseQuote.paymentAmount,
170
+ currency: purchaseQuote.currencyType,
171
+ quoteExpirationDate: purchaseQuote.quoteExpirationDate,
172
+ paymentSessionId: paymentSession.id,
173
+ // Present for a payment intent / embedded checkout; a hosted
174
+ // checkout session returns `url` instead.
175
+ clientSecret: paymentSession.client_secret ?? undefined,
176
+ checkoutUrl: paymentSession.url ?? undefined,
177
+ adjustments,
178
+ fees,
179
+ }, null, 2));
180
+ }
181
+ catch (error) {
182
+ if (error instanceof errors_js_1.FiatPaymentsDisabledError) {
183
+ console.error('Fiat (Stripe) payments are disabled on this payment service. Use the credit-paid commands (buy-arns-name, extend-arns-lease, ...) instead.');
184
+ throw error;
185
+ }
186
+ throw error;
187
+ }
188
+ }
149
189
  async function buyArNSName(options, turbo) {
150
190
  const name = requiredNameFromOptions(options);
151
191
  const type = typeFromOptions(options.type);
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.removeArNSRecordOptions = exports.setArNSRecordOptions = exports.transferArNSAntOptions = exports.arnsPurchaseStatusOptions = exports.upgradeArNSNameOptions = exports.increaseArNSUndernamesOptions = exports.extendArNSLeaseOptions = exports.buyArNSNameOptions = exports.arnsPriceOptions = exports.listSharesOptions = exports.revokeCreditsOptions = exports.shareCreditsOptions = exports.uploadFileOptions = exports.uploadFolderOptions = exports.uploadOptions = exports.globalOptions = exports.walletOptions = exports.optionMap = void 0;
18
+ exports.removeArNSRecordOptions = exports.setArNSRecordOptions = exports.transferArNSAntOptions = exports.arnsPurchaseStatusOptions = exports.upgradeArNSNameOptions = exports.increaseArNSUndernamesOptions = exports.extendArNSLeaseOptions = exports.buyArNSNameOptions = exports.arnsFiatQuoteOptions = exports.arnsPriceOptions = exports.listSharesOptions = exports.revokeCreditsOptions = exports.shareCreditsOptions = exports.uploadFileOptions = exports.uploadFolderOptions = exports.uploadOptions = exports.globalOptions = exports.walletOptions = exports.optionMap = void 0;
19
19
  exports.optionMap = {
20
20
  token: {
21
21
  alias: '-t, --token <type>',
@@ -304,6 +304,23 @@ exports.arnsPriceOptions = [
304
304
  exports.optionMap.arnsIncreaseQty,
305
305
  exports.optionMap.arnsProcessId,
306
306
  ];
307
+ exports.arnsFiatQuoteOptions = [
308
+ exports.optionMap.arnsName,
309
+ exports.optionMap.arnsType,
310
+ exports.optionMap.arnsYears,
311
+ exports.optionMap.arnsIncreaseQty,
312
+ exports.optionMap.arnsProcessId,
313
+ exports.optionMap.address,
314
+ exports.optionMap.currency,
315
+ {
316
+ alias: '--method <method>',
317
+ description: 'Stripe integration mode: payment-intent (default) or checkout-session',
318
+ },
319
+ {
320
+ alias: '--promo-code <promoCode...>',
321
+ description: 'Promo code(s) to apply to the fiat price',
322
+ },
323
+ ];
307
324
  exports.buyArNSNameOptions = [
308
325
  ...exports.walletOptions,
309
326
  exports.optionMap.arnsName,
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TurboHTTPService = exports.defaultRetryConfig = void 0;
3
+ exports.TurboHTTPService = exports.x402UploadEndpoints = exports.defaultRetryConfig = void 0;
4
4
  /**
5
5
  * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
6
6
  *
@@ -34,6 +34,18 @@ const defaultHeaders = {
34
34
  'x-turbo-source-version': version_js_1.version,
35
35
  'x-turbo-source-identifier': 'turbo-sdk',
36
36
  };
37
+ /**
38
+ * Canonical x402 upload routes on the upload service.
39
+ *
40
+ * These were previously `/x402/data-item/{signed,unsigned}`. The service
41
+ * renamed them to `/x402/upload/*` and kept a `data-item/signed` alias, but not
42
+ * a `data-item/unsigned` one — so every unsigned x402 upload 404ed. Both
43
+ * canonical paths are served by all released upload-service versions.
44
+ */
45
+ exports.x402UploadEndpoints = {
46
+ signed: '/x402/upload/signed',
47
+ unsigned: '/x402/upload/unsigned',
48
+ };
37
49
  class TurboHTTPService {
38
50
  constructor({ url, logger, retryConfig = (0, exports.defaultRetryConfig)(logger), }) {
39
51
  this.logger = logger;
@@ -124,7 +136,9 @@ class TurboHTTPService {
124
136
  throw new errors_js_1.FailedRequestError('Max retries reached - ' + lastError?.message, lastError?.status);
125
137
  }
126
138
  async x402Post({ signal, allowedStatuses, headers, data, x402Options, }) {
127
- const endpoint = '/x402/data-item/' + (x402Options.unsignedData ? 'unsigned' : 'signed');
139
+ const endpoint = x402Options.unsignedData
140
+ ? exports.x402UploadEndpoints.unsigned
141
+ : exports.x402UploadEndpoints.signed;
128
142
  this.logger.debug('Using X402 options for POST request', {
129
143
  endpoint,
130
144
  x402Options,
@@ -190,6 +190,95 @@ class TurboUnauthenticatedPaymentService {
190
190
  const query = params.toString();
191
191
  return query.length > 0 ? `?${query}` : '';
192
192
  }
193
+ /**
194
+ * Quote a fiat (Stripe) ArNS purchase — buy a name with a credit card in one
195
+ * step, with no Turbo Credits top-up in between.
196
+ *
197
+ * Returns the recorded `purchaseQuote` (its `nonce` is what
198
+ * `getArNSPurchaseStatus` polls) plus the Stripe `paymentSession` to complete
199
+ * payment with. For `payment-intent`, confirm client-side with
200
+ * `stripe.confirmCardPayment(paymentSession.client_secret, ...)`, then poll
201
+ * the nonce until the purchase reports success or failure.
202
+ *
203
+ * Throws {@link FiatPaymentsDisabledError} when the service has Stripe turned
204
+ * off (normal in the testnet sandbox) so callers can fall back to the
205
+ * credit-paid path without string-matching a generic 503.
206
+ */
207
+ async getArNSFiatPurchaseQuote(params) {
208
+ this.validateArNSPurchaseParams(params);
209
+ const { address, currency, method = 'payment-intent', promoCodes = [], } = params;
210
+ if (typeof address !== 'string' || address.length === 0) {
211
+ throw new errors_js_1.ProvidedInputError('A destination `address` is required for a fiat ArNS purchase quote.');
212
+ }
213
+ if (!(0, types_js_1.isCurrency)(currency)) {
214
+ throw new errors_js_1.ProvidedInputError(`Invalid currency '${currency}'. Supported: ${types_js_1.fiatCurrencyTypes.join(', ')}`);
215
+ }
216
+ // Every interpolated segment is encoded. Five user-controlled values land in
217
+ // the path here, and an unencoded one (e.g. a name or address containing
218
+ // `../`) would silently retarget the request at another route.
219
+ const segments = [
220
+ method,
221
+ address,
222
+ currency,
223
+ params.intent,
224
+ params.name,
225
+ ].map((segment) => encodeURIComponent(segment));
226
+ const query = this.buildArNSFiatQuoteQuery(params, promoCodes);
227
+ try {
228
+ return await this.httpService.get({
229
+ endpoint: `/arns/quote/${segments.join('/')}${query}`,
230
+ });
231
+ }
232
+ catch (error) {
233
+ // The service returns 503 both for "Stripe is disabled" and for internal
234
+ // errors, so the body is what disambiguates them.
235
+ if (error instanceof errors_js_1.FailedRequestError &&
236
+ error.status === 503 &&
237
+ /Fiat \(Stripe\).*disabled/i.test(error.message)) {
238
+ throw new errors_js_1.FiatPaymentsDisabledError(error.message);
239
+ }
240
+ throw error;
241
+ }
242
+ }
243
+ /**
244
+ * Query string for a fiat quote. Distinct from `buildArNSPurchaseQuery`
245
+ * because this route takes `uiMode` + its paired URLs and has no `paidBy`
246
+ * (fiat has no delegated payer), and because promo codes must be REPEATED
247
+ * params here: the service reads them with `parseQueryParams`, which treats a
248
+ * comma-joined string as one code rather than several.
249
+ */
250
+ buildArNSFiatQuoteQuery(params, promoCodes) {
251
+ const { type, years, increaseQty, processId } = params;
252
+ const search = new URLSearchParams();
253
+ if (type !== undefined)
254
+ search.set('type', type);
255
+ if (years !== undefined)
256
+ search.set('years', `${years}`);
257
+ if (increaseQty !== undefined)
258
+ search.set('increaseQty', `${increaseQty}`);
259
+ if (processId !== undefined)
260
+ search.set('processId', processId);
261
+ const uiMode = params.uiMode;
262
+ if (uiMode !== undefined)
263
+ search.set('uiMode', uiMode);
264
+ if (uiMode === 'embedded') {
265
+ const { returnUrl } = params;
266
+ if (returnUrl !== undefined)
267
+ search.set('returnUrl', returnUrl);
268
+ }
269
+ else {
270
+ const { successUrl, cancelUrl } = params;
271
+ if (successUrl !== undefined)
272
+ search.set('successUrl', successUrl);
273
+ if (cancelUrl !== undefined)
274
+ search.set('cancelUrl', cancelUrl);
275
+ }
276
+ for (const code of promoCodes) {
277
+ search.append('promoCode', code);
278
+ }
279
+ const query = search.toString();
280
+ return query.length > 0 ? `?${query}` : '';
281
+ }
193
282
  appendPromoCodesToQuery(promoCodes) {
194
283
  const promoCodesQuery = promoCodes.join(',');
195
284
  return promoCodesQuery ? `promoCode=${promoCodesQuery}` : '';
@@ -355,6 +444,20 @@ class TurboAuthenticatedPaymentService extends TurboUnauthenticatedPaymentServic
355
444
  userAddress ??= await this.signer.getNativeAddress();
356
445
  return super.getBalance(userAddress);
357
446
  }
447
+ /**
448
+ * Quote a fiat (Stripe) ArNS purchase. `address` defaults to this signer's
449
+ * native address — the wallet that will own the name — so the common case
450
+ * needs no address at all. Pass one explicitly to buy on another wallet's
451
+ * behalf; the route takes the destination as a path param and requires no
452
+ * signature, which is why it is available unauthenticated too.
453
+ */
454
+ async getArNSFiatPurchaseQuote(params) {
455
+ const address = params.address ?? (await this.signer.getNativeAddress());
456
+ return super.getArNSFiatPurchaseQuote({
457
+ ...params,
458
+ address,
459
+ });
460
+ }
358
461
  async getFreeStatus(userAddress) {
359
462
  userAddress ??= await this.signer.getNativeAddress();
360
463
  return super.getFreeStatus(userAddress);
@@ -60,7 +60,6 @@ class TurboDataItemAbstractSigner {
60
60
  case 'base-eth':
61
61
  case 'usdc':
62
62
  case 'base-usdc':
63
- case 'base-ario':
64
63
  case 'polygon-usdc':
65
64
  return (0, ethers_2.computeAddress)((0, signing_key_1.computePublicKey)((0, base64_js_1.fromB64Url)(owner)));
66
65
  case 'kyve':
@@ -172,13 +171,23 @@ class TurboDataItemAbstractSigner {
172
171
  }
173
172
  }
174
173
  exports.TurboDataItemAbstractSigner = TurboDataItemAbstractSigner;
174
+ /**
175
+ * Builds the wallet client x402-fetch signs payment authorizations with.
176
+ *
177
+ * The chain matters because `wrapFetchWithPayment` maps `walletClient.chain.id`
178
+ * to a network name and prefers the matching entry in the service's `accepts`
179
+ * list. The upload service advertises Base mainnet (`base`), and x402 support
180
+ * here is limited to `base-usdc`, so mainnet is the correct default. Callers
181
+ * needing another network can supply their own signer via
182
+ * `X402Funding({ signer })`.
183
+ */
175
184
  async function makeX402Signer(arbundlesSigner) {
176
185
  // Node: our SDK uses EthereumSigner with a raw private key
177
186
  if (arbundlesSigner instanceof arbundles_1.EthereumSigner) {
178
187
  return (0, viem_1.createWalletClient)({
179
188
  account: (0, accounts_1.privateKeyToAccount)(('0x' +
180
189
  Buffer.from(arbundlesSigner.key).toString('hex'))),
181
- chain: chains_1.baseSepolia,
190
+ chain: chains_1.base,
182
191
  transport: (0, viem_1.http)(),
183
192
  });
184
193
  }
@@ -197,7 +206,7 @@ async function makeX402Signer(arbundlesSigner) {
197
206
  const account = accounts[0];
198
207
  return (0, viem_1.createWalletClient)({
199
208
  account,
200
- chain: chains_1.baseSepolia,
209
+ chain: chains_1.base,
201
210
  transport: (0, viem_1.custom)(provider),
202
211
  });
203
212
  }
@@ -17,17 +17,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.tokenToBaseMap = exports.exponentMap = exports.defaultTokenMap = void 0;
18
18
  exports.isTokenType = isTokenType;
19
19
  const types_js_1 = require("../../types.js");
20
- const common_js_1 = require("../../utils/common.js");
21
20
  const ario_js_1 = require("./ario.js");
22
21
  const arweave_js_1 = require("./arweave.js");
23
22
  const baseEth_js_1 = require("./baseEth.js");
24
- const erc20_js_1 = require("./erc20.js");
25
23
  const ethereum_js_1 = require("./ethereum.js");
26
24
  const kyve_js_1 = require("./kyve.js");
27
25
  const polygon_js_1 = require("./polygon.js");
28
26
  const solana_js_1 = require("./solana.js");
29
27
  const usdc_js_1 = require("./usdc.js");
30
- const baseARIOContractAddress = '0x138746adfA52909E5920def027f5a8dc1C7EfFb6';
31
28
  exports.defaultTokenMap = {
32
29
  arweave: (config) => new arweave_js_1.ArweaveToken(config),
33
30
  ario: (config) => new ario_js_1.ARIOToken(config),
@@ -40,12 +37,6 @@ exports.defaultTokenMap = {
40
37
  usdc: (config) => new usdc_js_1.USDCToken({ network: 'ethereum', ...config }),
41
38
  'base-usdc': (config) => new usdc_js_1.USDCToken({ network: 'base', ...config }),
42
39
  'polygon-usdc': (config) => new usdc_js_1.USDCToken({ network: 'polygon', ...config }),
43
- 'base-ario': (config) => new erc20_js_1.ERC20Token({
44
- ...config,
45
- pollingOptions: config.pollingOptions ?? baseEth_js_1.defaultBaseNetworkPollingOptions,
46
- tokenContractAddress: baseARIOContractAddress,
47
- gatewayUrl: config.gatewayUrl ?? common_js_1.defaultProdGatewayUrls['base-ario'],
48
- }),
49
40
  };
50
41
  const ethExponent = 18;
51
42
  const usdcExponent = 6;
@@ -53,7 +44,6 @@ const arioExponent = 6;
53
44
  exports.exponentMap = {
54
45
  arweave: 12,
55
46
  ario: arioExponent,
56
- 'base-ario': arioExponent,
57
47
  solana: 9,
58
48
  ethereum: ethExponent,
59
49
  'base-eth': ethExponent,
@@ -67,7 +57,6 @@ exports.exponentMap = {
67
57
  exports.tokenToBaseMap = {
68
58
  arweave: (a) => (0, arweave_js_1.ARToTokenAmount)(a),
69
59
  ario: (a) => (0, ario_js_1.ARIOToTokenAmount)(a),
70
- 'base-ario': (a) => (0, usdc_js_1.USDCToTokenAmount)(a),
71
60
  solana: (a) => (0, solana_js_1.SOLToTokenAmount)(a),
72
61
  ethereum: (a) => (0, ethereum_js_1.ETHToTokenAmount)(a),
73
62
  'base-eth': (a) => (0, ethereum_js_1.ETHToTokenAmount)(a),
@@ -69,6 +69,17 @@ class TurboUnauthenticatedClient {
69
69
  getArNSPurchaseStatus(p) {
70
70
  return this.paymentService.getArNSPurchaseStatus(p);
71
71
  }
72
+ /**
73
+ * Quote a fiat (Stripe) ArNS purchase — buy a name with a credit card in one
74
+ * step, no Turbo Credits top-up in between. Complete the returned
75
+ * `paymentSession` with Stripe, then poll {@link getArNSPurchaseStatus} using
76
+ * `purchaseQuote.nonce`.
77
+ *
78
+ * Throws `FiatPaymentsDisabledError` when the service has Stripe switched off.
79
+ */
80
+ getArNSFiatPurchaseQuote(params) {
81
+ return this.paymentService.getArNSFiatPurchaseQuote(params);
82
+ }
72
83
  /**
73
84
  * Returns a list of all supported fiat currencies.
74
85
  */
@@ -201,6 +212,18 @@ class TurboAuthenticatedClient extends TurboUnauthenticatedClient {
201
212
  buyArNSName(params) {
202
213
  return this.paymentService.buyArNSName(params);
203
214
  }
215
+ /**
216
+ * Quote a fiat (Stripe) ArNS purchase for this signer's wallet — a credit-card
217
+ * buy in one step, with no Turbo Credits top-up in between. `address` defaults
218
+ * to the signer's native address; pass one to buy on another wallet's behalf.
219
+ *
220
+ * Complete the returned `paymentSession` with Stripe, then poll
221
+ * {@link getArNSPurchaseStatus} using `purchaseQuote.nonce`. Throws
222
+ * `FiatPaymentsDisabledError` when the service has Stripe switched off.
223
+ */
224
+ getArNSFiatPurchaseQuote(params) {
225
+ return this.paymentService.getArNSFiatPurchaseQuote(params);
226
+ }
204
227
  /** Extends the lease on an existing ArNS name. */
205
228
  extendArNSLease(params) {
206
229
  return this.paymentService.extendArNSLease(params);
@@ -123,7 +123,9 @@ class TurboUnauthenticatedUploadService {
123
123
  };
124
124
  return this.httpService.post({
125
125
  data: dataBuffer,
126
- endpoint: '/x402/data-item/unsigned',
126
+ // Only reached when no signer was supplied; the x402 path recomputes this
127
+ // from `unsignedData`. Both must name the same route.
128
+ endpoint: http_js_2.x402UploadEndpoints.unsigned,
127
129
  signal,
128
130
  headers: tags !== undefined
129
131
  ? { 'x-data-item-tags': JSON.stringify(tags) }
@@ -142,7 +144,6 @@ class TurboAuthenticatedBaseUploadService extends TurboUnauthenticatedUploadServ
142
144
  'solana',
143
145
  'base-eth',
144
146
  'base-usdc',
145
- 'base-ario',
146
147
  ];
147
148
  this.signer = signer;
148
149
  this.paymentService = paymentService;
package/lib/cjs/types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.arNSPurchaseIntents = exports.validChunkingModes = exports.isJWK = exports.isWebUploadFolderParams = exports.isNodeUploadFolderParams = exports.multipartFinalizedStatus = exports.multipartFailedStatus = exports.multipartPendingStatus = exports.X402Funding = exports.OnDemandFunding = exports.ExistingBalanceFunding = exports.supportedEvmSignerTokens = exports.tokenTypes = exports.fiatCurrencyTypes = void 0;
3
+ exports.arNSFiatPurchaseMethods = exports.arNSPurchaseIntents = exports.validChunkingModes = exports.isJWK = exports.isWebUploadFolderParams = exports.isNodeUploadFolderParams = exports.multipartFinalizedStatus = exports.multipartFailedStatus = exports.multipartPendingStatus = exports.X402Funding = exports.OnDemandFunding = exports.ExistingBalanceFunding = exports.supportedEvmSignerTokens = exports.tokenTypes = exports.fiatCurrencyTypes = void 0;
4
4
  exports.isCurrency = isCurrency;
5
5
  exports.isKyvePrivateKey = isKyvePrivateKey;
6
6
  exports.isEthPrivateKey = isEthPrivateKey;
@@ -25,7 +25,6 @@ function isCurrency(currency) {
25
25
  exports.tokenTypes = [
26
26
  'arweave',
27
27
  'ario',
28
- 'base-ario',
29
28
  'solana',
30
29
  'ethereum',
31
30
  'kyve',
@@ -44,7 +43,6 @@ exports.supportedEvmSignerTokens = new Set([
44
43
  'polygon-usdc',
45
44
  'usdc',
46
45
  'base-usdc',
47
- 'base-ario',
48
46
  ]);
49
47
  class ExistingBalanceFunding {
50
48
  }
@@ -115,3 +113,20 @@ exports.arNSPurchaseIntents = [
115
113
  'Increase-Undername-Limit',
116
114
  'Upgrade-Name',
117
115
  ];
116
+ // ===== ArNS purchases paid with fiat (Stripe) — no Turbo Credits in between ====
117
+ /**
118
+ * Stripe integration mode for a fiat ArNS purchase quote.
119
+ *
120
+ * - `payment-intent` — returns a Stripe PaymentIntent. Confirm it client-side
121
+ * with `stripe.confirmCardPayment(paymentSession.client_secret, ...)`.
122
+ * - `checkout-session` — returns a Stripe Checkout Session to redirect to
123
+ * (`uiMode: 'hosted'`) or embed (`uiMode: 'embedded'`).
124
+ *
125
+ * Widened with `string & Record<never, never>` so a method added service-side is
126
+ * still callable without an SDK bump, while the known values keep autocomplete.
127
+ * (`string & {}` is the usual idiom but trips the `ban-types` lint rule.)
128
+ */
129
+ exports.arNSFiatPurchaseMethods = [
130
+ 'payment-intent',
131
+ 'checkout-session',
132
+ ];
@@ -50,7 +50,6 @@ const baseMainnetRpc = 'https://mainnet.base.org';
50
50
  exports.tokenToDevGatewayMap = {
51
51
  arweave: 'https://turbo-gateway.com', // No arweave test net
52
52
  ario: 'https://api.devnet.solana.com',
53
- 'base-ario': baseMainnetRpc, // No base-ario test net contract deployed
54
53
  solana: 'https://api.devnet.solana.com',
55
54
  ethereum: ethTestnetRpc,
56
55
  'base-eth': baseTestnetRpc,
@@ -64,7 +63,6 @@ exports.tokenToDevGatewayMap = {
64
63
  exports.defaultProdGatewayUrls = {
65
64
  arweave: 'https://turbo-gateway.com',
66
65
  ario: 'https://api.mainnet-beta.solana.com',
67
- 'base-ario': baseMainnetRpc,
68
66
  solana: 'https://api.mainnet-beta.solana.com',
69
67
  ethereum: 'https://cloudflare-eth.com/',
70
68
  'base-eth': baseMainnetRpc,
@@ -102,7 +100,6 @@ function createTurboSigner({ signer: clientProvidedSigner, privateKey: clientPro
102
100
  case 'base-eth':
103
101
  case 'usdc':
104
102
  case 'base-usdc':
105
- case 'base-ario':
106
103
  case 'polygon-usdc':
107
104
  if (!(0, types_js_1.isEthPrivateKey)(clientProvidedPrivateKey)) {
108
105
  throw new Error('A valid Ethereum private key must be provided for EthereumSigner.');
@@ -167,7 +164,6 @@ function isValidUserAddress(address, type) {
167
164
  case 'matic':
168
165
  case 'pol':
169
166
  case 'base-usdc':
170
- case 'base-ario':
171
167
  case 'usdc':
172
168
  case 'polygon-usdc':
173
169
  return isValidECDSAAddress(address);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AbortError = exports.InsufficientCreditsError = exports.ProvidedInputError = exports.FailedRequestError = exports.UnauthenticatedRequestError = exports.BaseError = void 0;
3
+ exports.AbortError = exports.FiatPaymentsDisabledError = exports.InsufficientCreditsError = exports.ProvidedInputError = exports.FailedRequestError = exports.UnauthenticatedRequestError = exports.BaseError = void 0;
4
4
  /**
5
5
  * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
6
6
  *
@@ -60,6 +60,25 @@ class InsufficientCreditsError extends BaseError {
60
60
  }
61
61
  }
62
62
  exports.InsufficientCreditsError = InsufficientCreditsError;
63
+ /**
64
+ * Raised when the payment service has fiat (Stripe) payments switched off, which
65
+ * it signals with `503` and the body "Fiat (Stripe) ArNS payments are disabled".
66
+ * This is a normal state in the testnet sandbox, not an outage.
67
+ *
68
+ * Distinguished from a generic `503` deliberately: the same status is also used
69
+ * for internal errors (body "Internal Server Error: ..."), so status alone is
70
+ * ambiguous. Recovery: fall back to the credit-paid path (`buyArNSName` and
71
+ * friends), or surface fiat checkout as unavailable.
72
+ */
73
+ class FiatPaymentsDisabledError extends BaseError {
74
+ constructor(message) {
75
+ super(message ??
76
+ 'Fiat (Stripe) payments are disabled on this payment service. Use the credit-paid purchase path instead.');
77
+ /** Always the HTTP status that produced this error. */
78
+ this.status = 503;
79
+ }
80
+ }
81
+ exports.FiatPaymentsDisabledError = FiatPaymentsDisabledError;
63
82
  class AbortError extends BaseError {
64
83
  constructor(message = 'Request was aborted') {
65
84
  super(message);
@@ -20,7 +20,7 @@ import { DataItem } from '@dha-team/arbundles';
20
20
  import { program } from 'commander';
21
21
  import { readFileSync, readdirSync } from 'fs';
22
22
  import { version } from '../version.js';
23
- import { arnsPrice, arnsPurchaseStatus, buyArNSName, extendArNSLease, increaseArNSUndernames, removeArNSRecord, setArNSRecord, transferArNSAnt, upgradeArNSName, } from './commands/arns.js';
23
+ import { arnsFiatQuote, arnsPrice, arnsPurchaseStatus, buyArNSName, extendArNSLease, increaseArNSUndernames, removeArNSRecord, setArNSRecord, transferArNSAnt, upgradeArNSName, } from './commands/arns.js';
24
24
  import { fiatEstimate } from './commands/fiatEstimate.js';
25
25
  import { balance, cryptoFund, freeStatus, paymentHistory, price, topUp, uploadFile, uploadFolder, } from './commands/index.js';
26
26
  import { listShares } from './commands/listShares.js';
@@ -28,7 +28,7 @@ import { revokeCredits } from './commands/revokeCredits.js';
28
28
  import { shareCredits } from './commands/shareCredits.js';
29
29
  import { tokenPrice } from './commands/tokenPrice.js';
30
30
  import { x402UploadUnsignedFile } from './commands/x402UploadUnsignedData.js';
31
- import { arnsPriceOptions, arnsPurchaseStatusOptions, buyArNSNameOptions, extendArNSLeaseOptions, globalOptions, increaseArNSUndernamesOptions, listSharesOptions, optionMap, removeArNSRecordOptions, revokeCreditsOptions, setArNSRecordOptions, shareCreditsOptions, transferArNSAntOptions, upgradeArNSNameOptions, uploadFileOptions, uploadFolderOptions, walletOptions, } from './options.js';
31
+ import { arnsFiatQuoteOptions, arnsPriceOptions, arnsPurchaseStatusOptions, buyArNSNameOptions, extendArNSLeaseOptions, globalOptions, increaseArNSUndernamesOptions, listSharesOptions, optionMap, removeArNSRecordOptions, revokeCreditsOptions, setArNSRecordOptions, shareCreditsOptions, transferArNSAntOptions, upgradeArNSNameOptions, uploadFileOptions, uploadFolderOptions, walletOptions, } from './options.js';
32
32
  import { applyOptions, runCommand } from './utils.js';
33
33
  applyOptions(program
34
34
  .name('turbo')
@@ -100,6 +100,11 @@ applyOptions(program
100
100
  .description('Get the Turbo Credit (winc + mARIO) price to buy, extend, increase undernames, or upgrade an ArNS name'), arnsPriceOptions).action(async (_commandOptions, command) => {
101
101
  await runCommand(command, arnsPrice);
102
102
  });
103
+ applyOptions(program
104
+ .command('arns-fiat-quote')
105
+ .description('Quote an ArNS purchase paid by credit card (Stripe) - no credits needed'), arnsFiatQuoteOptions).action(async (_commandOptions, command) => {
106
+ await runCommand(command, arnsFiatQuote);
107
+ });
103
108
  applyOptions(program
104
109
  .command('buy-arns-name')
105
110
  .description('Buy an ArNS name (lease or permabuy) with Turbo Credits'), buyArNSNameOptions).action(async (_commandOptions, command) => {
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import { BigNumber } from 'bignumber.js';
17
17
  import { TurboFactory } from '../../node/factory.js';
18
- import { InsufficientCreditsError } from '../../utils/errors.js';
18
+ import { FiatPaymentsDisabledError, InsufficientCreditsError, } from '../../utils/errors.js';
19
19
  import { wincPerCredit } from '../constants.js';
20
20
  import { configFromOptions, turboFromOptions } from '../utils.js';
21
21
  export function requiredNameFromOptions(options) {
@@ -130,6 +130,45 @@ export async function arnsPrice(options, turbo) {
130
130
  mARIO,
131
131
  }, null, 2));
132
132
  }
133
+ export async function arnsFiatQuote(options, turbo) {
134
+ const params = arnsPriceParamsFromOptions(options);
135
+ const address = options.address;
136
+ if (address === undefined) {
137
+ throw new Error('A destination --address is required for a fiat ArNS quote.');
138
+ }
139
+ const client = turbo ?? TurboFactory.unauthenticated(configFromOptions(options));
140
+ try {
141
+ const { purchaseQuote, paymentSession, adjustments, fees } = await client.getArNSFiatPurchaseQuote({
142
+ ...params,
143
+ address,
144
+ currency: (options.currency ?? 'usd'),
145
+ method: options.method,
146
+ promoCodes: options.promoCode,
147
+ });
148
+ console.log(JSON.stringify({
149
+ name: purchaseQuote.name,
150
+ intent: purchaseQuote.intent,
151
+ nonce: purchaseQuote.nonce,
152
+ paymentAmount: purchaseQuote.paymentAmount,
153
+ currency: purchaseQuote.currencyType,
154
+ quoteExpirationDate: purchaseQuote.quoteExpirationDate,
155
+ paymentSessionId: paymentSession.id,
156
+ // Present for a payment intent / embedded checkout; a hosted
157
+ // checkout session returns `url` instead.
158
+ clientSecret: paymentSession.client_secret ?? undefined,
159
+ checkoutUrl: paymentSession.url ?? undefined,
160
+ adjustments,
161
+ fees,
162
+ }, null, 2));
163
+ }
164
+ catch (error) {
165
+ if (error instanceof FiatPaymentsDisabledError) {
166
+ console.error('Fiat (Stripe) payments are disabled on this payment service. Use the credit-paid commands (buy-arns-name, extend-arns-lease, ...) instead.');
167
+ throw error;
168
+ }
169
+ throw error;
170
+ }
171
+ }
133
172
  export async function buyArNSName(options, turbo) {
134
173
  const name = requiredNameFromOptions(options);
135
174
  const type = typeFromOptions(options.type);
@@ -301,6 +301,23 @@ export const arnsPriceOptions = [
301
301
  optionMap.arnsIncreaseQty,
302
302
  optionMap.arnsProcessId,
303
303
  ];
304
+ export const arnsFiatQuoteOptions = [
305
+ optionMap.arnsName,
306
+ optionMap.arnsType,
307
+ optionMap.arnsYears,
308
+ optionMap.arnsIncreaseQty,
309
+ optionMap.arnsProcessId,
310
+ optionMap.address,
311
+ optionMap.currency,
312
+ {
313
+ alias: '--method <method>',
314
+ description: 'Stripe integration mode: payment-intent (default) or checkout-session',
315
+ },
316
+ {
317
+ alias: '--promo-code <promoCode...>',
318
+ description: 'Promo code(s) to apply to the fiat price',
319
+ },
320
+ ];
304
321
  export const buyArNSNameOptions = [
305
322
  ...walletOptions,
306
323
  optionMap.arnsName,
@@ -30,6 +30,18 @@ const defaultHeaders = {
30
30
  'x-turbo-source-version': version,
31
31
  'x-turbo-source-identifier': 'turbo-sdk',
32
32
  };
33
+ /**
34
+ * Canonical x402 upload routes on the upload service.
35
+ *
36
+ * These were previously `/x402/data-item/{signed,unsigned}`. The service
37
+ * renamed them to `/x402/upload/*` and kept a `data-item/signed` alias, but not
38
+ * a `data-item/unsigned` one — so every unsigned x402 upload 404ed. Both
39
+ * canonical paths are served by all released upload-service versions.
40
+ */
41
+ export const x402UploadEndpoints = {
42
+ signed: '/x402/upload/signed',
43
+ unsigned: '/x402/upload/unsigned',
44
+ };
33
45
  export class TurboHTTPService {
34
46
  constructor({ url, logger, retryConfig = defaultRetryConfig(logger), }) {
35
47
  this.logger = logger;
@@ -120,7 +132,9 @@ export class TurboHTTPService {
120
132
  throw new FailedRequestError('Max retries reached - ' + lastError?.message, lastError?.status);
121
133
  }
122
134
  async x402Post({ signal, allowedStatuses, headers, data, x402Options, }) {
123
- const endpoint = '/x402/data-item/' + (x402Options.unsignedData ? 'unsigned' : 'signed');
135
+ const endpoint = x402Options.unsignedData
136
+ ? x402UploadEndpoints.unsigned
137
+ : x402UploadEndpoints.signed;
124
138
  this.logger.debug('Using X402 options for POST request', {
125
139
  endpoint,
126
140
  x402Options,