@ardrive/turbo-sdk 1.36.0 → 1.37.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/README.md +15 -0
- package/bundles/web.bundle.min.js +172510 -144495
- package/lib/cjs/cli/options.js +6 -0
- package/lib/cjs/cli/utils.js +9 -1
- package/lib/cjs/common/chunked.js +1 -1
- package/lib/cjs/common/factory.js +2 -6
- package/lib/cjs/common/http.js +118 -1
- package/lib/cjs/common/logger.js +50 -59
- package/lib/cjs/common/payment.js +2 -2
- package/lib/cjs/common/signer.js +36 -1
- package/lib/cjs/common/token/ario.js +1 -1
- package/lib/cjs/common/token/arweave.js +1 -1
- package/lib/cjs/common/token/erc20.js +1 -1
- package/lib/cjs/common/token/ethereum.js +1 -1
- package/lib/cjs/common/token/kyve.js +1 -1
- package/lib/cjs/common/token/polygon.js +1 -1
- package/lib/cjs/common/token/solana.js +1 -1
- package/lib/cjs/common/upload.js +25 -3
- package/lib/cjs/common/winstonLogger.js +66 -0
- package/lib/cjs/types.js +9 -1
- package/lib/cjs/utils/axiosClient.js +1 -1
- package/lib/cjs/version.js +1 -1
- package/lib/esm/cli/options.js +6 -0
- package/lib/esm/cli/utils.js +10 -2
- package/lib/esm/common/chunked.js +2 -2
- package/lib/esm/common/factory.js +3 -7
- package/lib/esm/common/http.js +117 -1
- package/lib/esm/common/logger.js +48 -57
- package/lib/esm/common/payment.js +3 -3
- package/lib/esm/common/signer.js +36 -2
- package/lib/esm/common/token/ario.js +2 -2
- package/lib/esm/common/token/arweave.js +2 -2
- package/lib/esm/common/token/erc20.js +2 -2
- package/lib/esm/common/token/ethereum.js +2 -2
- package/lib/esm/common/token/kyve.js +2 -2
- package/lib/esm/common/token/polygon.js +2 -2
- package/lib/esm/common/token/solana.js +2 -2
- package/lib/esm/common/upload.js +27 -5
- package/lib/esm/common/winstonLogger.js +62 -0
- package/lib/esm/types.js +7 -0
- package/lib/esm/utils/axiosClient.js +2 -2
- package/lib/esm/version.js +1 -1
- package/lib/types/cli/options.d.ts +17 -0
- package/lib/types/cli/options.d.ts.map +1 -1
- package/lib/types/cli/types.d.ts +1 -0
- package/lib/types/cli/types.d.ts.map +1 -1
- package/lib/types/cli/utils.d.ts.map +1 -1
- package/lib/types/common/chunked.d.ts.map +1 -1
- package/lib/types/common/factory.d.ts +4 -5
- package/lib/types/common/factory.d.ts.map +1 -1
- package/lib/types/common/http.d.ts +10 -2
- package/lib/types/common/http.d.ts.map +1 -1
- package/lib/types/common/logger.d.ts +17 -26
- package/lib/types/common/logger.d.ts.map +1 -1
- package/lib/types/common/payment.d.ts.map +1 -1
- package/lib/types/common/signer.d.ts +3 -0
- package/lib/types/common/signer.d.ts.map +1 -1
- package/lib/types/common/token/ario.d.ts.map +1 -1
- package/lib/types/common/token/arweave.d.ts.map +1 -1
- package/lib/types/common/token/erc20.d.ts.map +1 -1
- package/lib/types/common/token/ethereum.d.ts.map +1 -1
- package/lib/types/common/token/kyve.d.ts.map +1 -1
- package/lib/types/common/token/polygon.d.ts.map +1 -1
- package/lib/types/common/token/solana.d.ts.map +1 -1
- package/lib/types/common/upload.d.ts +2 -1
- package/lib/types/common/upload.d.ts.map +1 -1
- package/lib/types/common/winstonLogger.d.ts +14 -0
- package/lib/types/common/winstonLogger.d.ts.map +1 -0
- package/lib/types/types.d.ts +24 -12
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/version.d.ts +1 -1
- package/package.json +9 -1
package/lib/cjs/cli/options.js
CHANGED
|
@@ -186,6 +186,11 @@ exports.optionMap = {
|
|
|
186
186
|
description: 'Enable on-demand crypto top-ups during upload if balance is insufficient',
|
|
187
187
|
default: false,
|
|
188
188
|
},
|
|
189
|
+
x402: {
|
|
190
|
+
alias: '--x402',
|
|
191
|
+
description: 'Pay for the action using x402 funding (if available). Requires token `base-usdc`.',
|
|
192
|
+
default: false,
|
|
193
|
+
},
|
|
189
194
|
topUpBufferMultiplier: {
|
|
190
195
|
alias: '--top-up-buffer-multiplier <topUpBufferMultiplier>',
|
|
191
196
|
description: 'Multiplier to apply to the estimated top-up amount to avoid underpayment during on-demand top-ups. Defaults to 1.1 (10% buffer).',
|
|
@@ -215,6 +220,7 @@ const onDemandOptions = [
|
|
|
215
220
|
exports.optionMap.onDemand,
|
|
216
221
|
exports.optionMap.topUpBufferMultiplier,
|
|
217
222
|
exports.optionMap.maxCryptoTopUpValue,
|
|
223
|
+
exports.optionMap.x402,
|
|
218
224
|
];
|
|
219
225
|
exports.uploadOptions = [
|
|
220
226
|
...exports.walletOptions,
|
package/lib/cjs/cli/utils.js
CHANGED
|
@@ -270,7 +270,10 @@ function getTagsFromOptions(options) {
|
|
|
270
270
|
return parseTags(options.tags);
|
|
271
271
|
}
|
|
272
272
|
function onDemandOptionsFromOptions(options) {
|
|
273
|
-
if (
|
|
273
|
+
if (options.x402 && options.onDemand) {
|
|
274
|
+
throw new Error('Cannot use both --x402 and --on-demand flags');
|
|
275
|
+
}
|
|
276
|
+
if (!options.onDemand && !options.x402) {
|
|
274
277
|
return { fundingMode: new index_js_1.ExistingBalanceFunding() };
|
|
275
278
|
}
|
|
276
279
|
const value = options.maxCryptoTopUpValue;
|
|
@@ -282,6 +285,11 @@ function onDemandOptionsFromOptions(options) {
|
|
|
282
285
|
const token = tokenFromOptions(options);
|
|
283
286
|
maxTokenAmount = index_js_1.tokenToBaseMap[token](value).toString();
|
|
284
287
|
}
|
|
288
|
+
if (options.x402) {
|
|
289
|
+
return {
|
|
290
|
+
fundingMode: new index_js_1.X402Funding({ maxMUSDCAmount: maxTokenAmount }),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
285
293
|
if (options.topUpBufferMultiplier !== undefined &&
|
|
286
294
|
(isNaN(options.topUpBufferMultiplier) || options.topUpBufferMultiplier < 1)) {
|
|
287
295
|
throw new Error('topUpBufferMultiplier must be a number >= 1');
|
|
@@ -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.
|
|
44
|
+
constructor({ http, token, maxChunkConcurrency = exports.defaultMaxChunkConcurrency, maxFinalizeMs, chunkByteCount = exports.defaultChunkByteCount, logger = logger_js_1.Logger.default, chunkingMode = 'auto', dataItemByteCount, }) {
|
|
45
45
|
this.assertChunkParams({
|
|
46
46
|
chunkByteCount,
|
|
47
47
|
chunkingMode,
|
|
@@ -24,14 +24,10 @@ const index_js_1 = require("./token/index.js");
|
|
|
24
24
|
const turbo_js_1 = require("./turbo.js");
|
|
25
25
|
const upload_js_1 = require("./upload.js");
|
|
26
26
|
class TurboBaseFactory {
|
|
27
|
-
/* @deprecated - use
|
|
27
|
+
/* @deprecated - use Logger directly */
|
|
28
28
|
static setLogLevel(level) {
|
|
29
29
|
this.logger.setLogLevel(level);
|
|
30
30
|
}
|
|
31
|
-
/* @deprecated - use TurboWinstonLogger directly */
|
|
32
|
-
static setLogFormat(format) {
|
|
33
|
-
this.logger.setLogFormat(format);
|
|
34
|
-
}
|
|
35
31
|
static unauthenticated({ paymentServiceConfig = {}, uploadServiceConfig = {}, token, } = {}) {
|
|
36
32
|
token = token === 'pol' ? 'matic' : token;
|
|
37
33
|
token ??= 'arweave'; // default to arweave if token is not provided
|
|
@@ -151,4 +147,4 @@ class TurboBaseFactory {
|
|
|
151
147
|
}
|
|
152
148
|
}
|
|
153
149
|
exports.TurboBaseFactory = TurboBaseFactory;
|
|
154
|
-
TurboBaseFactory.logger = logger_js_1.
|
|
150
|
+
TurboBaseFactory.logger = logger_js_1.Logger.default;
|
package/lib/cjs/common/http.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TurboHTTPService = void 0;
|
|
4
|
+
exports.toX402FetchBody = toX402FetchBody;
|
|
4
5
|
/**
|
|
5
6
|
* Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
|
|
6
7
|
*
|
|
@@ -18,6 +19,7 @@ exports.TurboHTTPService = void 0;
|
|
|
18
19
|
*/
|
|
19
20
|
const axios_1 = require("axios");
|
|
20
21
|
const node_stream_1 = require("node:stream");
|
|
22
|
+
const x402_fetch_1 = require("x402-fetch");
|
|
21
23
|
const axiosClient_js_1 = require("../utils/axiosClient.js");
|
|
22
24
|
const common_js_1 = require("../utils/common.js");
|
|
23
25
|
const errors_js_1 = require("../utils/errors.js");
|
|
@@ -36,7 +38,46 @@ class TurboHTTPService {
|
|
|
36
38
|
async get({ endpoint, signal, allowedStatuses = [200, 202], headers, }) {
|
|
37
39
|
return this.retryRequest(() => this.axios.get(endpoint, { headers, signal }), allowedStatuses);
|
|
38
40
|
}
|
|
39
|
-
async post({ endpoint, signal, allowedStatuses = [200, 202], headers, data, }) {
|
|
41
|
+
async post({ endpoint, signal, allowedStatuses = [200, 202], headers, data, x402Options, }) {
|
|
42
|
+
if (x402Options !== undefined) {
|
|
43
|
+
this.logger.debug('Using X402 options for POST request', {
|
|
44
|
+
endpoint,
|
|
45
|
+
x402Options,
|
|
46
|
+
});
|
|
47
|
+
const { body, duplex } = await toX402FetchBody(data);
|
|
48
|
+
try {
|
|
49
|
+
const maxMUSDCAmount = x402Options.maxMUSDCAmount !== undefined
|
|
50
|
+
? BigInt(x402Options.maxMUSDCAmount.toString())
|
|
51
|
+
: undefined;
|
|
52
|
+
const fetchWithPay = (0, x402_fetch_1.wrapFetchWithPayment)(fetch, x402Options.signer, maxMUSDCAmount);
|
|
53
|
+
const res = await fetchWithPay(this.axios.defaults.baseURL + '/x402/data-item/signed', {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers,
|
|
56
|
+
body,
|
|
57
|
+
signal,
|
|
58
|
+
...(duplex ? { duplex } : {}), // Use duplex only where streams are working
|
|
59
|
+
});
|
|
60
|
+
if (!allowedStatuses.includes(res.status)) {
|
|
61
|
+
const errorText = await res.text();
|
|
62
|
+
throw new errors_js_1.FailedRequestError(
|
|
63
|
+
// Return error message from server if available
|
|
64
|
+
errorText || res.statusText, res.status);
|
|
65
|
+
}
|
|
66
|
+
return res.json();
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (error instanceof errors_js_1.FailedRequestError) {
|
|
70
|
+
throw error; // rethrow FailedRequestError
|
|
71
|
+
}
|
|
72
|
+
// Handle CanceledError specifically
|
|
73
|
+
if (error.message.includes('The operation was aborted')) {
|
|
74
|
+
throw new axios_1.CanceledError();
|
|
75
|
+
}
|
|
76
|
+
// Log the error and throw a FailedRequestError
|
|
77
|
+
this.logger.error('Error posting data', { endpoint, error });
|
|
78
|
+
throw new errors_js_1.FailedRequestError(error instanceof Error ? error.message : 'Unknown error', error.response?.status);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
40
81
|
// Buffer and Readable → keep Axios (streams work fine there)
|
|
41
82
|
if (!(data instanceof ReadableStream)) {
|
|
42
83
|
if (data instanceof node_stream_1.Readable) {
|
|
@@ -141,3 +182,79 @@ async function toFetchBody(data) {
|
|
|
141
182
|
const blob = await new Response(data).blob();
|
|
142
183
|
return { body: blob }; // browser sets length
|
|
143
184
|
}
|
|
185
|
+
const isNode = typeof process !== 'undefined' && !!process.versions?.node;
|
|
186
|
+
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
|
187
|
+
function isFirefoxOrSafari() {
|
|
188
|
+
if (!isBrowser)
|
|
189
|
+
return false;
|
|
190
|
+
const ua = navigator.userAgent;
|
|
191
|
+
return ua.includes('Firefox') || ua.includes('Safari');
|
|
192
|
+
}
|
|
193
|
+
/** Create a re-usable body for x402 fetch protocol */
|
|
194
|
+
async function toX402FetchBody(data) {
|
|
195
|
+
//
|
|
196
|
+
// 🔹 NODE: always buffer to a non-stream body (Buffer)
|
|
197
|
+
// so fetchWithPayment can reuse it safely.
|
|
198
|
+
//
|
|
199
|
+
if (isNode) {
|
|
200
|
+
let buf;
|
|
201
|
+
// Web ReadableStream → Buffer
|
|
202
|
+
if (typeof ReadableStream !== 'undefined' &&
|
|
203
|
+
data instanceof ReadableStream) {
|
|
204
|
+
const ab = await new Response(data).arrayBuffer();
|
|
205
|
+
buf = Buffer.from(ab);
|
|
206
|
+
}
|
|
207
|
+
// Node Readable → Buffer
|
|
208
|
+
else if (data instanceof node_stream_1.Readable) {
|
|
209
|
+
const chunks = [];
|
|
210
|
+
for await (const chunk of data) {
|
|
211
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
212
|
+
}
|
|
213
|
+
buf = Buffer.concat(chunks.map((c) => Uint8Array.from(c)));
|
|
214
|
+
}
|
|
215
|
+
// Buffer / Uint8Array
|
|
216
|
+
else if (Buffer.isBuffer(data)) {
|
|
217
|
+
buf = data;
|
|
218
|
+
}
|
|
219
|
+
else if (data instanceof Uint8Array) {
|
|
220
|
+
buf = Buffer.from(data);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
throw new Error('Unsupported body type for toFetchBody (Node)');
|
|
224
|
+
}
|
|
225
|
+
// For Buffer body, Node *does not* need duplex.
|
|
226
|
+
return { body: buf };
|
|
227
|
+
}
|
|
228
|
+
//
|
|
229
|
+
// 🔹 BROWSER: keep your previous behavior (streams/Blob), no duplex.
|
|
230
|
+
//
|
|
231
|
+
let body;
|
|
232
|
+
// Already a web ReadableStream
|
|
233
|
+
if (typeof ReadableStream !== 'undefined' && data instanceof ReadableStream) {
|
|
234
|
+
body = data;
|
|
235
|
+
}
|
|
236
|
+
// Node Readable in browser (rare, but be safe) → Blob
|
|
237
|
+
else if (data instanceof node_stream_1.Readable) {
|
|
238
|
+
const chunks = [];
|
|
239
|
+
for await (const chunk of data) {
|
|
240
|
+
const buf = typeof chunk === 'string'
|
|
241
|
+
? new TextEncoder().encode(chunk)
|
|
242
|
+
: new Uint8Array(chunk);
|
|
243
|
+
chunks.push(buf);
|
|
244
|
+
}
|
|
245
|
+
body = new Blob(chunks);
|
|
246
|
+
}
|
|
247
|
+
// Buffer / Uint8Array
|
|
248
|
+
else if (Buffer.isBuffer(data) || data instanceof Uint8Array) {
|
|
249
|
+
body = new Blob([Uint8Array.from(data)]);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
throw new Error('Unsupported body type for toFetchBody (browser)');
|
|
253
|
+
}
|
|
254
|
+
// Firefox / Safari – avoid streaming uploads
|
|
255
|
+
if (isFirefoxOrSafari() && body instanceof ReadableStream) {
|
|
256
|
+
const blob = await new Response(body).blob();
|
|
257
|
+
return { body: blob };
|
|
258
|
+
}
|
|
259
|
+
return { body };
|
|
260
|
+
}
|
package/lib/cjs/common/logger.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.Logger = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
|
|
6
6
|
*
|
|
@@ -16,77 +16,68 @@ exports.TurboWinstonLogger = void 0;
|
|
|
16
16
|
* See the License for the specific language governing permissions and
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
|
-
const winston_1 = require("winston");
|
|
20
19
|
const version_js_1 = require("../version.js");
|
|
21
|
-
class
|
|
22
|
-
constructor({ level = 'info',
|
|
23
|
-
this.
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
class Logger {
|
|
21
|
+
constructor({ level = 'info', } = {}) {
|
|
22
|
+
this.level = 'info';
|
|
23
|
+
this.levels = {
|
|
24
|
+
debug: 0,
|
|
25
|
+
info: 1,
|
|
26
|
+
warn: 2,
|
|
27
|
+
error: 3,
|
|
28
|
+
none: 999,
|
|
29
|
+
};
|
|
30
|
+
this.level = level;
|
|
31
|
+
}
|
|
32
|
+
formatMessage(level, message, ...args) {
|
|
33
|
+
const timestamp = new Date().toISOString();
|
|
34
|
+
const meta = {
|
|
35
|
+
timestamp,
|
|
36
|
+
level,
|
|
37
|
+
message,
|
|
38
|
+
name: 'turbo-sdk',
|
|
39
|
+
version: version_js_1.version,
|
|
40
|
+
};
|
|
41
|
+
if (args.length > 0) {
|
|
42
|
+
return JSON.stringify({ ...meta, args });
|
|
26
43
|
}
|
|
27
|
-
|
|
28
|
-
|
|
44
|
+
return JSON.stringify(meta);
|
|
45
|
+
}
|
|
46
|
+
log(level, message, ...args) {
|
|
47
|
+
if (this.levels[level] < this.levels[this.level]) {
|
|
48
|
+
return;
|
|
29
49
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
});
|
|
50
|
+
const formattedMessage = this.formatMessage(level, message, ...args);
|
|
51
|
+
switch (level) {
|
|
52
|
+
case 'debug':
|
|
53
|
+
console.debug(formattedMessage);
|
|
54
|
+
break;
|
|
55
|
+
case 'info':
|
|
56
|
+
console.info(formattedMessage);
|
|
57
|
+
break;
|
|
58
|
+
case 'warn':
|
|
59
|
+
console.warn(formattedMessage);
|
|
60
|
+
break;
|
|
61
|
+
case 'error':
|
|
62
|
+
console.error(formattedMessage);
|
|
63
|
+
break;
|
|
45
64
|
}
|
|
46
65
|
}
|
|
47
66
|
info(message, ...args) {
|
|
48
|
-
|
|
49
|
-
return;
|
|
50
|
-
this.logger.info(message, ...args);
|
|
67
|
+
this.log('info', message, ...args);
|
|
51
68
|
}
|
|
52
69
|
warn(message, ...args) {
|
|
53
|
-
|
|
54
|
-
return;
|
|
55
|
-
this.logger.warn(message, ...args);
|
|
70
|
+
this.log('warn', message, ...args);
|
|
56
71
|
}
|
|
57
72
|
error(message, ...args) {
|
|
58
|
-
|
|
59
|
-
return;
|
|
60
|
-
this.logger.error(message, ...args);
|
|
73
|
+
this.log('error', message, ...args);
|
|
61
74
|
}
|
|
62
75
|
debug(message, ...args) {
|
|
63
|
-
|
|
64
|
-
return;
|
|
65
|
-
this.logger.debug(message, ...args);
|
|
76
|
+
this.log('debug', message, ...args);
|
|
66
77
|
}
|
|
67
78
|
setLogLevel(level) {
|
|
68
|
-
this.
|
|
69
|
-
if ('silent' in this.logger) {
|
|
70
|
-
this.logger.silent = level === 'none';
|
|
71
|
-
}
|
|
72
|
-
if ('level' in this.logger) {
|
|
73
|
-
this.logger.level = level;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
setLogFormat(logFormat) {
|
|
77
|
-
if ('format' in this.logger) {
|
|
78
|
-
this.logger.format = getLogFormat(logFormat);
|
|
79
|
-
}
|
|
79
|
+
this.level = level;
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
-
exports.
|
|
83
|
-
|
|
84
|
-
function getLogFormat(logFormat) {
|
|
85
|
-
return winston_1.format.combine((0, winston_1.format)((info) => {
|
|
86
|
-
if (info.stack && info.level !== 'error') {
|
|
87
|
-
delete info.stack;
|
|
88
|
-
}
|
|
89
|
-
return info;
|
|
90
|
-
})(), winston_1.format.errors({ stack: true }), // Ensure errors show a stack trace
|
|
91
|
-
winston_1.format.timestamp(), logFormat === 'json' ? winston_1.format.json() : winston_1.format.simple());
|
|
92
|
-
}
|
|
82
|
+
exports.Logger = Logger;
|
|
83
|
+
Logger.default = new Logger();
|
|
@@ -25,7 +25,7 @@ const index_js_1 = require("./token/index.js");
|
|
|
25
25
|
exports.developmentPaymentServiceURL = 'https://payment.ardrive.dev';
|
|
26
26
|
exports.defaultPaymentServiceURL = 'https://payment.ardrive.io';
|
|
27
27
|
class TurboUnauthenticatedPaymentService {
|
|
28
|
-
constructor({ url = exports.defaultPaymentServiceURL, logger = logger_js_1.
|
|
28
|
+
constructor({ url = exports.defaultPaymentServiceURL, logger = logger_js_1.Logger.default, retryConfig = (0, axiosClient_js_1.defaultRetryConfig)(logger), token = 'arweave', }) {
|
|
29
29
|
this.logger = logger;
|
|
30
30
|
this.httpService = new http_js_1.TurboHTTPService({
|
|
31
31
|
url: `${url}/v1`,
|
|
@@ -245,7 +245,7 @@ class TurboUnauthenticatedPaymentService {
|
|
|
245
245
|
exports.TurboUnauthenticatedPaymentService = TurboUnauthenticatedPaymentService;
|
|
246
246
|
// NOTE: to avoid redundancy, we use inheritance here - but generally prefer composition over inheritance
|
|
247
247
|
class TurboAuthenticatedPaymentService extends TurboUnauthenticatedPaymentService {
|
|
248
|
-
constructor({ url = exports.defaultPaymentServiceURL, retryConfig, signer, logger = logger_js_1.
|
|
248
|
+
constructor({ url = exports.defaultPaymentServiceURL, retryConfig, signer, logger = logger_js_1.Logger.default, token = 'arweave', tokenTools, }) {
|
|
249
249
|
super({ url, retryConfig, logger, token });
|
|
250
250
|
this.signer = signer;
|
|
251
251
|
this.tokenTools = tokenTools;
|
package/lib/cjs/common/signer.js
CHANGED
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.TurboDataItemAbstractSigner = void 0;
|
|
7
|
+
exports.makeX402Signer = makeX402Signer;
|
|
7
8
|
/**
|
|
8
9
|
* Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
|
|
9
10
|
*
|
|
@@ -31,6 +32,9 @@ const crypto_2 = require("crypto");
|
|
|
31
32
|
const ethers_1 = require("ethers");
|
|
32
33
|
const ethers_2 = require("ethers");
|
|
33
34
|
const tweetnacl_1 = __importDefault(require("tweetnacl"));
|
|
35
|
+
const viem_1 = require("viem");
|
|
36
|
+
const accounts_1 = require("viem/accounts");
|
|
37
|
+
const chains_1 = require("viem/chains");
|
|
34
38
|
const types_js_1 = require("../types.js");
|
|
35
39
|
const base64_js_1 = require("../utils/base64.js");
|
|
36
40
|
const logger_js_1 = require("./logger.js");
|
|
@@ -40,7 +44,7 @@ const solana_js_1 = require("./token/solana.js");
|
|
|
40
44
|
* Abstract class for signing TurboDataItems.
|
|
41
45
|
*/
|
|
42
46
|
class TurboDataItemAbstractSigner {
|
|
43
|
-
constructor({ signer, logger = logger_js_1.
|
|
47
|
+
constructor({ signer, logger = logger_js_1.Logger.default, token, walletAdapter, }) {
|
|
44
48
|
this.logger = logger;
|
|
45
49
|
this.signer = signer;
|
|
46
50
|
this.token = token;
|
|
@@ -155,3 +159,34 @@ class TurboDataItemAbstractSigner {
|
|
|
155
159
|
}
|
|
156
160
|
}
|
|
157
161
|
exports.TurboDataItemAbstractSigner = TurboDataItemAbstractSigner;
|
|
162
|
+
async function makeX402Signer(arbundlesSigner) {
|
|
163
|
+
// Node: our SDK uses EthereumSigner with a raw private key
|
|
164
|
+
if (arbundlesSigner instanceof arbundles_1.EthereumSigner) {
|
|
165
|
+
return (0, viem_1.createWalletClient)({
|
|
166
|
+
account: (0, accounts_1.privateKeyToAccount)(('0x' +
|
|
167
|
+
Buffer.from(arbundlesSigner.key).toString('hex'))),
|
|
168
|
+
chain: chains_1.baseSepolia,
|
|
169
|
+
transport: (0, viem_1.http)(),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
// Browser: use injected wallet + selected account
|
|
173
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
174
|
+
if (typeof window !== 'undefined' && window.ethereum) {
|
|
175
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
176
|
+
const provider = window.ethereum;
|
|
177
|
+
// ask wallet for an account
|
|
178
|
+
const accounts = (await provider.request({
|
|
179
|
+
method: 'eth_requestAccounts',
|
|
180
|
+
}));
|
|
181
|
+
if (accounts === undefined || accounts.length === 0) {
|
|
182
|
+
throw new Error('No accounts returned from wallet');
|
|
183
|
+
}
|
|
184
|
+
const account = accounts[0];
|
|
185
|
+
return (0, viem_1.createWalletClient)({
|
|
186
|
+
account,
|
|
187
|
+
chain: chains_1.baseSepolia,
|
|
188
|
+
transport: (0, viem_1.custom)(provider),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
throw new Error('Unable to construct x402 signer for x402 options');
|
|
192
|
+
}
|
|
@@ -23,7 +23,7 @@ const common_js_1 = require("../../utils/common.js");
|
|
|
23
23
|
const version_js_1 = require("../../version.js");
|
|
24
24
|
const logger_js_1 = require("../logger.js");
|
|
25
25
|
class ARIOToken {
|
|
26
|
-
constructor({ cuUrl = common_js_1.defaultProdAoConfigs.ario.cuUrl, logger = logger_js_1.
|
|
26
|
+
constructor({ cuUrl = common_js_1.defaultProdAoConfigs.ario.cuUrl, logger = logger_js_1.Logger.default, pollingOptions = {
|
|
27
27
|
initialBackoffMs: 500,
|
|
28
28
|
pollingIntervalMs: 0, // no polling for ARIO process
|
|
29
29
|
maxAttempts: 0, // no polling for ARIO process
|
|
@@ -29,7 +29,7 @@ const ArweaveClass =
|
|
|
29
29
|
// @ts-ignore -- Access the correct class constructor for Arweave
|
|
30
30
|
arweave_1.default.default?.default || arweave_1.default.default || arweave_1.default;
|
|
31
31
|
class ArweaveToken {
|
|
32
|
-
constructor({ gatewayUrl = 'https://arweave.net', arweave, logger = logger_js_1.
|
|
32
|
+
constructor({ gatewayUrl = 'https://arweave.net', arweave, logger = logger_js_1.Logger.default, mintU = true, pollingOptions = {
|
|
33
33
|
maxAttempts: 10,
|
|
34
34
|
pollingIntervalMs: 3_000,
|
|
35
35
|
initialBackoffMs: 7_000,
|
|
@@ -23,7 +23,7 @@ const common_js_1 = require("../../utils/common.js");
|
|
|
23
23
|
const logger_js_1 = require("../logger.js");
|
|
24
24
|
const ethereum_js_1 = require("./ethereum.js");
|
|
25
25
|
class ERC20Token extends ethereum_js_1.EthereumToken {
|
|
26
|
-
constructor({ tokenContractAddress, logger = logger_js_1.
|
|
26
|
+
constructor({ tokenContractAddress, logger = logger_js_1.Logger.default, gatewayUrl = common_js_1.defaultProdGatewayUrls.ethereum, pollingOptions, }) {
|
|
27
27
|
super({ logger, gatewayUrl, pollingOptions });
|
|
28
28
|
this.tokenContract = new ethers_1.ethers.Contract(tokenContractAddress, [
|
|
29
29
|
'function decimals() view returns (uint8)',
|
|
@@ -31,7 +31,7 @@ exports.defaultEthereumPollingOptions = {
|
|
|
31
31
|
pollingIntervalMs: 1_500,
|
|
32
32
|
};
|
|
33
33
|
class EthereumToken {
|
|
34
|
-
constructor({ logger = logger_js_1.
|
|
34
|
+
constructor({ logger = logger_js_1.Logger.default, gatewayUrl = common_js_1.defaultProdGatewayUrls.ethereum, pollingOptions = exports.defaultEthereumPollingOptions, } = {}) {
|
|
35
35
|
this.logger = logger;
|
|
36
36
|
this.gatewayUrl = gatewayUrl;
|
|
37
37
|
this.pollingOptions = pollingOptions;
|
|
@@ -38,7 +38,7 @@ exports.ukyveToTokenAmount = ukyveToTokenAmount;
|
|
|
38
38
|
const KYVEToTokenAmount = (sol) => new bignumber_js_1.BigNumber(sol).times(1e6).valueOf();
|
|
39
39
|
exports.KYVEToTokenAmount = KYVEToTokenAmount;
|
|
40
40
|
class KyveToken {
|
|
41
|
-
constructor({ logger = logger_js_1.
|
|
41
|
+
constructor({ logger = logger_js_1.Logger.default, gatewayUrl = common_js_1.defaultProdGatewayUrls.kyve, pollingOptions = {
|
|
42
42
|
maxAttempts: 5,
|
|
43
43
|
pollingIntervalMs: 1_000,
|
|
44
44
|
initialBackoffMs: 500,
|
|
@@ -11,7 +11,7 @@ exports.defaultPolygonPollingOptions = {
|
|
|
11
11
|
pollingIntervalMs: 1_000,
|
|
12
12
|
};
|
|
13
13
|
class PolygonToken extends ethereum_js_1.EthereumToken {
|
|
14
|
-
constructor({ logger = logger_js_1.
|
|
14
|
+
constructor({ logger = logger_js_1.Logger.default, gatewayUrl = common_js_1.defaultProdGatewayUrls.pol, pollingOptions = exports.defaultPolygonPollingOptions, } = {}) {
|
|
15
15
|
super({ logger, gatewayUrl, pollingOptions });
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -32,7 +32,7 @@ const SOLToTokenAmount = (sol) => new bignumber_js_1.BigNumber(sol).times(1e9).v
|
|
|
32
32
|
exports.SOLToTokenAmount = SOLToTokenAmount;
|
|
33
33
|
exports.memoProgramId = 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr';
|
|
34
34
|
class SolanaToken {
|
|
35
|
-
constructor({ logger = logger_js_1.
|
|
35
|
+
constructor({ logger = logger_js_1.Logger.default, gatewayUrl = common_js_1.defaultProdGatewayUrls.solana, pollingOptions = {
|
|
36
36
|
maxAttempts: 10,
|
|
37
37
|
pollingIntervalMs: 2_500,
|
|
38
38
|
initialBackoffMs: 500,
|
package/lib/cjs/common/upload.js
CHANGED
|
@@ -28,6 +28,7 @@ const events_js_1 = require("./events.js");
|
|
|
28
28
|
const http_js_1 = require("./http.js");
|
|
29
29
|
const index_js_1 = require("./index.js");
|
|
30
30
|
const logger_js_1 = require("./logger.js");
|
|
31
|
+
const signer_js_1 = require("./signer.js");
|
|
31
32
|
function isTurboUploadFileWithStreamFactoryParams(params) {
|
|
32
33
|
return 'fileStreamFactory' in params;
|
|
33
34
|
}
|
|
@@ -43,7 +44,7 @@ exports.creditSharingTagNames = {
|
|
|
43
44
|
exports.developmentUploadServiceURL = 'https://upload.ardrive.dev';
|
|
44
45
|
exports.defaultUploadServiceURL = 'https://upload.ardrive.io';
|
|
45
46
|
class TurboUnauthenticatedUploadService {
|
|
46
|
-
constructor({ url = exports.defaultUploadServiceURL, logger = logger_js_1.
|
|
47
|
+
constructor({ url = exports.defaultUploadServiceURL, logger = logger_js_1.Logger.default, retryConfig = (0, axiosClient_js_1.defaultRetryConfig)(logger), token = 'arweave', }) {
|
|
47
48
|
this.token = token;
|
|
48
49
|
this.logger = logger;
|
|
49
50
|
this.httpService = new http_js_1.TurboHTTPService({
|
|
@@ -53,7 +54,7 @@ class TurboUnauthenticatedUploadService {
|
|
|
53
54
|
});
|
|
54
55
|
this.retryConfig = retryConfig;
|
|
55
56
|
}
|
|
56
|
-
async uploadSignedDataItem({ dataItemStreamFactory, dataItemSizeFactory, dataItemOpts, signal, events = {}, }) {
|
|
57
|
+
async uploadSignedDataItem({ dataItemStreamFactory, dataItemSizeFactory, dataItemOpts, signal, events = {}, x402Options, }) {
|
|
57
58
|
const dataItemSize = dataItemSizeFactory();
|
|
58
59
|
this.logger.debug('Uploading signed data item...');
|
|
59
60
|
// create the tapped stream with events
|
|
@@ -83,6 +84,7 @@ class TurboUnauthenticatedUploadService {
|
|
|
83
84
|
signal,
|
|
84
85
|
data: streamWithUploadEvents,
|
|
85
86
|
headers,
|
|
87
|
+
x402Options,
|
|
86
88
|
});
|
|
87
89
|
// resume the stream so events start flowing to the post
|
|
88
90
|
resume();
|
|
@@ -94,6 +96,7 @@ exports.TurboUnauthenticatedUploadService = TurboUnauthenticatedUploadService;
|
|
|
94
96
|
class TurboAuthenticatedBaseUploadService extends TurboUnauthenticatedUploadService {
|
|
95
97
|
constructor({ url = exports.defaultUploadServiceURL, retryConfig, signer, logger, token, paymentService, }) {
|
|
96
98
|
super({ url, retryConfig, logger, token });
|
|
99
|
+
this.x402EnabledTokens = ['base-usdc'];
|
|
97
100
|
this.enabledOnDemandTokens = [
|
|
98
101
|
'ario',
|
|
99
102
|
'solana',
|
|
@@ -176,6 +179,14 @@ class TurboAuthenticatedBaseUploadService extends TurboUnauthenticatedUploadServ
|
|
|
176
179
|
}
|
|
177
180
|
async uploadFile(params) {
|
|
178
181
|
const { signal, dataItemOpts, events, fileStreamFactory, fileSizeFactory, fundingMode = new types_js_1.ExistingBalanceFunding(), } = this.resolveUploadFileConfig(params);
|
|
182
|
+
if (fundingMode instanceof types_js_1.X402Funding &&
|
|
183
|
+
!this.x402EnabledTokens.includes(this.token)) {
|
|
184
|
+
throw new Error('x402 uploads are not supported for token: ' + this.token);
|
|
185
|
+
}
|
|
186
|
+
if (params.chunkingMode === 'force' && fundingMode instanceof types_js_1.X402Funding) {
|
|
187
|
+
throw new Error("Chunking mode 'force' is not supported when x402 is enabled");
|
|
188
|
+
}
|
|
189
|
+
this.logger.debug('Starting file upload', { params });
|
|
179
190
|
let retries = 0;
|
|
180
191
|
const maxRetries = this.retryConfig.retries ?? 3;
|
|
181
192
|
const retryDelay = this.retryConfig.retryDelay ??
|
|
@@ -220,7 +231,8 @@ class TurboAuthenticatedBaseUploadService extends TurboUnauthenticatedUploadServ
|
|
|
220
231
|
chunkingMode: params.chunkingMode,
|
|
221
232
|
maxFinalizeMs: params.maxFinalizeMs,
|
|
222
233
|
});
|
|
223
|
-
if (chunkedUploader.shouldUseChunkUploader
|
|
234
|
+
if (chunkedUploader.shouldUseChunkUploader &&
|
|
235
|
+
!(fundingMode instanceof types_js_1.X402Funding)) {
|
|
224
236
|
const response = await chunkedUploader.upload({
|
|
225
237
|
dataItemStreamFactory,
|
|
226
238
|
dataItemSizeFactory,
|
|
@@ -230,12 +242,20 @@ class TurboAuthenticatedBaseUploadService extends TurboUnauthenticatedUploadServ
|
|
|
230
242
|
});
|
|
231
243
|
return { ...response, cryptoFundResult };
|
|
232
244
|
}
|
|
245
|
+
const x402Options = fundingMode instanceof types_js_1.X402Funding
|
|
246
|
+
? {
|
|
247
|
+
signer: fundingMode.signer ??
|
|
248
|
+
(await (0, signer_js_1.makeX402Signer)(this.signer.signer)),
|
|
249
|
+
maxMUSDCAmount: fundingMode.maxMUSDCAmount,
|
|
250
|
+
}
|
|
251
|
+
: undefined;
|
|
233
252
|
const response = await this.uploadSignedDataItem({
|
|
234
253
|
dataItemStreamFactory,
|
|
235
254
|
dataItemSizeFactory,
|
|
236
255
|
dataItemOpts,
|
|
237
256
|
signal,
|
|
238
257
|
events,
|
|
258
|
+
x402Options,
|
|
239
259
|
});
|
|
240
260
|
return { ...response, cryptoFundResult };
|
|
241
261
|
}
|
|
@@ -387,6 +407,7 @@ class TurboAuthenticatedBaseUploadService extends TurboUnauthenticatedUploadServ
|
|
|
387
407
|
});
|
|
388
408
|
},
|
|
389
409
|
},
|
|
410
|
+
fundingMode,
|
|
390
411
|
});
|
|
391
412
|
const relativePath = this.getRelativePath(file, params);
|
|
392
413
|
paths[relativePath] = { id: result.id };
|
|
@@ -479,6 +500,7 @@ class TurboAuthenticatedBaseUploadService extends TurboUnauthenticatedUploadServ
|
|
|
479
500
|
maxChunkConcurrency,
|
|
480
501
|
maxFinalizeMs,
|
|
481
502
|
chunkingMode,
|
|
503
|
+
fundingMode,
|
|
482
504
|
});
|
|
483
505
|
emitter.emit('folder-success');
|
|
484
506
|
return {
|