@ardrive/turbo-sdk 1.42.0-alpha.7 → 1.42.0-alpha.9
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/commands/arns.js +37 -9
- package/lib/cjs/cli/options.js +7 -6
- package/lib/cjs/common/arnsActions.js +102 -0
- package/lib/cjs/common/payment.js +215 -80
- package/lib/cjs/common/turbo.js +60 -15
- package/lib/cjs/types.js +20 -1
- package/lib/esm/cli/commands/arns.js +37 -9
- package/lib/esm/cli/options.js +7 -6
- package/lib/esm/common/arnsActions.js +93 -0
- package/lib/esm/common/payment.js +215 -80
- package/lib/esm/common/turbo.js +60 -15
- package/lib/esm/types.js +19 -0
- package/lib/types/cli/commands/arns.d.ts +16 -27
- package/lib/types/cli/commands/arns.d.ts.map +1 -1
- package/lib/types/cli/options.d.ts +14 -11
- package/lib/types/cli/options.d.ts.map +1 -1
- package/lib/types/cli/types.d.ts +8 -4
- package/lib/types/cli/types.d.ts.map +1 -1
- package/lib/types/common/arnsActions.d.ts +43 -0
- package/lib/types/common/arnsActions.d.ts.map +1 -0
- package/lib/types/common/payment.d.ts +153 -33
- package/lib/types/common/payment.d.ts.map +1 -1
- package/lib/types/common/turbo.d.ts +91 -36
- package/lib/types/common/turbo.d.ts.map +1 -1
- package/lib/types/types.d.ts +202 -28
- package/lib/types/types.d.ts.map +1 -1
- package/package.json +1 -1
package/lib/cjs/common/turbo.js
CHANGED
|
@@ -69,6 +69,19 @@ class TurboUnauthenticatedClient {
|
|
|
69
69
|
getArNSPurchaseStatus(p) {
|
|
70
70
|
return this.paymentService.getArNSPurchaseStatus(p);
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Returns the ArNS names a wallet owns or controls via Turbo's custodial
|
|
74
|
+
* ArNS-with-credits feature. `custodial: true` on a returned name means
|
|
75
|
+
* Turbo still holds/manages its ANT (transfer/manage routes apply);
|
|
76
|
+
* `custodial: false` means self-custody (or an already-completed exit) and
|
|
77
|
+
* is informational only.
|
|
78
|
+
*
|
|
79
|
+
* To read a name's current records or lease/expiration state, use
|
|
80
|
+
* `@ar.io/sdk` directly against the returned `antId`.
|
|
81
|
+
*/
|
|
82
|
+
getArNSNames(address) {
|
|
83
|
+
return this.paymentService.getArNSNames(address);
|
|
84
|
+
}
|
|
72
85
|
/**
|
|
73
86
|
* Quote a fiat (Stripe) ArNS purchase — buy a name with a credit card in one
|
|
74
87
|
* step, no Turbo Credits top-up in between. Complete the returned
|
|
@@ -201,16 +214,22 @@ class TurboAuthenticatedClient extends TurboUnauthenticatedClient {
|
|
|
201
214
|
getPaymentHistory(params) {
|
|
202
215
|
return this.paymentService.getPaymentHistory(params);
|
|
203
216
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
217
|
+
// ===== ArNS actions (sponsored) =====
|
|
218
|
+
//
|
|
219
|
+
// Thin delegations. The two-shape branch and the owner-proof construction
|
|
220
|
+
// live in the payment service; see its comments for why callers must branch
|
|
221
|
+
// on `status` rather than on which action they asked for.
|
|
222
|
+
/** Create an action. Debits credits HERE — capture the nonce before signing. */
|
|
223
|
+
createArNSAction(action, params, ownerProof) {
|
|
224
|
+
return this.paymentService.createArNSAction(action, params, ownerProof);
|
|
210
225
|
}
|
|
211
|
-
/**
|
|
212
|
-
|
|
213
|
-
return this.paymentService.
|
|
226
|
+
/** Submit the owner-signed transaction (FULL serialized tx, base64). */
|
|
227
|
+
signArNSAction(nonce, signedTransaction) {
|
|
228
|
+
return this.paymentService.signArNSAction(nonce, signedTransaction);
|
|
229
|
+
}
|
|
230
|
+
/** Status by nonce — open, needs no signature. Use it to resume. */
|
|
231
|
+
getArNSActionStatus(nonce) {
|
|
232
|
+
return this.paymentService.getArNSActionStatus(nonce);
|
|
214
233
|
}
|
|
215
234
|
/**
|
|
216
235
|
* Quote a fiat (Stripe) ArNS purchase for this signer's wallet — a credit-card
|
|
@@ -224,27 +243,53 @@ class TurboAuthenticatedClient extends TurboUnauthenticatedClient {
|
|
|
224
243
|
getArNSFiatPurchaseQuote(params) {
|
|
225
244
|
return this.paymentService.getArNSFiatPurchaseQuote(params);
|
|
226
245
|
}
|
|
227
|
-
/**
|
|
246
|
+
/**
|
|
247
|
+
* Buy a name. The ANT is minted straight to `owner`; Turbo never holds it.
|
|
248
|
+
* The only action that always needs the owner's signature — once, ever.
|
|
249
|
+
*/
|
|
250
|
+
buyArNSName(params) {
|
|
251
|
+
return this.paymentService.buyArNSName(params);
|
|
252
|
+
}
|
|
253
|
+
/** Extend a lease. No owner signature needed. */
|
|
228
254
|
extendArNSLease(params) {
|
|
229
255
|
return this.paymentService.extendArNSLease(params);
|
|
230
256
|
}
|
|
231
|
-
/**
|
|
257
|
+
/** Raise the undername limit. No owner signature needed. */
|
|
232
258
|
increaseArNSUndernameLimit(params) {
|
|
233
259
|
return this.paymentService.increaseArNSUndernameLimit(params);
|
|
234
260
|
}
|
|
235
|
-
/**
|
|
261
|
+
/** Upgrade a lease to a permanent name. No owner signature needed. */
|
|
236
262
|
upgradeArNSName(params) {
|
|
237
263
|
return this.paymentService.upgradeArNSName(params);
|
|
238
264
|
}
|
|
239
|
-
|
|
240
|
-
return this.paymentService.transferArNSAnt(params);
|
|
241
|
-
}
|
|
265
|
+
/** Point a name (or undername) at data. Free; handles both shapes. */
|
|
242
266
|
setArNSRecord(params) {
|
|
243
267
|
return this.paymentService.setArNSRecord(params);
|
|
244
268
|
}
|
|
269
|
+
/** Remove a record. Free; handles both shapes. */
|
|
245
270
|
removeArNSRecord(params) {
|
|
246
271
|
return this.paymentService.removeArNSRecord(params);
|
|
247
272
|
}
|
|
273
|
+
/** Grant controller rights — omit `target` for Turbo itself. Free. */
|
|
274
|
+
addArNSController(params) {
|
|
275
|
+
return this.paymentService.addArNSController(params);
|
|
276
|
+
}
|
|
277
|
+
/** Revoke controller rights. Always available, always free. */
|
|
278
|
+
removeArNSController(params) {
|
|
279
|
+
return this.paymentService.removeArNSController(params);
|
|
280
|
+
}
|
|
281
|
+
/** Hand the ANT to a new owner. Irreversible. Free. */
|
|
282
|
+
transferArNSAnt(params) {
|
|
283
|
+
return this.paymentService.transferArNSAnt(params);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Returns the ArNS names owned or controlled by the connected signer's
|
|
287
|
+
* wallet (or the given `userAddress`) via Turbo's custodial
|
|
288
|
+
* ArNS-with-credits feature.
|
|
289
|
+
*/
|
|
290
|
+
getArNSNames(userAddress) {
|
|
291
|
+
return this.paymentService.getArNSNames(userAddress);
|
|
292
|
+
}
|
|
248
293
|
/**
|
|
249
294
|
* Returns a list of all credit share approvals for the user.
|
|
250
295
|
*/
|
package/lib/cjs/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.arNSFiatPurchaseMethods = exports.arNSPurchaseIntents = exports.validChunkingModes = exports.isJWK = exports.isWebUploadFolderParams = exports.isNodeUploadFolderParams = exports.multipartFinalizedStatus = exports.multipartFailedStatus = exports.multipartPendingStatus = exports.X402Funding = exports.OnDemandFunding = exports.ExistingBalanceFunding = exports.supportedEvmSignerTokens = exports.tokenTypes = exports.fiatCurrencyTypes = void 0;
|
|
3
|
+
exports.arNSFiatPurchaseMethods = exports.arNSActions = exports.arNSPurchaseIntents = exports.validChunkingModes = exports.isJWK = exports.isWebUploadFolderParams = exports.isNodeUploadFolderParams = exports.multipartFinalizedStatus = exports.multipartFailedStatus = exports.multipartPendingStatus = exports.X402Funding = exports.OnDemandFunding = exports.ExistingBalanceFunding = exports.supportedEvmSignerTokens = exports.tokenTypes = exports.fiatCurrencyTypes = void 0;
|
|
4
4
|
exports.isCurrency = isCurrency;
|
|
5
5
|
exports.isKyvePrivateKey = isKyvePrivateKey;
|
|
6
6
|
exports.isEthPrivateKey = isEthPrivateKey;
|
|
@@ -113,6 +113,25 @@ exports.arNSPurchaseIntents = [
|
|
|
113
113
|
'Increase-Undername-Limit',
|
|
114
114
|
'Upgrade-Name',
|
|
115
115
|
];
|
|
116
|
+
// ===== ArNS actions (sponsored — the current bundler surface) =====
|
|
117
|
+
/**
|
|
118
|
+
* The nine sponsored ArNS actions.
|
|
119
|
+
*
|
|
120
|
+
* NOT included, because the bundler does not sponsor them: `primary-name`,
|
|
121
|
+
* `release-name`, `reassign`, and ANT metadata (name/description/keywords/logo).
|
|
122
|
+
* Those stay on the direct-signer path and cost the user SOL.
|
|
123
|
+
*/
|
|
124
|
+
exports.arNSActions = [
|
|
125
|
+
'buy-name',
|
|
126
|
+
'extend-lease',
|
|
127
|
+
'upgrade-name',
|
|
128
|
+
'increase-undername-limit',
|
|
129
|
+
'set-record',
|
|
130
|
+
'remove-record',
|
|
131
|
+
'add-controller',
|
|
132
|
+
'remove-controller',
|
|
133
|
+
'transfer',
|
|
134
|
+
];
|
|
116
135
|
// ===== ArNS purchases paid with fiat (Stripe) — no Turbo Credits in between ====
|
|
117
136
|
/**
|
|
118
137
|
* Stripe integration mode for a fiat ArNS purchase quote.
|
|
@@ -14,10 +14,26 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import { BigNumber } from 'bignumber.js';
|
|
17
|
+
import { solanaOwnerSigner } from '../../common/arnsActions.js';
|
|
17
18
|
import { TurboFactory } from '../../node/factory.js';
|
|
18
19
|
import { FiatPaymentsDisabledError, InsufficientCreditsError, } from '../../utils/errors.js';
|
|
19
20
|
import { wincPerCredit } from '../constants.js';
|
|
20
21
|
import { configFromOptions, turboFromOptions } from '../utils.js';
|
|
22
|
+
/**
|
|
23
|
+
* The ANT owner's Solana key.
|
|
24
|
+
*
|
|
25
|
+
* Deliberately separate from the wallet that pays: the payer holds Turbo
|
|
26
|
+
* credits (and may be Arweave or Ethereum), while the owner holds the ANT and
|
|
27
|
+
* must be Solana. The owner needs a key to SIGN with, not a funded account —
|
|
28
|
+
* Turbo is the fee payer on every sponsored action.
|
|
29
|
+
*/
|
|
30
|
+
function ownerFromOptions(options) {
|
|
31
|
+
if (options.ownerKey === undefined || options.ownerKey === '') {
|
|
32
|
+
throw new Error('Must provide --owner-key (a base58 Solana secret key) — it owns the ANT and signs for it. ' +
|
|
33
|
+
'This is separate from the wallet paying in Turbo Credits.');
|
|
34
|
+
}
|
|
35
|
+
return solanaOwnerSigner(options.ownerKey);
|
|
36
|
+
}
|
|
21
37
|
export function requiredNameFromOptions(options) {
|
|
22
38
|
if (options.name === undefined || options.name.length === 0) {
|
|
23
39
|
throw new Error('Must provide an ArNS --name');
|
|
@@ -111,10 +127,12 @@ async function withCreditErrorMapping(fn) {
|
|
|
111
127
|
}
|
|
112
128
|
function logPurchaseResult(action, result) {
|
|
113
129
|
console.log(JSON.stringify({
|
|
114
|
-
message: `${action}
|
|
130
|
+
message: `${action} completed!`,
|
|
115
131
|
nonce: result.nonce,
|
|
116
|
-
|
|
117
|
-
|
|
132
|
+
antId: result.antId,
|
|
133
|
+
// Solana transaction id of the on-chain write.
|
|
134
|
+
messageId: result.messageId,
|
|
135
|
+
...(result.alreadyCompleted === true ? { alreadyCompleted: true } : {}),
|
|
118
136
|
}, null, 2));
|
|
119
137
|
console.log(`\nTrack this purchase with:\n turbo arns-purchase-status --nonce ${result.nonce}`);
|
|
120
138
|
}
|
|
@@ -132,6 +150,14 @@ export async function arnsPrice(options, turbo) {
|
|
|
132
150
|
}
|
|
133
151
|
export async function arnsFiatQuote(options, turbo) {
|
|
134
152
|
const params = arnsPriceParamsFromOptions(options);
|
|
153
|
+
// `arnsPriceParamsFromOptions` substitutes PRICING_PLACEHOLDER_PROCESS_ID for
|
|
154
|
+
// an omitted Buy-Name `processId`, which is fine for a price lookup but NOT
|
|
155
|
+
// here: a quote records a real purchase, and that placeholder is not a valid
|
|
156
|
+
// ANT. Send `processId` only when the caller actually supplied one — omitting
|
|
157
|
+
// it is what tells Turbo to custodially provision the ANT.
|
|
158
|
+
if (options.processId === undefined) {
|
|
159
|
+
delete params.processId;
|
|
160
|
+
}
|
|
135
161
|
const address = options.address;
|
|
136
162
|
if (address === undefined) {
|
|
137
163
|
throw new Error('A destination --address is required for a fiat ArNS quote.');
|
|
@@ -173,20 +199,19 @@ export async function buyArNSName(options, turbo) {
|
|
|
173
199
|
const name = requiredNameFromOptions(options);
|
|
174
200
|
const type = typeFromOptions(options.type);
|
|
175
201
|
const paidBy = paidByFromArNSOptions(options.paidBy);
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
const processId = options.processId;
|
|
202
|
+
// Every buy mints a fresh ANT straight to `--owner-key`; Turbo never holds
|
|
203
|
+
// it, and there is no bring-your-own-ANT path any more.
|
|
204
|
+
const owner = ownerFromOptions(options);
|
|
180
205
|
const client = turbo ?? (await turboFromOptions(options));
|
|
181
206
|
const result = await withCreditErrorMapping(() => type === 'lease'
|
|
182
207
|
? client.buyArNSName({
|
|
183
208
|
name,
|
|
209
|
+
owner,
|
|
184
210
|
type: 'lease',
|
|
185
211
|
years: positiveIntFromOption(options.years, '--years'),
|
|
186
|
-
processId,
|
|
187
212
|
paidBy,
|
|
188
213
|
})
|
|
189
|
-
: client.buyArNSName({ name, type: 'permabuy',
|
|
214
|
+
: client.buyArNSName({ name, owner, type: 'permabuy', paidBy }));
|
|
190
215
|
logPurchaseResult('ArNS name purchase', result);
|
|
191
216
|
}
|
|
192
217
|
export async function extendArNSLease(options, turbo) {
|
|
@@ -235,6 +260,7 @@ export async function transferArNSAnt(options, turbo) {
|
|
|
235
260
|
const client = turbo ?? (await turboFromOptions(options));
|
|
236
261
|
const result = await client.transferArNSAnt({
|
|
237
262
|
antId: options.antId,
|
|
263
|
+
owner: ownerFromOptions(options),
|
|
238
264
|
target: options.target,
|
|
239
265
|
});
|
|
240
266
|
console.log(JSON.stringify({ message: 'ANT transfer submitted!', ...result }, null, 2));
|
|
@@ -250,6 +276,7 @@ export async function setArNSRecord(options, turbo) {
|
|
|
250
276
|
const client = turbo ?? (await turboFromOptions(options));
|
|
251
277
|
const result = await client.setArNSRecord({
|
|
252
278
|
antId: options.antId,
|
|
279
|
+
owner: ownerFromOptions(options),
|
|
253
280
|
undername: options.undername ?? '@',
|
|
254
281
|
transactionId: options.transactionId,
|
|
255
282
|
ttlSeconds,
|
|
@@ -266,6 +293,7 @@ export async function removeArNSRecord(options, turbo) {
|
|
|
266
293
|
const client = turbo ?? (await turboFromOptions(options));
|
|
267
294
|
const result = await client.removeArNSRecord({
|
|
268
295
|
antId: options.antId,
|
|
296
|
+
owner: ownerFromOptions(options),
|
|
269
297
|
undername: options.undername,
|
|
270
298
|
});
|
|
271
299
|
console.log(JSON.stringify({ message: 'ArNS record removed!', ...result }, null, 2));
|
package/lib/esm/cli/options.js
CHANGED
|
@@ -203,9 +203,9 @@ export const optionMap = {
|
|
|
203
203
|
alias: '--increase-qty <qty>',
|
|
204
204
|
description: 'Number of additional undernames (Increase-Undername-Limit)',
|
|
205
205
|
},
|
|
206
|
-
|
|
207
|
-
alias: '--
|
|
208
|
-
description: '
|
|
206
|
+
arnsOwnerKey: {
|
|
207
|
+
alias: '--owner-key <base58SolanaSecretKey>',
|
|
208
|
+
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',
|
|
209
209
|
},
|
|
210
210
|
arnsNonce: {
|
|
211
211
|
alias: '--nonce <nonce>',
|
|
@@ -299,14 +299,12 @@ export const arnsPriceOptions = [
|
|
|
299
299
|
optionMap.arnsType,
|
|
300
300
|
optionMap.arnsYears,
|
|
301
301
|
optionMap.arnsIncreaseQty,
|
|
302
|
-
optionMap.arnsProcessId,
|
|
303
302
|
];
|
|
304
303
|
export const arnsFiatQuoteOptions = [
|
|
305
304
|
optionMap.arnsName,
|
|
306
305
|
optionMap.arnsType,
|
|
307
306
|
optionMap.arnsYears,
|
|
308
307
|
optionMap.arnsIncreaseQty,
|
|
309
|
-
optionMap.arnsProcessId,
|
|
310
308
|
optionMap.address,
|
|
311
309
|
optionMap.currency,
|
|
312
310
|
{
|
|
@@ -323,7 +321,7 @@ export const buyArNSNameOptions = [
|
|
|
323
321
|
optionMap.arnsName,
|
|
324
322
|
optionMap.arnsType,
|
|
325
323
|
optionMap.arnsYears,
|
|
326
|
-
optionMap.
|
|
324
|
+
optionMap.arnsOwnerKey,
|
|
327
325
|
optionMap.paidBy,
|
|
328
326
|
];
|
|
329
327
|
export const extendArNSLeaseOptions = [
|
|
@@ -346,11 +344,13 @@ export const upgradeArNSNameOptions = [
|
|
|
346
344
|
export const arnsPurchaseStatusOptions = [optionMap.arnsNonce];
|
|
347
345
|
export const transferArNSAntOptions = [
|
|
348
346
|
...walletOptions,
|
|
347
|
+
optionMap.arnsOwnerKey,
|
|
349
348
|
optionMap.arnsAntId,
|
|
350
349
|
optionMap.arnsTarget,
|
|
351
350
|
];
|
|
352
351
|
export const setArNSRecordOptions = [
|
|
353
352
|
...walletOptions,
|
|
353
|
+
optionMap.arnsOwnerKey,
|
|
354
354
|
optionMap.arnsAntId,
|
|
355
355
|
optionMap.arnsUndername,
|
|
356
356
|
optionMap.arnsTransactionId,
|
|
@@ -358,6 +358,7 @@ export const setArNSRecordOptions = [
|
|
|
358
358
|
];
|
|
359
359
|
export const removeArNSRecordOptions = [
|
|
360
360
|
...walletOptions,
|
|
361
|
+
optionMap.arnsOwnerKey,
|
|
361
362
|
optionMap.arnsAntId,
|
|
362
363
|
optionMap.arnsUndername,
|
|
363
364
|
];
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { Keypair, VersionedTransaction } from '@solana/web3.js';
|
|
17
|
+
import bs58 from 'bs58';
|
|
18
|
+
import nacl from 'tweetnacl';
|
|
19
|
+
import { toB64Url } from '../utils/base64.js';
|
|
20
|
+
/**
|
|
21
|
+
* Canonical ACTION-BOUND message for the owner proof.
|
|
22
|
+
*
|
|
23
|
+
* MUST match the bundler's `buildArNSCustodyMessage` byte for byte
|
|
24
|
+
* (newline-delimited) or every signature is rejected. The bundler rebuilds this
|
|
25
|
+
* from the request and verifies the signature over `message + nonce`, so a
|
|
26
|
+
* signature captured for one operation cannot authorize a different one, and
|
|
27
|
+
* the nonce is consumed on use so the same one cannot be replayed (e.g. to
|
|
28
|
+
* revert a record to an older value).
|
|
29
|
+
*/
|
|
30
|
+
export function buildArNSCustodyMessage(action, fields) {
|
|
31
|
+
return ['arns', action, ...fields].join('\n');
|
|
32
|
+
}
|
|
33
|
+
/** Solana's signature-type discriminator in Turbo's signed-request scheme. */
|
|
34
|
+
const SOLANA_SIGNATURE_TYPE = 4;
|
|
35
|
+
/**
|
|
36
|
+
* The ANT owner's half of the envelope, in its own `x-owner-*` headers.
|
|
37
|
+
*
|
|
38
|
+
* Two signatures travel on a record action — the PAYER's signed request over
|
|
39
|
+
* `"" + nonce`, and the OWNER's action-bound proof over `message + nonce` —
|
|
40
|
+
* from two different keys, usually on two different chains. They cannot share
|
|
41
|
+
* one header set: whichever verifier ran second would reject a signature that
|
|
42
|
+
* was never meant for it.
|
|
43
|
+
*/
|
|
44
|
+
export async function arNSOwnerProofHeaders(owner, message, nonce) {
|
|
45
|
+
const address = await owner.getAddress();
|
|
46
|
+
const signature = await owner.signMessage(Uint8Array.from(Buffer.from(message + nonce)));
|
|
47
|
+
return {
|
|
48
|
+
// A Solana address IS the base58-encoded 32-byte ed25519 public key, so
|
|
49
|
+
// decoding the address recovers exactly the bytes the bundler verifies
|
|
50
|
+
// against. Keeps ArNSOwnerSigner minimal — no separate getPublicKey().
|
|
51
|
+
'x-owner-public-key': toB64Url(Buffer.from(bs58.decode(address))),
|
|
52
|
+
'x-owner-nonce': nonce,
|
|
53
|
+
'x-owner-signature': toB64Url(Buffer.from(signature)),
|
|
54
|
+
'x-owner-signature-type': String(SOLANA_SIGNATURE_TYPE),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Build an {@link ArNSOwnerSigner} from a raw Solana secret key.
|
|
59
|
+
*
|
|
60
|
+
* For servers and tests. A browser wallet (Phantom, Solflare, or a console's
|
|
61
|
+
* embedded wallet) should implement the interface directly against its own
|
|
62
|
+
* `signTransaction` / `signMessage` rather than exposing a secret key.
|
|
63
|
+
*
|
|
64
|
+
* Note the owner needs a key to SIGN with, not a funded account: Turbo is the
|
|
65
|
+
* fee payer on every sponsored action, so this wallet's SOL balance can stay
|
|
66
|
+
* at zero for the entire life of the name.
|
|
67
|
+
*/
|
|
68
|
+
export function solanaOwnerSigner(secretKey) {
|
|
69
|
+
const keypair = Keypair.fromSecretKey(typeof secretKey === 'string' ? bs58.decode(secretKey) : secretKey);
|
|
70
|
+
return {
|
|
71
|
+
getAddress: () => keypair.publicKey.toBase58(),
|
|
72
|
+
signTransaction: async (transactionBase64) => {
|
|
73
|
+
const tx = VersionedTransaction.deserialize(Buffer.from(transactionBase64, 'base64'));
|
|
74
|
+
// Sign the bytes as returned. Turbo's fee-payer signature already covers
|
|
75
|
+
// this exact message; rebuilding it from parts invalidates that and the
|
|
76
|
+
// submission is rejected.
|
|
77
|
+
tx.sign([keypair]);
|
|
78
|
+
return Buffer.from(tx.serialize()).toString('base64');
|
|
79
|
+
},
|
|
80
|
+
signMessage: async (message) => nacl.sign.detached(message, keypair.secretKey),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Count the signature slots a prepared transaction still has empty.
|
|
85
|
+
*
|
|
86
|
+
* Turbo pre-signs as fee payer and leaves exactly one slot for the owner, so a
|
|
87
|
+
* client can assert that before prompting a wallet — a cheap way to catch a
|
|
88
|
+
* malformed or already-signed transaction before bothering the user.
|
|
89
|
+
*/
|
|
90
|
+
export function emptySignatureSlots(transactionBase64) {
|
|
91
|
+
const tx = VersionedTransaction.deserialize(Buffer.from(transactionBase64, 'base64'));
|
|
92
|
+
return tx.signatures.filter((sig) => sig.every((byte) => byte === 0)).length;
|
|
93
|
+
}
|