@ardrive/turbo-sdk 1.41.3 → 1.42.0-alpha.10

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 (97) hide show
  1. package/lib/cjs/cli/cli.js +61 -0
  2. package/lib/cjs/cli/commands/arns.js +317 -0
  3. package/lib/cjs/cli/commands/freeStatus.js +47 -0
  4. package/lib/cjs/cli/commands/index.js +3 -0
  5. package/lib/cjs/cli/commands/paymentHistory.js +25 -0
  6. package/lib/cjs/cli/options.js +124 -11
  7. package/lib/cjs/cli/utils.js +3 -19
  8. package/lib/cjs/common/arnsActions.js +102 -0
  9. package/lib/cjs/common/factory.js +1 -3
  10. package/lib/cjs/common/http.js +20 -5
  11. package/lib/cjs/common/index.js +6 -0
  12. package/lib/cjs/common/payment.js +482 -3
  13. package/lib/cjs/common/signer.js +27 -6
  14. package/lib/cjs/common/token/ario.js +104 -83
  15. package/lib/cjs/common/token/index.js +0 -11
  16. package/lib/cjs/common/token/solana.js +1 -0
  17. package/lib/cjs/common/turbo.js +131 -0
  18. package/lib/cjs/common/upload.js +3 -2
  19. package/lib/cjs/node/factory.js +1 -3
  20. package/lib/cjs/types.js +44 -3
  21. package/lib/cjs/utils/common.js +5 -21
  22. package/lib/cjs/utils/errors.js +38 -1
  23. package/lib/cjs/utils/uuid.js +31 -0
  24. package/lib/cjs/web/factory.js +1 -3
  25. package/lib/cjs/web/signer.js +4 -2
  26. package/lib/esm/cli/cli.js +63 -2
  27. package/lib/esm/cli/commands/arns.js +300 -0
  28. package/lib/esm/cli/commands/freeStatus.js +44 -0
  29. package/lib/esm/cli/commands/index.js +3 -0
  30. package/lib/esm/cli/commands/paymentHistory.js +22 -0
  31. package/lib/esm/cli/options.js +123 -10
  32. package/lib/esm/cli/utils.js +4 -20
  33. package/lib/esm/common/arnsActions.js +93 -0
  34. package/lib/esm/common/factory.js +1 -3
  35. package/lib/esm/common/http.js +19 -4
  36. package/lib/esm/common/index.js +6 -0
  37. package/lib/esm/common/payment.js +482 -3
  38. package/lib/esm/common/signer.js +28 -7
  39. package/lib/esm/common/token/ario.js +102 -84
  40. package/lib/esm/common/token/index.js +1 -12
  41. package/lib/esm/common/token/solana.js +1 -0
  42. package/lib/esm/common/turbo.js +131 -0
  43. package/lib/esm/common/upload.js +4 -3
  44. package/lib/esm/node/factory.js +1 -3
  45. package/lib/esm/types.js +43 -2
  46. package/lib/esm/utils/common.js +4 -20
  47. package/lib/esm/utils/errors.js +35 -0
  48. package/lib/esm/utils/uuid.js +28 -0
  49. package/lib/esm/web/factory.js +1 -3
  50. package/lib/esm/web/signer.js +4 -2
  51. package/lib/types/cli/commands/arns.d.ts +86 -0
  52. package/lib/types/cli/commands/arns.d.ts.map +1 -0
  53. package/lib/types/cli/commands/freeStatus.d.ts +3 -0
  54. package/lib/types/cli/commands/freeStatus.d.ts.map +1 -0
  55. package/lib/types/cli/commands/index.d.ts +3 -0
  56. package/lib/types/cli/commands/index.d.ts.map +1 -1
  57. package/lib/types/cli/commands/paymentHistory.d.ts +18 -0
  58. package/lib/types/cli/commands/paymentHistory.d.ts.map +1 -0
  59. package/lib/types/cli/options.d.ts +238 -10
  60. package/lib/types/cli/options.d.ts.map +1 -1
  61. package/lib/types/cli/types.d.ts +41 -2
  62. package/lib/types/cli/types.d.ts.map +1 -1
  63. package/lib/types/cli/utils.d.ts.map +1 -1
  64. package/lib/types/common/arnsActions.d.ts +43 -0
  65. package/lib/types/common/arnsActions.d.ts.map +1 -0
  66. package/lib/types/common/factory.d.ts +1 -1
  67. package/lib/types/common/factory.d.ts.map +1 -1
  68. package/lib/types/common/http.d.ts +14 -1
  69. package/lib/types/common/http.d.ts.map +1 -1
  70. package/lib/types/common/index.d.ts +2 -0
  71. package/lib/types/common/index.d.ts.map +1 -1
  72. package/lib/types/common/payment.d.ts +230 -1
  73. package/lib/types/common/payment.d.ts.map +1 -1
  74. package/lib/types/common/signer.d.ts +12 -6
  75. package/lib/types/common/signer.d.ts.map +1 -1
  76. package/lib/types/common/token/ario.d.ts +13 -9
  77. package/lib/types/common/token/ario.d.ts.map +1 -1
  78. package/lib/types/common/token/index.d.ts.map +1 -1
  79. package/lib/types/common/token/solana.d.ts.map +1 -1
  80. package/lib/types/common/turbo.d.ts +140 -1
  81. package/lib/types/common/turbo.d.ts.map +1 -1
  82. package/lib/types/common/upload.d.ts.map +1 -1
  83. package/lib/types/node/factory.d.ts +1 -1
  84. package/lib/types/node/factory.d.ts.map +1 -1
  85. package/lib/types/types.d.ts +483 -7
  86. package/lib/types/types.d.ts.map +1 -1
  87. package/lib/types/utils/common.d.ts +0 -12
  88. package/lib/types/utils/common.d.ts.map +1 -1
  89. package/lib/types/utils/errors.d.ts +29 -0
  90. package/lib/types/utils/errors.d.ts.map +1 -1
  91. package/lib/types/utils/uuid.d.ts +7 -0
  92. package/lib/types/utils/uuid.d.ts.map +1 -0
  93. package/lib/types/web/factory.d.ts +1 -1
  94. package/lib/types/web/factory.d.ts.map +1 -1
  95. package/lib/types/web/signer.d.ts +1 -1
  96. package/lib/types/web/signer.d.ts.map +1 -1
  97. package/package.json +3 -2
