@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
|
@@ -17,7 +17,11 @@ exports.TurboAuthenticatedPaymentService = exports.TurboUnauthenticatedPaymentSe
|
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
19
|
const bignumber_js_1 = require("bignumber.js");
|
|
20
|
+
const types_js_1 = require("../types.js");
|
|
20
21
|
const common_js_1 = require("../utils/common.js");
|
|
22
|
+
const errors_js_1 = require("../utils/errors.js");
|
|
23
|
+
const uuid_js_1 = require("../utils/uuid.js");
|
|
24
|
+
const arnsActions_js_1 = require("./arnsActions.js");
|
|
21
25
|
const http_js_1 = require("./http.js");
|
|
22
26
|
const http_js_2 = require("./http.js");
|
|
23
27
|
const logger_js_1 = require("./logger.js");
|
|
@@ -37,7 +41,7 @@ class TurboUnauthenticatedPaymentService {
|
|
|
37
41
|
}
|
|
38
42
|
async getBalance(address) {
|
|
39
43
|
const balance = await this.httpService.get({
|
|
40
|
-
endpoint: `/account/balance/${this.token}?address=${address}`,
|
|
44
|
+
endpoint: `/account/balance/${this.token}?address=${encodeURIComponent(address)}`,
|
|
41
45
|
allowedStatuses: [200, 404],
|
|
42
46
|
});
|
|
43
47
|
return balance.winc
|
|
@@ -50,6 +54,31 @@ class TurboUnauthenticatedPaymentService {
|
|
|
50
54
|
receivedApprovals: [],
|
|
51
55
|
};
|
|
52
56
|
}
|
|
57
|
+
async getFreeStatus(address) {
|
|
58
|
+
const status = await this.httpService.get({
|
|
59
|
+
endpoint: `/account/free?address=${encodeURIComponent(address)}`,
|
|
60
|
+
allowedStatuses: [200, 404],
|
|
61
|
+
});
|
|
62
|
+
// Normalize: preserve a legitimate `0` (free tier off) or `null` (unlimited),
|
|
63
|
+
// and coerce a missing field (e.g. a 404 body) to `null`.
|
|
64
|
+
return { bytesRemaining: status?.bytesRemaining ?? null };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Returns the ArNS names a wallet owns or controls -- both custodial names
|
|
68
|
+
* bought via Turbo's ArNS-with-credits feature (Turbo may spawn and hold
|
|
69
|
+
* the ANT on the caller's behalf, depending on the buy) and self-custody
|
|
70
|
+
* names, in one list. See `TurboArNSName` for field semantics, including
|
|
71
|
+
* the `custodial` flag distinguishing the two.
|
|
72
|
+
*
|
|
73
|
+
* To read a name's current records or lease/expiration state, use
|
|
74
|
+
* `@ar.io/sdk` directly against the returned `antId` -- it talks to the
|
|
75
|
+
* chain directly and needs no round-trip through this SDK/backend.
|
|
76
|
+
*/
|
|
77
|
+
getArNSNames(address) {
|
|
78
|
+
return this.httpService.get({
|
|
79
|
+
endpoint: `/arns/my-names/${encodeURIComponent(address)}`,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
53
82
|
getFiatRates() {
|
|
54
83
|
return this.httpService.get({
|
|
55
84
|
endpoint: '/rates',
|
|
@@ -94,6 +123,206 @@ class TurboUnauthenticatedPaymentService {
|
|
|
94
123
|
equivalentWincTokenAmount: actualPaymentAmount.toString(),
|
|
95
124
|
};
|
|
96
125
|
}
|
|
126
|
+
async getArNSPriceForName(params) {
|
|
127
|
+
// `async` so a validation failure surfaces as a rejected promise (consistent
|
|
128
|
+
// with `purchaseArNSName`) rather than a synchronous throw.
|
|
129
|
+
this.validateArNSPurchaseParams(params);
|
|
130
|
+
const price = await this.httpService.get({
|
|
131
|
+
endpoint: `/arns/price/${params.intent.toLowerCase()}/${encodeURIComponent(params.name)}${this.buildArNSPurchaseQuery(params)}`,
|
|
132
|
+
});
|
|
133
|
+
// Normalize the figure to charge into ONE field. `winc` is the name only
|
|
134
|
+
// and excludes the ANT spawn surcharge — for a Buy-Name that surcharge can
|
|
135
|
+
// exceed the name's own price, so a caller reading `winc` silently
|
|
136
|
+
// under-quotes every purchase. Surfacing `wincTotal` makes the correct
|
|
137
|
+
// field the obvious one.
|
|
138
|
+
return {
|
|
139
|
+
...price,
|
|
140
|
+
wincTotal: price.wincTotalWithAntSpawn ?? price.winc,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Preview the flat credits margin one of the eight non-purchase actions
|
|
145
|
+
* (everything except Buy-Name/Extend-Lease/Upgrade-Name/
|
|
146
|
+
* Increase-Undername-Limit) will debit, without creating it. Those four
|
|
147
|
+
* purchase actions are priced by `getArNSPriceForName` instead — their
|
|
148
|
+
* cost is dominated by the ARIO purchase, not this margin, so this route
|
|
149
|
+
* rejects them.
|
|
150
|
+
*
|
|
151
|
+
* No signature required: this mirrors `getArNSPriceForName`'s read-only,
|
|
152
|
+
* unauthenticated shape rather than `getArNSActionStatus`'s (which happens
|
|
153
|
+
* to live on the authenticated client today despite needing no signature
|
|
154
|
+
* either).
|
|
155
|
+
*/
|
|
156
|
+
async getArNSActionPrice(action) {
|
|
157
|
+
return this.httpService.get({
|
|
158
|
+
endpoint: `/arns/actions/${action}/price`,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Fail fast (client-side) on malformed ArNS requests so JS callers that bypass
|
|
163
|
+
* the compile-time intent unions get a clear `ProvidedInputError` instead of an
|
|
164
|
+
* opaque service 4xx. Enforces the required fields per intent:
|
|
165
|
+
* - `Buy-Name`: `type` ('lease' | 'permabuy'); leases also need `years`.
|
|
166
|
+
* `processId` is OPTIONAL — omit it to have the bundler custodially
|
|
167
|
+
* provision the ANT (Turbo owns it), supply it for a user-owned ANT.
|
|
168
|
+
* - `Extend-Lease`: positive `years`
|
|
169
|
+
* - `Increase-Undername-Limit`: positive `increaseQty`
|
|
170
|
+
* - `Upgrade-Name`: just `name`
|
|
171
|
+
*/
|
|
172
|
+
validateArNSPurchaseParams(params) {
|
|
173
|
+
const p = params;
|
|
174
|
+
if (!types_js_1.arNSPurchaseIntents.includes(p.intent)) {
|
|
175
|
+
throw new errors_js_1.ProvidedInputError(`Invalid ArNS intent '${p.intent}'. Expected one of: ${types_js_1.arNSPurchaseIntents.join(', ')}.`);
|
|
176
|
+
}
|
|
177
|
+
if (typeof p.name !== 'string' || p.name.length === 0) {
|
|
178
|
+
throw new errors_js_1.ProvidedInputError('An ArNS `name` is required.');
|
|
179
|
+
}
|
|
180
|
+
const isPositiveNumber = (v) => typeof v === 'number' && Number.isFinite(v) && v > 0;
|
|
181
|
+
switch (p.intent) {
|
|
182
|
+
case 'Buy-Name':
|
|
183
|
+
if (p.type !== 'lease' && p.type !== 'permabuy') {
|
|
184
|
+
throw new errors_js_1.ProvidedInputError("Buy-Name requires a `type` of 'lease' or 'permabuy'.");
|
|
185
|
+
}
|
|
186
|
+
// `processId` is optional for Buy-Name: omitting it drives the
|
|
187
|
+
// bundler's custodial provisioning path (Turbo spawns + owns the ANT).
|
|
188
|
+
// If supplied it must be a non-empty string (user-owned ANT).
|
|
189
|
+
if (p.processId !== undefined &&
|
|
190
|
+
(typeof p.processId !== 'string' || p.processId.length === 0)) {
|
|
191
|
+
throw new errors_js_1.ProvidedInputError('Buy-Name `processId`, when provided, must be a non-empty string (the ANT the name resolves to).');
|
|
192
|
+
}
|
|
193
|
+
if (p.type === 'lease' && !isPositiveNumber(p.years)) {
|
|
194
|
+
throw new errors_js_1.ProvidedInputError('A lease `Buy-Name` requires a positive `years`.');
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
197
|
+
case 'Extend-Lease':
|
|
198
|
+
if (!isPositiveNumber(p.years)) {
|
|
199
|
+
throw new errors_js_1.ProvidedInputError('Extend-Lease requires a positive `years`.');
|
|
200
|
+
}
|
|
201
|
+
break;
|
|
202
|
+
case 'Increase-Undername-Limit':
|
|
203
|
+
if (!isPositiveNumber(p.increaseQty)) {
|
|
204
|
+
throw new errors_js_1.ProvidedInputError('Increase-Undername-Limit requires a positive `increaseQty`.');
|
|
205
|
+
}
|
|
206
|
+
break;
|
|
207
|
+
case 'Upgrade-Name':
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
getArNSPurchaseStatus({ nonce, }) {
|
|
212
|
+
return this.httpService.get({
|
|
213
|
+
endpoint: `/arns/purchase/${encodeURIComponent(nonce)}`,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
buildArNSPurchaseQuery(input) {
|
|
217
|
+
// The intent-specific union members each carry only their own fields; read
|
|
218
|
+
// them through a single widened view rather than narrowing per intent.
|
|
219
|
+
const { type, years, increaseQty, processId, paidBy } = input;
|
|
220
|
+
const params = new URLSearchParams();
|
|
221
|
+
if (type !== undefined)
|
|
222
|
+
params.set('type', type);
|
|
223
|
+
if (years !== undefined)
|
|
224
|
+
params.set('years', `${years}`);
|
|
225
|
+
if (increaseQty !== undefined)
|
|
226
|
+
params.set('increaseQty', `${increaseQty}`);
|
|
227
|
+
if (processId !== undefined)
|
|
228
|
+
params.set('processId', processId);
|
|
229
|
+
if (paidBy !== undefined) {
|
|
230
|
+
for (const payer of Array.isArray(paidBy) ? paidBy : [paidBy]) {
|
|
231
|
+
params.append('paidBy', payer);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const query = params.toString();
|
|
235
|
+
return query.length > 0 ? `?${query}` : '';
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Quote a fiat (Stripe) ArNS purchase — buy a name with a credit card in one
|
|
239
|
+
* step, with no Turbo Credits top-up in between.
|
|
240
|
+
*
|
|
241
|
+
* Returns the recorded `purchaseQuote` (its `nonce` is what
|
|
242
|
+
* `getArNSPurchaseStatus` polls) plus the Stripe `paymentSession` to complete
|
|
243
|
+
* payment with. For `payment-intent`, confirm client-side with
|
|
244
|
+
* `stripe.confirmCardPayment(paymentSession.client_secret, ...)`, then poll
|
|
245
|
+
* the nonce until the purchase reports success or failure.
|
|
246
|
+
*
|
|
247
|
+
* Throws {@link FiatPaymentsDisabledError} when the service has Stripe turned
|
|
248
|
+
* off (normal in the testnet sandbox) so callers can fall back to the
|
|
249
|
+
* credit-paid path without string-matching a generic 503.
|
|
250
|
+
*/
|
|
251
|
+
async getArNSFiatPurchaseQuote(params) {
|
|
252
|
+
this.validateArNSPurchaseParams(params);
|
|
253
|
+
const { address, currency, method = 'payment-intent', promoCodes = [], } = params;
|
|
254
|
+
if (typeof address !== 'string' || address.length === 0) {
|
|
255
|
+
throw new errors_js_1.ProvidedInputError('A destination `address` is required for a fiat ArNS purchase quote.');
|
|
256
|
+
}
|
|
257
|
+
if (!(0, types_js_1.isCurrency)(currency)) {
|
|
258
|
+
throw new errors_js_1.ProvidedInputError(`Invalid currency '${currency}'. Supported: ${types_js_1.fiatCurrencyTypes.join(', ')}`);
|
|
259
|
+
}
|
|
260
|
+
// Every interpolated segment is encoded. Five user-controlled values land in
|
|
261
|
+
// the path here, and an unencoded one (e.g. a name or address containing
|
|
262
|
+
// `../`) would silently retarget the request at another route.
|
|
263
|
+
const segments = [
|
|
264
|
+
method,
|
|
265
|
+
address,
|
|
266
|
+
currency,
|
|
267
|
+
params.intent,
|
|
268
|
+
params.name,
|
|
269
|
+
].map((segment) => encodeURIComponent(segment));
|
|
270
|
+
const query = this.buildArNSFiatQuoteQuery(params, promoCodes);
|
|
271
|
+
try {
|
|
272
|
+
return await this.httpService.get({
|
|
273
|
+
endpoint: `/arns/quote/${segments.join('/')}${query}`,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
// The service returns 503 both for "Stripe is disabled" and for internal
|
|
278
|
+
// errors, so the body is what disambiguates them.
|
|
279
|
+
if (error instanceof errors_js_1.FailedRequestError &&
|
|
280
|
+
error.status === 503 &&
|
|
281
|
+
/Fiat \(Stripe\).*disabled/i.test(error.message)) {
|
|
282
|
+
throw new errors_js_1.FiatPaymentsDisabledError(error.message);
|
|
283
|
+
}
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Query string for a fiat quote. Distinct from `buildArNSPurchaseQuery`
|
|
289
|
+
* because this route takes `uiMode` + its paired URLs and has no `paidBy`
|
|
290
|
+
* (fiat has no delegated payer), and because promo codes must be REPEATED
|
|
291
|
+
* params here: the service reads them with `parseQueryParams`, which treats a
|
|
292
|
+
* comma-joined string as one code rather than several.
|
|
293
|
+
*/
|
|
294
|
+
buildArNSFiatQuoteQuery(params, promoCodes) {
|
|
295
|
+
const { type, years, increaseQty, processId } = params;
|
|
296
|
+
const search = new URLSearchParams();
|
|
297
|
+
if (type !== undefined)
|
|
298
|
+
search.set('type', type);
|
|
299
|
+
if (years !== undefined)
|
|
300
|
+
search.set('years', `${years}`);
|
|
301
|
+
if (increaseQty !== undefined)
|
|
302
|
+
search.set('increaseQty', `${increaseQty}`);
|
|
303
|
+
if (processId !== undefined)
|
|
304
|
+
search.set('processId', processId);
|
|
305
|
+
const uiMode = params.uiMode;
|
|
306
|
+
if (uiMode !== undefined)
|
|
307
|
+
search.set('uiMode', uiMode);
|
|
308
|
+
if (uiMode === 'embedded') {
|
|
309
|
+
const { returnUrl } = params;
|
|
310
|
+
if (returnUrl !== undefined)
|
|
311
|
+
search.set('returnUrl', returnUrl);
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
const { successUrl, cancelUrl } = params;
|
|
315
|
+
if (successUrl !== undefined)
|
|
316
|
+
search.set('successUrl', successUrl);
|
|
317
|
+
if (cancelUrl !== undefined)
|
|
318
|
+
search.set('cancelUrl', cancelUrl);
|
|
319
|
+
}
|
|
320
|
+
for (const code of promoCodes) {
|
|
321
|
+
search.append('promoCode', code);
|
|
322
|
+
}
|
|
323
|
+
const query = search.toString();
|
|
324
|
+
return query.length > 0 ? `?${query}` : '';
|
|
325
|
+
}
|
|
97
326
|
appendPromoCodesToQuery(promoCodes) {
|
|
98
327
|
const promoCodesQuery = promoCodes.join(',');
|
|
99
328
|
return promoCodesQuery ? `promoCode=${promoCodesQuery}` : '';
|
|
@@ -194,7 +423,7 @@ class TurboUnauthenticatedPaymentService {
|
|
|
194
423
|
}
|
|
195
424
|
async getCreditShareApprovals({ userAddress, }) {
|
|
196
425
|
const response = await this.httpService.get({
|
|
197
|
-
endpoint: `/account/approvals/get?userAddress=${userAddress}`,
|
|
426
|
+
endpoint: `/account/approvals/get?userAddress=${encodeURIComponent(userAddress)}`,
|
|
198
427
|
allowedStatuses: [200, 404],
|
|
199
428
|
});
|
|
200
429
|
if (response?.givenApprovals === undefined &&
|
|
@@ -259,6 +488,323 @@ class TurboAuthenticatedPaymentService extends TurboUnauthenticatedPaymentServic
|
|
|
259
488
|
userAddress ??= await this.signer.getNativeAddress();
|
|
260
489
|
return super.getBalance(userAddress);
|
|
261
490
|
}
|
|
491
|
+
/**
|
|
492
|
+
* Quote a fiat (Stripe) ArNS purchase. `address` defaults to this signer's
|
|
493
|
+
* native address — the wallet that will own the name — so the common case
|
|
494
|
+
* needs no address at all. Pass one explicitly to buy on another wallet's
|
|
495
|
+
* behalf; the route takes the destination as a path param and requires no
|
|
496
|
+
* signature, which is why it is available unauthenticated too.
|
|
497
|
+
*/
|
|
498
|
+
async getArNSFiatPurchaseQuote(params) {
|
|
499
|
+
const address = params.address ?? (await this.signer.getNativeAddress());
|
|
500
|
+
return super.getArNSFiatPurchaseQuote({
|
|
501
|
+
...params,
|
|
502
|
+
address,
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
async getFreeStatus(userAddress) {
|
|
506
|
+
userAddress ??= await this.signer.getNativeAddress();
|
|
507
|
+
return super.getFreeStatus(userAddress);
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* The signer's OWN completed top-up history (crypto + fiat), merged newest
|
|
511
|
+
* first and keyset-paginated. This is a SIGNED GET: unlike `getBalance` /
|
|
512
|
+
* `getFreeStatus` (which name a wallet by `?address=`), payment history is
|
|
513
|
+
* self-scoped and returns only the rows belonging to the signing wallet — the
|
|
514
|
+
* service reads the address from the signature, never a query param.
|
|
515
|
+
*
|
|
516
|
+
* We sign the bare nonce (no action-binding of `limit`/`cursor`) to match the
|
|
517
|
+
* service's `verifySignature` middleware; the pagination params ride in the
|
|
518
|
+
* query string. Pass `cursor` from a prior response to fetch the next page.
|
|
519
|
+
*/
|
|
520
|
+
async getPaymentHistory({ limit, cursor, } = {}) {
|
|
521
|
+
const headers = await this.signer.generateSignedRequestHeaders();
|
|
522
|
+
const query = new URLSearchParams();
|
|
523
|
+
if (limit !== undefined) {
|
|
524
|
+
query.set('limit', `${limit}`);
|
|
525
|
+
}
|
|
526
|
+
if (cursor !== undefined) {
|
|
527
|
+
query.set('cursor', cursor);
|
|
528
|
+
}
|
|
529
|
+
const queryString = query.toString();
|
|
530
|
+
return this.httpService.get({
|
|
531
|
+
endpoint: `/account/payments${queryString ? `?${queryString}` : ''}`,
|
|
532
|
+
headers,
|
|
533
|
+
allowedStatuses: [200],
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Buy / extend / upgrade an ArNS name, paying with the signer's Turbo credit
|
|
538
|
+
* balance. The bundler performs the on-chain ARIO purchase and debits credits;
|
|
539
|
+
* a `402` (FailedRequestError.status === 402) indicates insufficient credits.
|
|
540
|
+
*/
|
|
541
|
+
// ===== ArNS actions — the sponsored surface =====
|
|
542
|
+
//
|
|
543
|
+
// Every ArNS operation is an ACTION, and an action has exactly one of two
|
|
544
|
+
// shapes, chosen by the SERVER rather than the caller: either Turbo already
|
|
545
|
+
// holds the authority (`completed`), or the ANT owner must sign a transaction
|
|
546
|
+
// Turbo has already fee-payer-signed (`awaiting-signature`).
|
|
547
|
+
//
|
|
548
|
+
// The shape is not stable per action, which is why callers must branch on
|
|
549
|
+
// `status` and never on which action they asked for: `set-record` completes
|
|
550
|
+
// alone while Turbo is a controller, and degrades to `awaiting-signature`
|
|
551
|
+
// the moment the customer revokes Turbo.
|
|
552
|
+
//
|
|
553
|
+
// This replaced `/arns/purchase/{intent}/{name}`, `/arns/transfer/{antId}`
|
|
554
|
+
// and `/arns/manage/*`, which were deleted along with Turbo-custodial ANTs.
|
|
555
|
+
// Turbo now takes custody of nothing: every ANT is minted straight to the
|
|
556
|
+
// customer.
|
|
557
|
+
/**
|
|
558
|
+
* Create an action. Returns `completed` or `awaiting-signature`.
|
|
559
|
+
*
|
|
560
|
+
* Credits are debited HERE, not at `/sign`. Capture the returned `nonce`
|
|
561
|
+
* before prompting for a signature: it is the idempotency key, and polling
|
|
562
|
+
* it is how you resume. Never re-create an action to "retry" — that debits
|
|
563
|
+
* a second time. An abandoned action is refunded automatically.
|
|
564
|
+
*/
|
|
565
|
+
async createArNSAction(action, params = {}, ownerProof) {
|
|
566
|
+
const nonce = (0, uuid_js_1.uuidV4)();
|
|
567
|
+
const headers = {
|
|
568
|
+
...(await this.signer.generateSignedRequestHeaders(nonce)),
|
|
569
|
+
'content-type': 'application/json',
|
|
570
|
+
};
|
|
571
|
+
// Record actions carry a SECOND signature, from the ANT owner's Solana key
|
|
572
|
+
// over a different message. It travels in its own `x-owner-*` headers
|
|
573
|
+
// because two signatures cannot share one header set.
|
|
574
|
+
if (ownerProof !== undefined) {
|
|
575
|
+
Object.assign(headers, await (0, arnsActions_js_1.arNSOwnerProofHeaders)(ownerProof.owner, ownerProof.message, (0, uuid_js_1.uuidV4)()));
|
|
576
|
+
}
|
|
577
|
+
try {
|
|
578
|
+
return await this.httpService.post({
|
|
579
|
+
endpoint: `/arns/actions/${action}`,
|
|
580
|
+
headers,
|
|
581
|
+
data: Buffer.from(JSON.stringify(params)),
|
|
582
|
+
// Non-idempotent signed write that has already debited. A blind retry
|
|
583
|
+
// risks paying twice for one name; poll the nonce instead.
|
|
584
|
+
retry: false,
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
catch (error) {
|
|
588
|
+
if (error instanceof errors_js_1.FailedRequestError && error.status === 402) {
|
|
589
|
+
throw new errors_js_1.InsufficientCreditsError(error.message);
|
|
590
|
+
}
|
|
591
|
+
throw error;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Submit the owner-signed transaction for an `awaiting-signature` action.
|
|
596
|
+
*
|
|
597
|
+
* `signedTransaction` is the FULL serialized transaction, base64 — not just
|
|
598
|
+
* the signature. Replaying a completed action returns `alreadyCompleted:
|
|
599
|
+
* true` rather than buying twice, so this is safe to call again if a
|
|
600
|
+
* response is lost.
|
|
601
|
+
*/
|
|
602
|
+
async signArNSAction(nonce, signedTransaction) {
|
|
603
|
+
return this.httpService.post({
|
|
604
|
+
endpoint: `/arns/actions/${nonce}/sign`,
|
|
605
|
+
headers: {
|
|
606
|
+
...(await this.signer.generateSignedRequestHeaders((0, uuid_js_1.uuidV4)())),
|
|
607
|
+
'content-type': 'application/json',
|
|
608
|
+
},
|
|
609
|
+
data: Buffer.from(JSON.stringify({ transaction: signedTransaction })),
|
|
610
|
+
retry: false,
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Status of an action by nonce. Open — no signature required — so it works
|
|
615
|
+
* from a status page or callback handler that never holds the payer's key.
|
|
616
|
+
*
|
|
617
|
+
* Terminal success carries `messageId`; terminal failure carries
|
|
618
|
+
* `failedDate`.
|
|
619
|
+
*/
|
|
620
|
+
async getArNSActionStatus(nonce) {
|
|
621
|
+
return this.httpService.get({
|
|
622
|
+
endpoint: `/arns/actions/${nonce}`,
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Run an action to a terminal state, signing if the server asks for it.
|
|
627
|
+
*
|
|
628
|
+
* This is the two-shape branch, once, in one place — so callers cannot
|
|
629
|
+
* hardcode which actions need a signature and break when a customer
|
|
630
|
+
* exercises ownership.
|
|
631
|
+
*/
|
|
632
|
+
async completeArNSAction(action, params, owner, opts = {}, ownerProofMessage) {
|
|
633
|
+
const created = await this.createArNSAction(action, params, owner !== undefined && ownerProofMessage !== undefined
|
|
634
|
+
? { owner, message: ownerProofMessage }
|
|
635
|
+
: undefined);
|
|
636
|
+
// Fires before any wallet prompt: the action is already debited, so the
|
|
637
|
+
// caller needs the nonce persisted even if the user walks away here.
|
|
638
|
+
await opts.onNonce?.(created.nonce);
|
|
639
|
+
if (created.status === 'completed')
|
|
640
|
+
return created;
|
|
641
|
+
if (owner === undefined) {
|
|
642
|
+
throw new Error(`ArNS action "${action}" requires the ANT owner's signature, but no owner signer was provided. ` +
|
|
643
|
+
`Pass \`owner\`, or drive createArNSAction/signArNSAction yourself. ` +
|
|
644
|
+
`Nonce ${created.nonce} is already debited — poll it rather than re-creating.`);
|
|
645
|
+
}
|
|
646
|
+
const signed = await owner.signTransaction(created.transaction);
|
|
647
|
+
return this.signArNSAction(created.nonce, signed);
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Buy a name. The ANT is minted straight to `owner` — Turbo never holds it.
|
|
651
|
+
*
|
|
652
|
+
* This is the ONLY action that always needs the owner's signature:
|
|
653
|
+
* `ario_ant::initialize` is the one instruction in the whole lifecycle that
|
|
654
|
+
* requires the ANT owner's key. The customer signs once, here, and never
|
|
655
|
+
* again unless they change controllers or transfer the name.
|
|
656
|
+
*
|
|
657
|
+
* The owner needs a Solana key to sign with, NOT a funded one — Turbo pays
|
|
658
|
+
* every lamport of fee and rent.
|
|
659
|
+
*/
|
|
660
|
+
async buyArNSName({ name, owner, type = 'lease', years, paidBy, onNonce, }) {
|
|
661
|
+
return this.completeArNSAction('buy-name', {
|
|
662
|
+
name,
|
|
663
|
+
ownerAddress: await owner.getAddress(),
|
|
664
|
+
type,
|
|
665
|
+
...(years !== undefined ? { years } : {}),
|
|
666
|
+
...(paidBy !== undefined ? { paidBy } : {}),
|
|
667
|
+
}, owner, { onNonce });
|
|
668
|
+
}
|
|
669
|
+
/** Extend a lease. Permissionless on chain — no owner signature needed. */
|
|
670
|
+
async extendArNSLease({ name, years, paidBy, onNonce, }) {
|
|
671
|
+
return this.completeArNSAction('extend-lease', { name, years, ...(paidBy !== undefined ? { paidBy } : {}) }, undefined, { onNonce });
|
|
672
|
+
}
|
|
673
|
+
/** Upgrade a lease to a permanent name. No owner signature needed. */
|
|
674
|
+
async upgradeArNSName({ name, paidBy, onNonce, }) {
|
|
675
|
+
return this.completeArNSAction('upgrade-name', { name, ...(paidBy !== undefined ? { paidBy } : {}) }, undefined, { onNonce });
|
|
676
|
+
}
|
|
677
|
+
/** Raise the undername limit. No owner signature needed. */
|
|
678
|
+
async increaseArNSUndernameLimit({ name, increaseQty, paidBy, onNonce, }) {
|
|
679
|
+
return this.completeArNSAction('increase-undername-limit', { name, increaseQty, ...(paidBy !== undefined ? { paidBy } : {}) }, undefined, { onNonce });
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Point a name (or undername) at an Arweave transaction.
|
|
683
|
+
*
|
|
684
|
+
* Costs a small credit margin — never SOL, which Turbo sponsors. Completes in one call while Turbo is
|
|
685
|
+
* a controller of the ANT, and returns `awaiting-signature` once the customer
|
|
686
|
+
* has revoked Turbo, at which point `owner` signs it themselves. Both paths
|
|
687
|
+
* are handled here.
|
|
688
|
+
*
|
|
689
|
+
* The owner proof is required EITHER WAY: Turbo is directing its own
|
|
690
|
+
* controller authority over an asset someone else owns, so nothing on chain
|
|
691
|
+
* records the owner's consent and we demand it. It is a MESSAGE signature,
|
|
692
|
+
* not a transaction — cheap and offline, but still a wallet prompt.
|
|
693
|
+
*/
|
|
694
|
+
async setArNSRecord({ antId, owner, transactionId, undername = '@', ttlSeconds = 3600, onNonce, }) {
|
|
695
|
+
return this.completeArNSAction('set-record', {
|
|
696
|
+
antId,
|
|
697
|
+
ownerAddress: await owner.getAddress(),
|
|
698
|
+
transactionId,
|
|
699
|
+
undername,
|
|
700
|
+
ttlSeconds,
|
|
701
|
+
}, owner, { onNonce }, (0, arnsActions_js_1.buildArNSCustodyMessage)('set-record', [
|
|
702
|
+
antId,
|
|
703
|
+
undername,
|
|
704
|
+
transactionId,
|
|
705
|
+
String(ttlSeconds),
|
|
706
|
+
]));
|
|
707
|
+
}
|
|
708
|
+
/** Remove a record (an undername). Costs credits, never SOL. */
|
|
709
|
+
async removeArNSRecord({ antId, owner, undername, onNonce, }) {
|
|
710
|
+
return this.completeArNSAction('remove-record', { antId, ownerAddress: await owner.getAddress(), undername }, owner, { onNonce }, (0, arnsActions_js_1.buildArNSCustodyMessage)('remove-record', [antId, undername]));
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Edit a RECORD's metadata — its display name, logo, description, keywords.
|
|
714
|
+
*
|
|
715
|
+
* Costs a small credit margin (never SOL), and is owner-or-controller on
|
|
716
|
+
* chain, so it behaves exactly like
|
|
717
|
+
* {@link setArNSRecord}: Turbo-alone while it is a controller, owner-signed
|
|
718
|
+
* after a revoke.
|
|
719
|
+
*
|
|
720
|
+
* Fields are TRI-STATE. Omit one to leave it unchanged; pass `null` to clear
|
|
721
|
+
* it. Those are bound distinctly by the owner proof, so "clear the
|
|
722
|
+
* description" and "set it to empty" are different authorizations.
|
|
723
|
+
*
|
|
724
|
+
* Note this is RECORD metadata. ANT-level metadata (the ANT's own name,
|
|
725
|
+
* ticker, description, keywords, logo) is NOT sponsored and stays on the
|
|
726
|
+
* direct-signer path via `@ar.io/sdk`.
|
|
727
|
+
*/
|
|
728
|
+
async setArNSRecordMetadata({ antId, owner, undername = '@', displayName, recordLogo, recordDescription, recordKeywords, onNonce, }) {
|
|
729
|
+
return this.completeArNSAction('set-record-metadata', {
|
|
730
|
+
antId,
|
|
731
|
+
ownerAddress: await owner.getAddress(),
|
|
732
|
+
undername,
|
|
733
|
+
// Sent explicitly, including `null`, so the server sees the same
|
|
734
|
+
// tri-state the proof was signed over.
|
|
735
|
+
...(displayName !== undefined ? { displayName } : {}),
|
|
736
|
+
...(recordLogo !== undefined ? { recordLogo } : {}),
|
|
737
|
+
...(recordDescription !== undefined ? { recordDescription } : {}),
|
|
738
|
+
...(recordKeywords !== undefined ? { recordKeywords } : {}),
|
|
739
|
+
}, owner, { onNonce }, (0, arnsActions_js_1.buildArNSCustodyMessage)('set-record-metadata', [
|
|
740
|
+
antId,
|
|
741
|
+
undername,
|
|
742
|
+
(0, arnsActions_js_1.arNSMetadataField)(displayName),
|
|
743
|
+
(0, arnsActions_js_1.arNSMetadataField)(recordLogo),
|
|
744
|
+
(0, arnsActions_js_1.arNSMetadataField)(recordDescription),
|
|
745
|
+
(0, arnsActions_js_1.arNSKeywordsField)(recordKeywords),
|
|
746
|
+
]));
|
|
747
|
+
}
|
|
748
|
+
/** Clear a record's metadata. Costs credits, never SOL; same two-shape rules. */
|
|
749
|
+
async removeArNSRecordMetadata({ antId, owner, undername, onNonce, }) {
|
|
750
|
+
return this.completeArNSAction('remove-record-metadata', { antId, ownerAddress: await owner.getAddress(), undername }, owner, { onNonce }, (0, arnsActions_js_1.buildArNSCustodyMessage)('remove-record-metadata', [antId, undername]));
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Hand ONE record to another address.
|
|
754
|
+
*
|
|
755
|
+
* Distinct from {@link transferArNSAnt}, which hands over the whole ANT and
|
|
756
|
+
* every record on it. Confusing the two gives away far more than intended.
|
|
757
|
+
*/
|
|
758
|
+
async transferArNSRecord({ antId, owner, undername, target, onNonce, }) {
|
|
759
|
+
return this.completeArNSAction('transfer-record', { antId, ownerAddress: await owner.getAddress(), undername, target }, owner, { onNonce }, (0, arnsActions_js_1.buildArNSCustodyMessage)('transfer-record', [antId, undername, target]));
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Grant controller rights on the ANT. Omit `target` for Turbo itself, which
|
|
763
|
+
* is what makes `setArNSRecord` a single call.
|
|
764
|
+
*
|
|
765
|
+
* Owner-signed: changing an ANT's access control is an owner-only
|
|
766
|
+
* instruction. Costs a small credit margin; Turbo funds the ACL page growth
|
|
767
|
+
* in SOL.
|
|
768
|
+
*/
|
|
769
|
+
async addArNSController({ antId, owner, target, onNonce, }) {
|
|
770
|
+
return this.completeArNSAction('add-controller', {
|
|
771
|
+
antId,
|
|
772
|
+
ownerAddress: await owner.getAddress(),
|
|
773
|
+
...(target !== undefined ? { target } : {}),
|
|
774
|
+
}, owner, { onNonce });
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Revoke controller rights — the escape hatch that keeps "Turbo is not a
|
|
778
|
+
* custodian" honest.
|
|
779
|
+
*
|
|
780
|
+
* Always available, and needs nothing from Turbo but the fee. Costs a small
|
|
781
|
+
* credit margin rather than SOL.
|
|
782
|
+
* After revoking, `setArNSRecord` keeps working: it simply starts returning
|
|
783
|
+
* `awaiting-signature` so the owner signs their own record writes.
|
|
784
|
+
*/
|
|
785
|
+
async removeArNSController({ antId, owner, target, onNonce, }) {
|
|
786
|
+
return this.completeArNSAction('remove-controller', {
|
|
787
|
+
antId,
|
|
788
|
+
ownerAddress: await owner.getAddress(),
|
|
789
|
+
...(target !== undefined ? { target } : {}),
|
|
790
|
+
}, owner, { onNonce });
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Hand the ANT to a new owner. Irreversible: after this lands, `owner` no
|
|
794
|
+
* longer controls the name. Owner-signed, and sponsored like the rest.
|
|
795
|
+
*/
|
|
796
|
+
async transferArNSAnt({ antId, owner, target, onNonce, }) {
|
|
797
|
+
return this.completeArNSAction('transfer', { antId, ownerAddress: await owner.getAddress(), target }, owner, { onNonce });
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Defaults to the signer's own address when `userAddress` is omitted
|
|
801
|
+
* (`null`/`undefined`). Passing `''` does NOT trigger this default --
|
|
802
|
+
* mirrors `getBalance`'s existing behavior above.
|
|
803
|
+
*/
|
|
804
|
+
async getArNSNames(userAddress) {
|
|
805
|
+
userAddress ??= await this.signer.getNativeAddress();
|
|
806
|
+
return super.getArNSNames(userAddress);
|
|
807
|
+
}
|
|
262
808
|
async getCreditShareApprovals({ userAddress, }) {
|
|
263
809
|
userAddress ??= await this.signer.getNativeAddress();
|
|
264
810
|
return super.getCreditShareApprovals({ userAddress });
|
package/lib/cjs/common/signer.js
CHANGED
|
@@ -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':
|
|
@@ -73,15 +72,27 @@ class TurboDataItemAbstractSigner {
|
|
|
73
72
|
return (0, base64_js_1.ownerToAddress)(owner);
|
|
74
73
|
}
|
|
75
74
|
}
|
|
76
|
-
async generateSignedRequestHeaders(
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
async generateSignedRequestHeaders(
|
|
76
|
+
// Callers may supply the nonce (e.g. a UUID required by some routes); the
|
|
77
|
+
// nonce round-trips to the service in `x-nonce` unchanged.
|
|
78
|
+
nonce = (0, crypto_2.randomBytes)(16).toString('hex'),
|
|
79
|
+
// Optional ACTION-BINDING data prepended to the nonce for SIGNING only (not
|
|
80
|
+
// sent): the service reconstructs the same string from the request and
|
|
81
|
+
// verifies the signature over `additionalData + nonce`. This binds the
|
|
82
|
+
// signature to a specific operation + params so it can't be replayed against
|
|
83
|
+
// a different request. Omitted → signs the bare nonce (unchanged behavior).
|
|
84
|
+
additionalData) {
|
|
85
|
+
const buffer = Buffer.from((additionalData ?? '') + nonce);
|
|
79
86
|
const signature = await this.signer.sign(Uint8Array.from(buffer));
|
|
80
87
|
const publicKey = (0, base64_js_1.toB64Url)(this.signer.publicKey);
|
|
81
88
|
return {
|
|
82
89
|
'x-public-key': publicKey,
|
|
83
90
|
'x-nonce': nonce,
|
|
84
91
|
'x-signature': (0, base64_js_1.toB64Url)(Buffer.from(signature)),
|
|
92
|
+
// Advertise the signature scheme so the service verifies with the right
|
|
93
|
+
// algorithm. Absent this, the server defaults to Arweave and every
|
|
94
|
+
// non-Arweave signed request (Ethereum, Solana, …) fails verification.
|
|
95
|
+
'x-signature-type': this.signer.signatureType.toString(),
|
|
85
96
|
};
|
|
86
97
|
}
|
|
87
98
|
async getPublicKey() {
|
|
@@ -160,13 +171,23 @@ class TurboDataItemAbstractSigner {
|
|
|
160
171
|
}
|
|
161
172
|
}
|
|
162
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
|
+
*/
|
|
163
184
|
async function makeX402Signer(arbundlesSigner) {
|
|
164
185
|
// Node: our SDK uses EthereumSigner with a raw private key
|
|
165
186
|
if (arbundlesSigner instanceof arbundles_1.EthereumSigner) {
|
|
166
187
|
return (0, viem_1.createWalletClient)({
|
|
167
188
|
account: (0, accounts_1.privateKeyToAccount)(('0x' +
|
|
168
189
|
Buffer.from(arbundlesSigner.key).toString('hex'))),
|
|
169
|
-
chain: chains_1.
|
|
190
|
+
chain: chains_1.base,
|
|
170
191
|
transport: (0, viem_1.http)(),
|
|
171
192
|
});
|
|
172
193
|
}
|
|
@@ -185,7 +206,7 @@ async function makeX402Signer(arbundlesSigner) {
|
|
|
185
206
|
const account = accounts[0];
|
|
186
207
|
return (0, viem_1.createWalletClient)({
|
|
187
208
|
account,
|
|
188
|
-
chain: chains_1.
|
|
209
|
+
chain: chains_1.base,
|
|
189
210
|
transport: (0, viem_1.custom)(provider),
|
|
190
211
|
});
|
|
191
212
|
}
|