@ardrive/turbo-sdk 1.42.0 → 1.43.0-alpha.2
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/lib/cjs/cli/cli.js +97 -0
- package/lib/cjs/cli/commands/arns.js +475 -0
- package/lib/cjs/cli/commands/freeStatus.js +47 -0
- package/lib/cjs/cli/commands/index.js +3 -0
- package/lib/cjs/cli/commands/paymentHistory.js +25 -0
- package/lib/cjs/cli/options.js +202 -1
- package/lib/cjs/common/arnsActions.js +125 -0
- package/lib/cjs/common/chunked.js +51 -1
- package/lib/cjs/common/folderIndex.js +401 -0
- package/lib/cjs/common/http.js +40 -6
- package/lib/cjs/common/index.js +7 -0
- package/lib/cjs/common/payment.js +548 -2
- package/lib/cjs/common/signer.js +27 -6
- package/lib/cjs/common/token/index.js +0 -11
- package/lib/cjs/common/turbo.js +162 -0
- package/lib/cjs/common/upload.js +314 -31
- package/lib/cjs/node/folderIndex.js +132 -0
- package/lib/cjs/node/index.js +1 -0
- package/lib/cjs/node/upload.js +7 -0
- package/lib/cjs/types.js +53 -3
- package/lib/cjs/utils/common.js +0 -4
- package/lib/cjs/utils/errors.js +38 -1
- package/lib/cjs/utils/folderIndex.js +88 -0
- package/lib/cjs/utils/uuid.js +31 -0
- package/lib/cjs/web/signer.js +4 -2
- package/lib/esm/cli/cli.js +99 -2
- package/lib/esm/cli/commands/arns.js +450 -0
- package/lib/esm/cli/commands/freeStatus.js +44 -0
- package/lib/esm/cli/commands/index.js +3 -0
- package/lib/esm/cli/commands/paymentHistory.js +22 -0
- package/lib/esm/cli/options.js +201 -0
- package/lib/esm/common/arnsActions.js +114 -0
- package/lib/esm/common/chunked.js +51 -1
- package/lib/esm/common/folderIndex.js +396 -0
- package/lib/esm/common/http.js +39 -5
- package/lib/esm/common/index.js +7 -0
- package/lib/esm/common/payment.js +548 -2
- package/lib/esm/common/signer.js +28 -7
- package/lib/esm/common/token/index.js +1 -12
- package/lib/esm/common/turbo.js +162 -0
- package/lib/esm/common/upload.js +316 -33
- package/lib/esm/node/folderIndex.js +129 -0
- package/lib/esm/node/index.js +1 -0
- package/lib/esm/node/upload.js +7 -0
- package/lib/esm/types.js +52 -2
- package/lib/esm/utils/common.js +0 -4
- package/lib/esm/utils/errors.js +35 -0
- package/lib/esm/utils/folderIndex.js +81 -0
- package/lib/esm/utils/uuid.js +28 -0
- package/lib/esm/web/signer.js +4 -2
- package/lib/types/cli/commands/arns.d.ts +147 -0
- package/lib/types/cli/commands/arns.d.ts.map +1 -0
- package/lib/types/cli/commands/freeStatus.d.ts +3 -0
- package/lib/types/cli/commands/freeStatus.d.ts.map +1 -0
- package/lib/types/cli/commands/index.d.ts +3 -0
- package/lib/types/cli/commands/index.d.ts.map +1 -1
- package/lib/types/cli/commands/paymentHistory.d.ts +18 -0
- package/lib/types/cli/commands/paymentHistory.d.ts.map +1 -0
- package/lib/types/cli/options.d.ts +423 -0
- package/lib/types/cli/options.d.ts.map +1 -1
- package/lib/types/cli/types.d.ts +78 -0
- package/lib/types/cli/types.d.ts.map +1 -1
- package/lib/types/common/arnsActions.d.ts +58 -0
- package/lib/types/common/arnsActions.d.ts.map +1 -0
- package/lib/types/common/chunked.d.ts +47 -2
- package/lib/types/common/chunked.d.ts.map +1 -1
- package/lib/types/common/folderIndex.d.ts +54 -0
- package/lib/types/common/folderIndex.d.ts.map +1 -0
- package/lib/types/common/http.d.ts +25 -2
- package/lib/types/common/http.d.ts.map +1 -1
- package/lib/types/common/index.d.ts +3 -0
- package/lib/types/common/index.d.ts.map +1 -1
- package/lib/types/common/payment.d.ts +291 -1
- package/lib/types/common/payment.d.ts.map +1 -1
- package/lib/types/common/signer.d.ts +12 -6
- package/lib/types/common/signer.d.ts.map +1 -1
- package/lib/types/common/token/index.d.ts.map +1 -1
- package/lib/types/common/turbo.d.ts +183 -1
- package/lib/types/common/turbo.d.ts.map +1 -1
- package/lib/types/common/upload.d.ts +41 -0
- package/lib/types/common/upload.d.ts.map +1 -1
- package/lib/types/node/folderIndex.d.ts +28 -0
- package/lib/types/node/folderIndex.d.ts.map +1 -0
- package/lib/types/node/index.d.ts +1 -0
- package/lib/types/node/index.d.ts.map +1 -1
- package/lib/types/node/upload.d.ts +1 -0
- package/lib/types/node/upload.d.ts.map +1 -1
- package/lib/types/types.d.ts +694 -3
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/utils/common.d.ts.map +1 -1
- package/lib/types/utils/errors.d.ts +29 -0
- package/lib/types/utils/errors.d.ts.map +1 -1
- package/lib/types/utils/folderIndex.d.ts +47 -0
- package/lib/types/utils/folderIndex.d.ts.map +1 -0
- package/lib/types/utils/uuid.d.ts +7 -0
- package/lib/types/utils/uuid.d.ts.map +1 -0
- package/lib/types/web/signer.d.ts +1 -1
- package/lib/types/web/signer.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.paymentHistory = paymentHistory;
|
|
4
|
+
const utils_js_1 = require("../utils.js");
|
|
5
|
+
async function paymentHistory(options) {
|
|
6
|
+
// Signature-required and self-scoped: a wallet is mandatory (there is no
|
|
7
|
+
// --address form), and the service returns only this signer's own top-ups.
|
|
8
|
+
const turbo = await (0, utils_js_1.turboFromOptions)(options);
|
|
9
|
+
const limit = options.limit !== undefined ? +options.limit : undefined;
|
|
10
|
+
if (limit !== undefined &&
|
|
11
|
+
(!Number.isInteger(limit) || limit < 1 || limit > 100)) {
|
|
12
|
+
throw new Error('--limit must be an integer between 1 and 100');
|
|
13
|
+
}
|
|
14
|
+
const { payments, hasMore, cursor } = await turbo.getPaymentHistory({
|
|
15
|
+
limit,
|
|
16
|
+
cursor: options.cursor,
|
|
17
|
+
});
|
|
18
|
+
// stdout carries only the JSON page and the next-page hint goes to stderr, so
|
|
19
|
+
// `... | jq` works at the default log level. (--debug intentionally routes SDK
|
|
20
|
+
// logs to stdout and will interleave — expected when you ask for debug output.)
|
|
21
|
+
console.log(JSON.stringify({ payments, hasMore, cursor }, null, 2));
|
|
22
|
+
if (hasMore) {
|
|
23
|
+
console.error(`\nMore results available — fetch the next page with --cursor ${cursor}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
package/lib/cjs/cli/options.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
-
exports.listSharesOptions = exports.revokeCreditsOptions = exports.shareCreditsOptions = exports.uploadFileOptions = exports.uploadFolderOptions = exports.uploadOptions = exports.globalOptions = exports.walletOptions = exports.optionMap = void 0;
|
|
18
|
+
exports.arnsActionPriceOptions = exports.removeArNSRecordMetadataOptions = exports.setArNSRecordMetadataOptions = exports.transferArNSRecordOptions = exports.removeArNSControllerOptions = exports.addArNSControllerOptions = exports.removeArNSRecordOptions = exports.setArNSRecordOptions = exports.transferArNSAntOptions = exports.arnsActionStatusOptions = 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>',
|
|
@@ -189,6 +189,102 @@ exports.optionMap = {
|
|
|
189
189
|
alias: '--max-crypto-top-up-value <maxCryptoTopUpValue>',
|
|
190
190
|
description: 'Maximum crypto top-up value to use for the upload. Defaults to no limit.',
|
|
191
191
|
},
|
|
192
|
+
// ---- ArNS (paid with Turbo Credits) ----
|
|
193
|
+
arnsName: {
|
|
194
|
+
alias: '--name <name>',
|
|
195
|
+
description: 'ArNS name to price, buy, or manage',
|
|
196
|
+
},
|
|
197
|
+
arnsType: {
|
|
198
|
+
alias: '--type <type>',
|
|
199
|
+
description: "ArNS purchase type for Buy-Name: 'lease' or 'permabuy'",
|
|
200
|
+
},
|
|
201
|
+
arnsYears: {
|
|
202
|
+
alias: '--years <years>',
|
|
203
|
+
description: 'Lease duration in years (Buy-Name lease / Extend-Lease)',
|
|
204
|
+
},
|
|
205
|
+
arnsIncreaseQty: {
|
|
206
|
+
alias: '--increase-qty <qty>',
|
|
207
|
+
description: 'Number of additional undernames (Increase-Undername-Limit)',
|
|
208
|
+
},
|
|
209
|
+
arnsOwnerKey: {
|
|
210
|
+
alias: '--owner-key <base58SolanaSecretKey>',
|
|
211
|
+
description: 'Base58 Solana secret key that OWNS the ANT and signs for it. Separate from the wallet paying in Turbo Credits — the owner needs a key to sign with, not SOL: Turbo pays every fee and rent',
|
|
212
|
+
},
|
|
213
|
+
arnsNonce: {
|
|
214
|
+
alias: '--nonce <nonce>',
|
|
215
|
+
description: 'ArNS purchase nonce to look up the status for',
|
|
216
|
+
},
|
|
217
|
+
arnsAntId: {
|
|
218
|
+
alias: '--ant-id <antId>',
|
|
219
|
+
description: 'ANT (Metaplex Core asset) ID to transfer or manage',
|
|
220
|
+
},
|
|
221
|
+
arnsTarget: {
|
|
222
|
+
alias: '--target <address>',
|
|
223
|
+
description: 'Target Solana pubkey to transfer the ANT to',
|
|
224
|
+
},
|
|
225
|
+
arnsUndername: {
|
|
226
|
+
alias: '--undername <undername>',
|
|
227
|
+
description: "ArNS undername record to set or remove (defaults to '@')",
|
|
228
|
+
},
|
|
229
|
+
arnsTransactionId: {
|
|
230
|
+
alias: '--transaction-id <transactionId>',
|
|
231
|
+
description: 'Arweave transaction ID the ArNS record resolves to',
|
|
232
|
+
},
|
|
233
|
+
arnsTtlSeconds: {
|
|
234
|
+
alias: '--ttl-seconds <ttlSeconds>',
|
|
235
|
+
description: 'TTL in seconds for the ArNS record',
|
|
236
|
+
},
|
|
237
|
+
arnsDisplayName: {
|
|
238
|
+
alias: '--display-name <displayName>',
|
|
239
|
+
description: 'Display name for the record (Set-Record-Metadata)',
|
|
240
|
+
},
|
|
241
|
+
arnsClearDisplayName: {
|
|
242
|
+
alias: '--clear-display-name',
|
|
243
|
+
description: 'Clear the record display name (Set-Record-Metadata)',
|
|
244
|
+
default: false,
|
|
245
|
+
},
|
|
246
|
+
arnsRecordLogo: {
|
|
247
|
+
alias: '--record-logo <transactionId>',
|
|
248
|
+
description: 'Arweave transaction ID for the record logo (Set-Record-Metadata)',
|
|
249
|
+
},
|
|
250
|
+
arnsClearRecordLogo: {
|
|
251
|
+
alias: '--clear-record-logo',
|
|
252
|
+
description: 'Clear the record logo (Set-Record-Metadata)',
|
|
253
|
+
default: false,
|
|
254
|
+
},
|
|
255
|
+
arnsRecordDescription: {
|
|
256
|
+
alias: '--record-description <description>',
|
|
257
|
+
description: 'Description for the record (Set-Record-Metadata)',
|
|
258
|
+
},
|
|
259
|
+
arnsClearRecordDescription: {
|
|
260
|
+
alias: '--clear-record-description',
|
|
261
|
+
description: 'Clear the record description (Set-Record-Metadata)',
|
|
262
|
+
default: false,
|
|
263
|
+
},
|
|
264
|
+
arnsRecordKeywords: {
|
|
265
|
+
alias: '--record-keywords <keywords...>',
|
|
266
|
+
description: 'Keywords for the record (Set-Record-Metadata)',
|
|
267
|
+
type: 'array',
|
|
268
|
+
},
|
|
269
|
+
arnsClearRecordKeywords: {
|
|
270
|
+
alias: '--clear-record-keywords',
|
|
271
|
+
description: 'Clear the record keywords (Set-Record-Metadata)',
|
|
272
|
+
default: false,
|
|
273
|
+
},
|
|
274
|
+
arnsAction: {
|
|
275
|
+
alias: '--action <action>',
|
|
276
|
+
description: 'ArNS action to price: set-record, remove-record, set-record-metadata, ' +
|
|
277
|
+
'remove-record-metadata, transfer-record, add-controller, remove-controller, or transfer',
|
|
278
|
+
},
|
|
279
|
+
// ---- Payment history ----
|
|
280
|
+
limit: {
|
|
281
|
+
alias: '--limit <limit>',
|
|
282
|
+
description: 'Max number of payment-history rows to return (1-100, default 50)',
|
|
283
|
+
},
|
|
284
|
+
cursor: {
|
|
285
|
+
alias: '--cursor <cursor>',
|
|
286
|
+
description: 'Opaque pagination cursor from a prior payment-history response',
|
|
287
|
+
},
|
|
192
288
|
};
|
|
193
289
|
exports.walletOptions = [
|
|
194
290
|
exports.optionMap.walletFile,
|
|
@@ -242,3 +338,108 @@ exports.shareCreditsOptions = [
|
|
|
242
338
|
];
|
|
243
339
|
exports.revokeCreditsOptions = [...exports.walletOptions, exports.optionMap.address];
|
|
244
340
|
exports.listSharesOptions = exports.revokeCreditsOptions;
|
|
341
|
+
// ---- ArNS command option sets (token + --payment-url come from globalOptions) ----
|
|
342
|
+
exports.arnsPriceOptions = [
|
|
343
|
+
exports.optionMap.arnsName,
|
|
344
|
+
exports.optionMap.arnsType,
|
|
345
|
+
exports.optionMap.arnsYears,
|
|
346
|
+
exports.optionMap.arnsIncreaseQty,
|
|
347
|
+
];
|
|
348
|
+
exports.arnsFiatQuoteOptions = [
|
|
349
|
+
exports.optionMap.arnsName,
|
|
350
|
+
exports.optionMap.arnsType,
|
|
351
|
+
exports.optionMap.arnsYears,
|
|
352
|
+
exports.optionMap.arnsIncreaseQty,
|
|
353
|
+
exports.optionMap.address,
|
|
354
|
+
exports.optionMap.currency,
|
|
355
|
+
{
|
|
356
|
+
alias: '--method <method>',
|
|
357
|
+
description: 'Stripe integration mode: payment-intent (default) or checkout-session',
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
alias: '--promo-code <promoCode...>',
|
|
361
|
+
description: 'Promo code(s) to apply to the fiat price',
|
|
362
|
+
},
|
|
363
|
+
];
|
|
364
|
+
exports.buyArNSNameOptions = [
|
|
365
|
+
...exports.walletOptions,
|
|
366
|
+
exports.optionMap.arnsName,
|
|
367
|
+
exports.optionMap.arnsType,
|
|
368
|
+
exports.optionMap.arnsYears,
|
|
369
|
+
exports.optionMap.arnsOwnerKey,
|
|
370
|
+
exports.optionMap.paidBy,
|
|
371
|
+
];
|
|
372
|
+
exports.extendArNSLeaseOptions = [
|
|
373
|
+
...exports.walletOptions,
|
|
374
|
+
exports.optionMap.arnsName,
|
|
375
|
+
exports.optionMap.arnsYears,
|
|
376
|
+
exports.optionMap.paidBy,
|
|
377
|
+
];
|
|
378
|
+
exports.increaseArNSUndernamesOptions = [
|
|
379
|
+
...exports.walletOptions,
|
|
380
|
+
exports.optionMap.arnsName,
|
|
381
|
+
exports.optionMap.arnsIncreaseQty,
|
|
382
|
+
exports.optionMap.paidBy,
|
|
383
|
+
];
|
|
384
|
+
exports.upgradeArNSNameOptions = [
|
|
385
|
+
...exports.walletOptions,
|
|
386
|
+
exports.optionMap.arnsName,
|
|
387
|
+
exports.optionMap.paidBy,
|
|
388
|
+
];
|
|
389
|
+
exports.arnsPurchaseStatusOptions = [exports.optionMap.arnsNonce];
|
|
390
|
+
exports.arnsActionStatusOptions = [...exports.walletOptions, exports.optionMap.arnsNonce];
|
|
391
|
+
exports.transferArNSAntOptions = [
|
|
392
|
+
...exports.walletOptions,
|
|
393
|
+
exports.optionMap.arnsOwnerKey,
|
|
394
|
+
exports.optionMap.arnsAntId,
|
|
395
|
+
exports.optionMap.arnsTarget,
|
|
396
|
+
];
|
|
397
|
+
exports.setArNSRecordOptions = [
|
|
398
|
+
...exports.walletOptions,
|
|
399
|
+
exports.optionMap.arnsOwnerKey,
|
|
400
|
+
exports.optionMap.arnsAntId,
|
|
401
|
+
exports.optionMap.arnsUndername,
|
|
402
|
+
exports.optionMap.arnsTransactionId,
|
|
403
|
+
exports.optionMap.arnsTtlSeconds,
|
|
404
|
+
];
|
|
405
|
+
exports.removeArNSRecordOptions = [
|
|
406
|
+
...exports.walletOptions,
|
|
407
|
+
exports.optionMap.arnsOwnerKey,
|
|
408
|
+
exports.optionMap.arnsAntId,
|
|
409
|
+
exports.optionMap.arnsUndername,
|
|
410
|
+
];
|
|
411
|
+
exports.addArNSControllerOptions = [
|
|
412
|
+
...exports.walletOptions,
|
|
413
|
+
exports.optionMap.arnsOwnerKey,
|
|
414
|
+
exports.optionMap.arnsAntId,
|
|
415
|
+
exports.optionMap.arnsTarget,
|
|
416
|
+
];
|
|
417
|
+
exports.removeArNSControllerOptions = exports.addArNSControllerOptions;
|
|
418
|
+
exports.transferArNSRecordOptions = [
|
|
419
|
+
...exports.walletOptions,
|
|
420
|
+
exports.optionMap.arnsOwnerKey,
|
|
421
|
+
exports.optionMap.arnsAntId,
|
|
422
|
+
exports.optionMap.arnsUndername,
|
|
423
|
+
exports.optionMap.arnsTarget,
|
|
424
|
+
];
|
|
425
|
+
exports.setArNSRecordMetadataOptions = [
|
|
426
|
+
...exports.walletOptions,
|
|
427
|
+
exports.optionMap.arnsOwnerKey,
|
|
428
|
+
exports.optionMap.arnsAntId,
|
|
429
|
+
exports.optionMap.arnsUndername,
|
|
430
|
+
exports.optionMap.arnsDisplayName,
|
|
431
|
+
exports.optionMap.arnsClearDisplayName,
|
|
432
|
+
exports.optionMap.arnsRecordLogo,
|
|
433
|
+
exports.optionMap.arnsClearRecordLogo,
|
|
434
|
+
exports.optionMap.arnsRecordDescription,
|
|
435
|
+
exports.optionMap.arnsClearRecordDescription,
|
|
436
|
+
exports.optionMap.arnsRecordKeywords,
|
|
437
|
+
exports.optionMap.arnsClearRecordKeywords,
|
|
438
|
+
];
|
|
439
|
+
exports.removeArNSRecordMetadataOptions = [
|
|
440
|
+
...exports.walletOptions,
|
|
441
|
+
exports.optionMap.arnsOwnerKey,
|
|
442
|
+
exports.optionMap.arnsAntId,
|
|
443
|
+
exports.optionMap.arnsUndername,
|
|
444
|
+
];
|
|
445
|
+
exports.arnsActionPriceOptions = [exports.optionMap.arnsAction];
|
|
@@ -0,0 +1,125 @@
|
|
|
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.arNSMetadataField = arNSMetadataField;
|
|
8
|
+
exports.arNSKeywordsField = arNSKeywordsField;
|
|
9
|
+
exports.arNSOwnerProofHeaders = arNSOwnerProofHeaders;
|
|
10
|
+
exports.solanaOwnerSigner = solanaOwnerSigner;
|
|
11
|
+
exports.emptySignatureSlots = emptySignatureSlots;
|
|
12
|
+
/**
|
|
13
|
+
* Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
|
|
14
|
+
*
|
|
15
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
16
|
+
* you may not use this file except in compliance with the License.
|
|
17
|
+
* You may obtain a copy of the License at
|
|
18
|
+
*
|
|
19
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
20
|
+
*
|
|
21
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
22
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
23
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
24
|
+
* See the License for the specific language governing permissions and
|
|
25
|
+
* limitations under the License.
|
|
26
|
+
*/
|
|
27
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
28
|
+
const bs58_1 = __importDefault(require("bs58"));
|
|
29
|
+
const tweetnacl_1 = __importDefault(require("tweetnacl"));
|
|
30
|
+
const base64_js_1 = require("../utils/base64.js");
|
|
31
|
+
/**
|
|
32
|
+
* Canonical ACTION-BOUND message for the owner proof.
|
|
33
|
+
*
|
|
34
|
+
* MUST match the bundler's `buildArNSCustodyMessage` byte for byte
|
|
35
|
+
* (newline-delimited) or every signature is rejected. The bundler rebuilds this
|
|
36
|
+
* from the request and verifies the signature over `message + nonce`, so a
|
|
37
|
+
* signature captured for one operation cannot authorize a different one, and
|
|
38
|
+
* the nonce is consumed on use so the same one cannot be replayed (e.g. to
|
|
39
|
+
* revert a record to an older value).
|
|
40
|
+
*/
|
|
41
|
+
function buildArNSCustodyMessage(action, fields) {
|
|
42
|
+
return ['arns', action, ...fields].join('\n');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A metadata field's ABSENT/EMPTY distinction, as the bundler binds it.
|
|
46
|
+
*
|
|
47
|
+
* `null` (clear the field) must not sign the same message as `''` (set it to
|
|
48
|
+
* empty), or a signature authorizing one would authorize the other. NUL can
|
|
49
|
+
* never appear in a value that reached here — the route rejects control
|
|
50
|
+
* characters — so it is a safe sentinel.
|
|
51
|
+
*/
|
|
52
|
+
function arNSMetadataField(value) {
|
|
53
|
+
return value === null || value === undefined ? '\u0000' : value;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Keywords joined on a separator the route rejects INSIDE a keyword, so
|
|
57
|
+
* `['a,b']` and `['a','b']` cannot be re-partitioned into one another with the
|
|
58
|
+
* same signature.
|
|
59
|
+
*/
|
|
60
|
+
function arNSKeywordsField(keywords) {
|
|
61
|
+
return keywords === null || keywords === undefined
|
|
62
|
+
? '\u0000'
|
|
63
|
+
: keywords.join('\u0001');
|
|
64
|
+
}
|
|
65
|
+
/** Solana's signature-type discriminator in Turbo's signed-request scheme. */
|
|
66
|
+
const SOLANA_SIGNATURE_TYPE = 4;
|
|
67
|
+
/**
|
|
68
|
+
* The ANT owner's half of the envelope, in its own `x-owner-*` headers.
|
|
69
|
+
*
|
|
70
|
+
* Two signatures travel on a record action — the PAYER's signed request over
|
|
71
|
+
* `"" + nonce`, and the OWNER's action-bound proof over `message + nonce` —
|
|
72
|
+
* from two different keys, usually on two different chains. They cannot share
|
|
73
|
+
* one header set: whichever verifier ran second would reject a signature that
|
|
74
|
+
* was never meant for it.
|
|
75
|
+
*/
|
|
76
|
+
async function arNSOwnerProofHeaders(owner, message, nonce) {
|
|
77
|
+
const address = await owner.getAddress();
|
|
78
|
+
const signature = await owner.signMessage(Uint8Array.from(Buffer.from(message + nonce)));
|
|
79
|
+
return {
|
|
80
|
+
// A Solana address IS the base58-encoded 32-byte ed25519 public key, so
|
|
81
|
+
// decoding the address recovers exactly the bytes the bundler verifies
|
|
82
|
+
// against. Keeps ArNSOwnerSigner minimal — no separate getPublicKey().
|
|
83
|
+
'x-owner-public-key': (0, base64_js_1.toB64Url)(Buffer.from(bs58_1.default.decode(address))),
|
|
84
|
+
'x-owner-nonce': nonce,
|
|
85
|
+
'x-owner-signature': (0, base64_js_1.toB64Url)(Buffer.from(signature)),
|
|
86
|
+
'x-owner-signature-type': String(SOLANA_SIGNATURE_TYPE),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Build an {@link ArNSOwnerSigner} from a raw Solana secret key.
|
|
91
|
+
*
|
|
92
|
+
* For servers and tests. A browser wallet (Phantom, Solflare, or a console's
|
|
93
|
+
* embedded wallet) should implement the interface directly against its own
|
|
94
|
+
* `signTransaction` / `signMessage` rather than exposing a secret key.
|
|
95
|
+
*
|
|
96
|
+
* Note the owner needs a key to SIGN with, not a funded account: Turbo is the
|
|
97
|
+
* fee payer on every sponsored action, so this wallet's SOL balance can stay
|
|
98
|
+
* at zero for the entire life of the name.
|
|
99
|
+
*/
|
|
100
|
+
function solanaOwnerSigner(secretKey) {
|
|
101
|
+
const keypair = web3_js_1.Keypair.fromSecretKey(typeof secretKey === 'string' ? bs58_1.default.decode(secretKey) : secretKey);
|
|
102
|
+
return {
|
|
103
|
+
getAddress: () => keypair.publicKey.toBase58(),
|
|
104
|
+
signTransaction: async (transactionBase64) => {
|
|
105
|
+
const tx = web3_js_1.VersionedTransaction.deserialize(Buffer.from(transactionBase64, 'base64'));
|
|
106
|
+
// Sign the bytes as returned. Turbo's fee-payer signature already covers
|
|
107
|
+
// this exact message; rebuilding it from parts invalidates that and the
|
|
108
|
+
// submission is rejected.
|
|
109
|
+
tx.sign([keypair]);
|
|
110
|
+
return Buffer.from(tx.serialize()).toString('base64');
|
|
111
|
+
},
|
|
112
|
+
signMessage: async (message) => tweetnacl_1.default.sign.detached(message, keypair.secretKey),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Count the signature slots a prepared transaction still has empty.
|
|
117
|
+
*
|
|
118
|
+
* Turbo pre-signs as fee payer and leaves exactly one slot for the owner, so a
|
|
119
|
+
* client can assert that before prompting a wallet — a cheap way to catch a
|
|
120
|
+
* malformed or already-signed transaction before bothering the user.
|
|
121
|
+
*/
|
|
122
|
+
function emptySignatureSlots(transactionBase64) {
|
|
123
|
+
const tx = web3_js_1.VersionedTransaction.deserialize(Buffer.from(transactionBase64, 'base64'));
|
|
124
|
+
return tx.signatures.filter((sig) => sig.every((byte) => byte === 0)).length;
|
|
125
|
+
}
|
|
@@ -41,7 +41,7 @@ const chunkingHeader = { 'x-chunking-version': '2' };
|
|
|
41
41
|
* uploading them in parallel, and emitting progress/error events.
|
|
42
42
|
*/
|
|
43
43
|
class ChunkedUploader {
|
|
44
|
-
constructor({ http, token, maxChunkConcurrency = exports.defaultMaxChunkConcurrency, maxFinalizeMs, chunkByteCount = exports.defaultChunkByteCount, logger = logger_js_1.Logger.default, chunkingMode = 'auto', dataItemByteCount, }) {
|
|
44
|
+
constructor({ http, token, maxChunkConcurrency = exports.defaultMaxChunkConcurrency, maxFinalizeMs, chunkByteCount = exports.defaultChunkByteCount, logger = logger_js_1.Logger.default, chunkingMode = 'auto', dataItemByteCount, x402, x402RefundIdentity, }) {
|
|
45
45
|
this.assertChunkParams({
|
|
46
46
|
chunkByteCount,
|
|
47
47
|
chunkingMode,
|
|
@@ -60,6 +60,9 @@ class ChunkedUploader {
|
|
|
60
60
|
dataItemByteCount,
|
|
61
61
|
});
|
|
62
62
|
this.maxBacklogQueue = this.maxChunkConcurrency * backlogQueueFactor;
|
|
63
|
+
this.x402 = x402;
|
|
64
|
+
this.x402RefundIdentity = x402RefundIdentity;
|
|
65
|
+
this.dataItemByteCount = dataItemByteCount;
|
|
63
66
|
}
|
|
64
67
|
shouldChunkUpload({ chunkByteCount, chunkingMode, dataItemByteCount, }) {
|
|
65
68
|
if (chunkingMode === 'disabled') {
|
|
@@ -98,7 +101,54 @@ class ChunkedUploader {
|
|
|
98
101
|
/**
|
|
99
102
|
* Initialize or resume an upload session, returning the upload ID.
|
|
100
103
|
*/
|
|
104
|
+
/**
|
|
105
|
+
* Open a multipart upload paid for with x402.
|
|
106
|
+
*
|
|
107
|
+
* The bundler settles the payment at CREATE, before accepting a single chunk
|
|
108
|
+
* — so an unpaid upload never consumes storage. That is why the size is
|
|
109
|
+
* declared here and the money moves here, not at finalize.
|
|
110
|
+
*
|
|
111
|
+
* The 402 handshake is delegated to `wrapFetchWithPayment`, the same
|
|
112
|
+
* mechanism the single-shot x402 POST already uses, so the signer and the
|
|
113
|
+
* spend cap behave identically on both paths.
|
|
114
|
+
*/
|
|
115
|
+
async initPaidUpload(x402) {
|
|
116
|
+
if (this.paidUploadId !== undefined) {
|
|
117
|
+
this.logger.debug('Resuming the already-paid chunked upload', {
|
|
118
|
+
uploadId: this.paidUploadId,
|
|
119
|
+
});
|
|
120
|
+
return this.paidUploadId;
|
|
121
|
+
}
|
|
122
|
+
if (this.x402RefundIdentity === undefined) {
|
|
123
|
+
throw new Error('An x402-paid chunked upload needs a refund identity — it is the Turbo wallet credited if the upload delivers fewer bytes than were paid for.');
|
|
124
|
+
}
|
|
125
|
+
const { address, signatureType } = this.x402RefundIdentity;
|
|
126
|
+
const endpoint = `/chunks/${this.token}/-1/-1?chunkSize=${this.chunkByteCount}` +
|
|
127
|
+
`&totalBytes=${this.dataItemByteCount}` +
|
|
128
|
+
`&address=${encodeURIComponent(address)}` +
|
|
129
|
+
`&signatureType=${signatureType}`;
|
|
130
|
+
this.logger.debug('Opening an x402-paid chunked upload', {
|
|
131
|
+
totalBytes: this.dataItemByteCount,
|
|
132
|
+
});
|
|
133
|
+
const res = await this.http.get({
|
|
134
|
+
endpoint: endpoint,
|
|
135
|
+
headers: chunkingHeader,
|
|
136
|
+
x402Options: x402,
|
|
137
|
+
});
|
|
138
|
+
if (res.chunkSize !== undefined && res.chunkSize !== this.chunkByteCount) {
|
|
139
|
+
this.logger.warn('Chunk size mismatch! Overriding with server value.', {
|
|
140
|
+
clientExpected: this.chunkByteCount,
|
|
141
|
+
serverReturned: res.chunkSize,
|
|
142
|
+
});
|
|
143
|
+
this.chunkByteCount = res.chunkSize;
|
|
144
|
+
}
|
|
145
|
+
this.paidUploadId = res.id;
|
|
146
|
+
return res.id;
|
|
147
|
+
}
|
|
101
148
|
async initUpload() {
|
|
149
|
+
if (this.x402 !== undefined) {
|
|
150
|
+
return this.initPaidUpload(this.x402);
|
|
151
|
+
}
|
|
102
152
|
const res = await this.http.get({
|
|
103
153
|
endpoint: `/chunks/${this.token}/-1/-1?chunkSize=${this.chunkByteCount}`,
|
|
104
154
|
headers: chunkingHeader,
|