@@ -124,7 +124,9 @@ async function privateKeyFromOptions({ mnemonic, privateKey, walletFile, token,
124
124
  }
125
125
  else if (walletFile !== undefined) {
126
126
  const wallet = JSON.parse((0, fs_1.readFileSync)(walletFile, 'utf-8'));
127
- return token === 'solana' ? bs58_1.default.encode(wallet) : wallet;
127
+ return token === 'solana' || token === 'ario'
128
+ ? bs58_1.default.encode(wallet)
129
+ : wallet;
128
130
  }
129
131
  else if (privateKey !== undefined) {
130
132
  return privateKey;
@@ -138,8 +140,6 @@ function configFromOptions(options) {
138
140
  let paymentUrl = undefined;
139
141
  let uploadUrl = undefined;
140
142
  let gatewayUrl = undefined;
141
- let processId = undefined;
142
- let cuUrl = undefined;
143
143
  if (options.local && options.dev) {
144
144
  throw new Error('Cannot use both --local and --dev flags');
145
145
  }
@@ -148,10 +148,6 @@ function configFromOptions(options) {
148
148
  paymentUrl = index_js_1.developmentTurboConfiguration.paymentServiceConfig.url;
149
149
  uploadUrl = index_js_1.developmentTurboConfiguration.uploadServiceConfig.url;
150
150
  gatewayUrl = common_js_1.tokenToDevGatewayMap[token];
151
- if (options.token === 'ario') {
152
- processId = common_js_1.tokenToDevAoConfigMap[token].processId;
153
- cuUrl = common_js_1.tokenToDevAoConfigMap[token].cuUrl;
154
- }
155
151
  }
156
152
  else if (options.local) {
157
153
  // Use local endpoints
@@ -163,10 +159,6 @@ function configFromOptions(options) {
163
159
  // Use default endpoints
164
160
  paymentUrl = index_js_1.defaultTurboConfiguration.paymentServiceConfig.url;
165
161
  uploadUrl = index_js_1.defaultTurboConfiguration.uploadServiceConfig.url;
166
- if (options.token === 'ario') {
167
- processId = common_js_1.defaultProdAoConfigs[token].processId;
168
- cuUrl = common_js_1.defaultProdAoConfigs[token].cuUrl;
169
- }
170
162
  }
171
163
  // Override gateway, payment, and upload service default endpoints if provided
172
164
  if (options.gateway !== undefined) {
@@ -178,19 +170,11 @@ function configFromOptions(options) {
178
170
  if (options.uploadUrl !== undefined) {
179
171
  uploadUrl = options.uploadUrl;
180
172
  }
181
- if (options.cuUrl !== undefined) {
182
- cuUrl = options.cuUrl;
183
- }
184
- if (options.processId !== undefined) {
185
- processId = options.processId;
186
- }
187
173
  const config = {
188
174
  paymentServiceConfig: { url: paymentUrl },
189
175
  uploadServiceConfig: { url: uploadUrl },
190
176
  gatewayUrl,
191
177
  token,
192
- processId,
193
- cuUrl,
194
178
  };
195
179
  return config;
196
180
  }
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildArNSCustodyMessage = buildArNSCustodyMessage;
7
+ exports.arNSOwnerProofHeaders = arNSOwnerProofHeaders;
8
+ exports.solanaOwnerSigner = solanaOwnerSigner;
9
+ exports.emptySignatureSlots = emptySignatureSlots;
10
+ /**
11
+ * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
12
+ *
13
+ * Licensed under the Apache License, Version 2.0 (the "License");
14
+ * you may not use this file except in compliance with the License.
15
+ * You may obtain a copy of the License at
16
+ *
17
+ * http://www.apache.org/licenses/LICENSE-2.0
18
+ *
19
+ * Unless required by applicable law or agreed to in writing, software
20
+ * distributed under the License is distributed on an "AS IS" BASIS,
21
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22
+ * See the License for the specific language governing permissions and
23
+ * limitations under the License.
24
+ */
25
+ const web3_js_1 = require("@solana/web3.js");
26
+ const bs58_1 = __importDefault(require("bs58"));
27
+ const tweetnacl_1 = __importDefault(require("tweetnacl"));
28
+ const base64_js_1 = require("../utils/base64.js");
29
+ /**
30
+ * Canonical ACTION-BOUND message for the owner proof.
31
+ *
32
+ * MUST match the bundler's `buildArNSCustodyMessage` byte for byte
33
+ * (newline-delimited) or every signature is rejected. The bundler rebuilds this
34
+ * from the request and verifies the signature over `message + nonce`, so a
35
+ * signature captured for one operation cannot authorize a different one, and
36
+ * the nonce is consumed on use so the same one cannot be replayed (e.g. to
37
+ * revert a record to an older value).
38
+ */
39
+ function buildArNSCustodyMessage(action, fields) {
40
+ return ['arns', action, ...fields].join('\n');
41
+ }
42
+ /** Solana's signature-type discriminator in Turbo's signed-request scheme. */
43
+ const SOLANA_SIGNATURE_TYPE = 4;
44
+ /**
45
+ * The ANT owner's half of the envelope, in its own `x-owner-*` headers.
46
+ *
47
+ * Two signatures travel on a record action — the PAYER's signed request over
48
+ * `"" + nonce`, and the OWNER's action-bound proof over `message + nonce` —
49
+ * from two different keys, usually on two different chains. They cannot share
50
+ * one header set: whichever verifier ran second would reject a signature that
51
+ * was never meant for it.
52
+ */
53
+ async function arNSOwnerProofHeaders(owner, message, nonce) {
54
+ const address = await owner.getAddress();
55
+ const signature = await owner.signMessage(Uint8Array.from(Buffer.from(message + nonce)));
56
+ return {
57
+ // A Solana address IS the base58-encoded 32-byte ed25519 public key, so
58
+ // decoding the address recovers exactly the bytes the bundler verifies
59
+ // against. Keeps ArNSOwnerSigner minimal — no separate getPublicKey().
60
+ 'x-owner-public-key': (0, base64_js_1.toB64Url)(Buffer.from(bs58_1.default.decode(address))),
61
+ 'x-owner-nonce': nonce,
62
+ 'x-owner-signature': (0, base64_js_1.toB64Url)(Buffer.from(signature)),
63
+ 'x-owner-signature-type': String(SOLANA_SIGNATURE_TYPE),
64
+ };
65
+ }
66
+ /**
67
+ * Build an {@link ArNSOwnerSigner} from a raw Solana secret key.
68
+ *
69
+ * For servers and tests. A browser wallet (Phantom, Solflare, or a console's
70
+ * embedded wallet) should implement the interface directly against its own
71
+ * `signTransaction` / `signMessage` rather than exposing a secret key.
72
+ *
73
+ * Note the owner needs a key to SIGN with, not a funded account: Turbo is the
74
+ * fee payer on every sponsored action, so this wallet's SOL balance can stay
75
+ * at zero for the entire life of the name.
76
+ */
77
+ function solanaOwnerSigner(secretKey) {
78
+ const keypair = web3_js_1.Keypair.fromSecretKey(typeof secretKey === 'string' ? bs58_1.default.decode(secretKey) : secretKey);
79
+ return {
80
+ getAddress: () => keypair.publicKey.toBase58(),
81
+ signTransaction: async (transactionBase64) => {
82
+ const tx = web3_js_1.VersionedTransaction.deserialize(Buffer.from(transactionBase64, 'base64'));
83
+ // Sign the bytes as returned. Turbo's fee-payer signature already covers
84
+ // this exact message; rebuilding it from parts invalidates that and the
85
+ // submission is rejected.
86
+ tx.sign([keypair]);
87
+ return Buffer.from(tx.serialize()).toString('base64');
88
+ },
89
+ signMessage: async (message) => tweetnacl_1.default.sign.detached(message, keypair.secretKey),
90
+ };
91
+ }
92
+ /**
93
+ * Count the signature slots a prepared transaction still has empty.
94
+ *
95
+ * Turbo pre-signs as fee payer and leaves exactly one slot for the owner, so a
96
+ * client can assert that before prompting a wallet — a cheap way to catch a
97
+ * malformed or already-signed transaction before bothering the user.
98
+ */
99
+ function emptySignatureSlots(transactionBase64) {
100
+ const tx = web3_js_1.VersionedTransaction.deserialize(Buffer.from(transactionBase64, 'base64'));
101
+ return tx.signatures.filter((sig) => sig.every((byte) => byte === 0)).length;
102
+ }
@@ -46,7 +46,7 @@ class TurboBaseFactory {
46
46
  paymentService,
47
47
  });
48
48
  }
49
- getAuthenticatedTurbo({ privateKey, signer: providedSigner, paymentServiceConfig = {}, uploadServiceConfig = {}, token, gatewayUrl, tokenMap, tokenTools, logger, walletAdapter, processId, cuUrl, }) {
49
+ getAuthenticatedTurbo({ privateKey, signer: providedSigner, paymentServiceConfig = {}, uploadServiceConfig = {}, token, gatewayUrl, tokenMap, tokenTools, logger, walletAdapter, }) {
50
50
  token = token === 'pol' ? 'matic' : token;
51
51
  if (!token) {
52
52
  if (providedSigner) {
@@ -104,8 +104,6 @@ class TurboBaseFactory {
104
104
  tokenTools = tokenMap.arweave;
105
105
  }
106
106
  tokenTools = index_js_1.defaultTokenMap[token]?.({
107
- cuUrl,
108
- processId,
109
107
  gatewayUrl,
110
108
  logger,
111
109
  });
@@ -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;
@@ -47,7 +59,7 @@ class TurboHTTPService {
47
59
  signal,
48
60
  }), allowedStatuses);
49
61
  }
50
- async post({ endpoint, signal, allowedStatuses = [200, 202], headers, data, x402Options, }) {
62
+ async post({ endpoint, signal, allowedStatuses = [200, 202], headers, data, x402Options, retry = true, }) {
51
63
  if (x402Options !== undefined) {
52
64
  return this.x402Post({
53
65
  signal,
@@ -59,9 +71,10 @@ class TurboHTTPService {
59
71
  }
60
72
  // Convert all data types to fetch-compatible body
61
73
  const { body, duplex } = await toFetchBody(data);
62
- // Use retry for Buffer/Uint8Array, tryRequest for streams
74
+ // Use retry for Buffer/Uint8Array, tryRequest for streams. Callers can opt
75
+ // out of retry for non-idempotent signed writes via `retry: false`.
63
76
  const isReusableData = data instanceof Buffer || data instanceof Uint8Array;
64
- const requestFn = isReusableData
77
+ const requestFn = isReusableData && retry
65
78
  ? this.withRetry.bind(this)
66
79
  : this.tryRequest.bind(this);
67
80
  return requestFn(() => fetch(this.baseURL + endpoint, {
@@ -123,7 +136,9 @@ class TurboHTTPService {
123
136
  throw new errors_js_1.FailedRequestError('Max retries reached - ' + lastError?.message, lastError?.status);
124
137
  }
125
138
  async x402Post({ signal, allowedStatuses, headers, data, x402Options, }) {
126
- const endpoint = '/x402/data-item/' + (x402Options.unsignedData ? 'unsigned' : 'signed');
139
+ const endpoint = x402Options.unsignedData
140
+ ? exports.x402UploadEndpoints.unsigned
141
+ : exports.x402UploadEndpoints.signed;
127
142
  this.logger.debug('Using X402 options for POST request', {
128
143
  endpoint,
129
144
  x402Options,
@@ -31,6 +31,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
31
31
  */
32
32
  __exportStar(require("./upload.js"), exports);
33
33
  __exportStar(require("./payment.js"), exports);
34
+ // The ANT-owner helpers. Without this the documented
35
+ // `import { solanaOwnerSigner } from '@ardrive/turbo-sdk'` does not resolve.
36
+ __exportStar(require("./arnsActions.js"), exports);
34
37
  __exportStar(require("./turbo.js"), exports);
35
38
  __exportStar(require("./currency.js"), exports);
36
39
  __exportStar(require("./token/index.js"), exports);
40
+ // Typed errors so consumers can `catch (e) { if (e instanceof InsufficientCreditsError) ... }`
41
+ // e.g. to prompt a top-up on a 402 ArNS purchase.
42
+ __exportStar(require("../utils/errors.js"), exports);