@thru/passkey 0.2.16 → 0.2.18
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/dist/server.cjs +44 -26
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +32 -8
- package/dist/server.d.ts +32 -8
- package/dist/server.js +43 -26
- package/dist/server.js.map +1 -1
- package/package.json +3 -2
- package/src/server/create-wallet.test.ts +18 -4
- package/src/server/create-wallet.ts +5 -5
- package/src/server/handlers.ts +1 -1
- package/src/server/index.ts +3 -1
- package/src/server/submit.test.ts +103 -0
- package/src/server/submit.ts +53 -24
- package/src/server/types.ts +13 -2
- package/src/server/utils.test.ts +16 -0
- package/src/server/utils.ts +9 -2
package/dist/server.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/server/index.ts
|
|
21
21
|
var server_exports = {};
|
|
22
22
|
__export(server_exports, {
|
|
23
|
+
buildPasskeyTransaction: () => buildPasskeyTransaction,
|
|
23
24
|
createPasskeyChallenge: () => createPasskeyChallenge,
|
|
24
25
|
createPasskeyHandlers: () => createPasskeyHandlers,
|
|
25
26
|
createPasskeyWallet: () => createPasskeyWallet,
|
|
@@ -104,8 +105,8 @@ async function getStateProof(client, address, proofType = 1, targetSlot) {
|
|
|
104
105
|
return proof.proof;
|
|
105
106
|
}
|
|
106
107
|
async function trackTransaction(client, signature, timeoutMs = 5e3) {
|
|
108
|
+
let finalizedSeen = false;
|
|
107
109
|
try {
|
|
108
|
-
let finalizedSeen = false;
|
|
109
110
|
for await (const update of client.transactions.track(signature, { timeoutMs })) {
|
|
110
111
|
if (update.executionResult) {
|
|
111
112
|
const vmError = update.executionResult.vmError !== void 0 && update.executionResult.vmError !== null ? BigInt(update.executionResult.vmError) : 0n;
|
|
@@ -129,6 +130,12 @@ async function trackTransaction(client, signature, timeoutMs = 5e3) {
|
|
|
129
130
|
};
|
|
130
131
|
}
|
|
131
132
|
} catch {
|
|
133
|
+
if (finalizedSeen) {
|
|
134
|
+
return {
|
|
135
|
+
signature,
|
|
136
|
+
status: "finalized_without_execution"
|
|
137
|
+
};
|
|
138
|
+
}
|
|
132
139
|
return {
|
|
133
140
|
signature,
|
|
134
141
|
status: "timeout"
|
|
@@ -201,8 +208,8 @@ async function createPasskeyWallet(opts) {
|
|
|
201
208
|
program: import_passkey_manager.PASSKEY_MANAGER_PROGRAM_ADDRESS,
|
|
202
209
|
instructionData: createIx,
|
|
203
210
|
accounts: {
|
|
204
|
-
readWrite:
|
|
205
|
-
readOnly:
|
|
211
|
+
readWrite: accountCtx.readWriteAddresses,
|
|
212
|
+
readOnly: accountCtx.readOnlyAddresses
|
|
206
213
|
},
|
|
207
214
|
header: { fee: 0n }
|
|
208
215
|
});
|
|
@@ -255,8 +262,8 @@ async function createPasskeyWallet(opts) {
|
|
|
255
262
|
program: import_passkey_manager.PASSKEY_MANAGER_PROGRAM_ADDRESS,
|
|
256
263
|
instructionData: registerIx,
|
|
257
264
|
accounts: {
|
|
258
|
-
readWrite:
|
|
259
|
-
readOnly:
|
|
265
|
+
readWrite: accountCtx.readWriteAddresses,
|
|
266
|
+
readOnly: accountCtx.readOnlyAddresses
|
|
260
267
|
},
|
|
261
268
|
header: { fee: 0n }
|
|
262
269
|
});
|
|
@@ -296,29 +303,39 @@ async function createPasskeyChallenge(opts) {
|
|
|
296
303
|
|
|
297
304
|
// src/server/submit.ts
|
|
298
305
|
var import_passkey_manager3 = require("@thru/passkey-manager");
|
|
306
|
+
async function buildPasskeyTransaction(opts) {
|
|
307
|
+
const validateIx = (0, import_passkey_manager3.encodeValidateInstruction)({
|
|
308
|
+
walletAccountIdx: opts.accountCtx.walletAccountIdx,
|
|
309
|
+
authIdx: 0,
|
|
310
|
+
signatureR: (0, import_passkey_manager3.hexToBytes)(opts.signatureR),
|
|
311
|
+
signatureS: (0, import_passkey_manager3.hexToBytes)(opts.signatureS),
|
|
312
|
+
authenticatorData: Buffer.from(opts.authenticatorData, "base64"),
|
|
313
|
+
clientDataJSON: Buffer.from(opts.clientDataJSON, "base64")
|
|
314
|
+
});
|
|
315
|
+
const instructionData = (0, import_passkey_manager3.concatenateInstructions)([validateIx, opts.invokeIx]);
|
|
316
|
+
const transaction = await opts.client.transactions.build({
|
|
317
|
+
feePayer: { publicKey: opts.adminPublicKey },
|
|
318
|
+
program: import_passkey_manager3.PASSKEY_MANAGER_PROGRAM_ADDRESS,
|
|
319
|
+
instructionData,
|
|
320
|
+
accounts: {
|
|
321
|
+
readWrite: opts.accountCtx.readWriteAddresses,
|
|
322
|
+
readOnly: opts.accountCtx.readOnlyAddresses
|
|
323
|
+
},
|
|
324
|
+
header: {
|
|
325
|
+
fee: 0n,
|
|
326
|
+
...opts.header
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
await transaction.sign(opts.adminPrivateKey);
|
|
330
|
+
return {
|
|
331
|
+
transaction,
|
|
332
|
+
rawTransaction: transaction.toWire()
|
|
333
|
+
};
|
|
334
|
+
}
|
|
299
335
|
async function submitPasskeyTransaction(opts) {
|
|
300
336
|
return withSerializedFeePayer(opts.adminPublicKey, async () => {
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
authIdx: 0,
|
|
304
|
-
signatureR: (0, import_passkey_manager3.hexToBytes)(opts.signatureR),
|
|
305
|
-
signatureS: (0, import_passkey_manager3.hexToBytes)(opts.signatureS),
|
|
306
|
-
authenticatorData: Buffer.from(opts.authenticatorData, "base64"),
|
|
307
|
-
clientDataJSON: Buffer.from(opts.clientDataJSON, "base64")
|
|
308
|
-
});
|
|
309
|
-
const instructionData = (0, import_passkey_manager3.concatenateInstructions)([validateIx, opts.invokeIx]);
|
|
310
|
-
const transaction = await opts.client.transactions.build({
|
|
311
|
-
feePayer: { publicKey: opts.adminPublicKey },
|
|
312
|
-
program: import_passkey_manager3.PASSKEY_MANAGER_PROGRAM_ADDRESS,
|
|
313
|
-
instructionData,
|
|
314
|
-
accounts: {
|
|
315
|
-
readWrite: opts.accountCtx.readWriteAddresses,
|
|
316
|
-
readOnly: opts.accountCtx.readOnlyAddresses
|
|
317
|
-
},
|
|
318
|
-
header: { fee: 0n }
|
|
319
|
-
});
|
|
320
|
-
await transaction.sign(opts.adminPrivateKey);
|
|
321
|
-
const signature = await opts.client.transactions.send(transaction.toWire());
|
|
337
|
+
const { rawTransaction } = await buildPasskeyTransaction(opts);
|
|
338
|
+
const signature = await opts.client.transactions.send(rawTransaction);
|
|
322
339
|
return trackTransaction(opts.client, signature);
|
|
323
340
|
});
|
|
324
341
|
}
|
|
@@ -384,6 +401,7 @@ function createPasskeyHandlers(opts) {
|
|
|
384
401
|
}
|
|
385
402
|
// Annotate the CommonJS export names for ESM import in node:
|
|
386
403
|
0 && (module.exports = {
|
|
404
|
+
buildPasskeyTransaction,
|
|
387
405
|
createPasskeyChallenge,
|
|
388
406
|
createPasskeyHandlers,
|
|
389
407
|
createPasskeyWallet,
|
package/dist/server.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/index.ts","../src/server/create-wallet.ts","../../helpers/src/constants.ts","../../helpers/src/address.ts","../src/server/utils.ts","../src/server/challenge.ts","../src/server/submit.ts","../src/server/handlers.ts"],"sourcesContent":["export type {\n ThruClient,\n PasskeySignaturePayload,\n PasskeyChallengeSubmitPayload,\n TransactionResult,\n PasskeyChallengeResult,\n PasskeyContextResult,\n} from './types';\n\nexport { createPasskeyWallet } from './create-wallet';\nexport { createPasskeyChallenge } from './challenge';\nexport { submitPasskeyTransaction } from './submit';\nexport { createPasskeyHandlers } from './handlers';\n","import {\n PASSKEY_MANAGER_PROGRAM_ADDRESS,\n base64UrlToBytes,\n buildAccountContext,\n createCredentialLookupSeed,\n createWalletSeed,\n deriveCredentialLookupAddress,\n deriveWalletAddress,\n encodeCreateInstruction,\n encodeRegisterCredentialInstruction,\n} from '@thru/passkey-manager';\nimport {\n toThruAddress,\n getStateProof,\n trackTransaction,\n withSerializedFeePayer,\n} from './utils';\nimport type { ThruClient } from './types';\n\nexport async function createPasskeyWallet(opts: {\n client: ThruClient;\n adminPublicKey: Uint8Array;\n adminPrivateKey: string;\n adminAddress: string;\n pubkeyX: Uint8Array;\n pubkeyY: Uint8Array;\n credentialId?: string;\n walletName?: string;\n}): Promise<{ walletAddress: string; credentialLookupAddress?: string }> {\n const walletName = opts.walletName ?? 'default';\n const seed = await createWalletSeed(walletName, opts.pubkeyX, opts.pubkeyY);\n const walletBytes = await deriveWalletAddress(seed, PASSKEY_MANAGER_PROGRAM_ADDRESS);\n const walletAddress = toThruAddress(walletBytes);\n\n await withSerializedFeePayer(opts.adminPublicKey, async () => {\n let walletExists = false;\n try {\n await opts.client.accounts.get(walletAddress);\n walletExists = true;\n } catch {\n walletExists = false;\n }\n\n if (walletExists) return;\n\n const stateProof = await getStateProof(opts.client, walletAddress);\n const accountCtx = buildAccountContext({\n walletAddress,\n readWriteAccounts: [],\n readOnlyAccounts: [],\n feePayerAddress: opts.adminAddress,\n programAddress: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n });\n\n const createIx = encodeCreateInstruction({\n walletAccountIdx: accountCtx.walletAccountIdx,\n authority: {\n tag: 1,\n pubkeyX: opts.pubkeyX,\n pubkeyY: opts.pubkeyY,\n },\n seed,\n stateProof,\n });\n\n const transaction = await opts.client.transactions.build({\n feePayer: { publicKey: opts.adminPublicKey },\n program: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n instructionData: createIx,\n accounts: {\n readWrite: [walletAddress],\n readOnly: [],\n },\n header: { fee: 0n },\n });\n\n await transaction.sign(opts.adminPrivateKey);\n const signature = await opts.client.transactions.send(transaction.toWire());\n const result = await trackTransaction(opts.client, signature, 60000);\n if (result.status !== 'finalized') {\n throw new Error(\n `Wallet creation failed with status: ${result.status}${\n result.errorCode !== undefined ? ` (error code: ${result.errorCode})` : ''\n }`\n );\n }\n });\n\n let credentialLookupAddress: string | undefined;\n if (opts.credentialId) {\n const credentialIdBytes = base64UrlToBytes(opts.credentialId);\n const lookupAddressBytes = await deriveCredentialLookupAddress(\n credentialIdBytes,\n walletName,\n PASSKEY_MANAGER_PROGRAM_ADDRESS\n );\n const lookupAddress = toThruAddress(lookupAddressBytes);\n\n credentialLookupAddress = lookupAddress;\n\n try {\n await withSerializedFeePayer(opts.adminPublicKey, async () => {\n let lookupExists = false;\n try {\n await opts.client.accounts.get(lookupAddress);\n lookupExists = true;\n } catch {\n lookupExists = false;\n }\n\n if (lookupExists) return;\n\n const credSeed = await createCredentialLookupSeed(credentialIdBytes, walletName);\n const stateProof = await getStateProof(opts.client, lookupAddress);\n const accountCtx = buildAccountContext({\n walletAddress,\n readWriteAccounts: [lookupAddressBytes],\n readOnlyAccounts: [],\n feePayerAddress: opts.adminAddress,\n programAddress: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n });\n\n const registerIx = encodeRegisterCredentialInstruction({\n walletAccountIdx: accountCtx.walletAccountIdx,\n lookupAccountIdx: accountCtx.getAccountIndex(lookupAddressBytes),\n seed: credSeed,\n stateProof,\n });\n\n const transaction = await opts.client.transactions.build({\n feePayer: { publicKey: opts.adminPublicKey },\n program: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n instructionData: registerIx,\n accounts: {\n readWrite: [walletAddress, lookupAddress],\n readOnly: [],\n },\n header: { fee: 0n },\n });\n\n await transaction.sign(opts.adminPrivateKey);\n const signature = await opts.client.transactions.send(transaction.toWire());\n const result = await trackTransaction(opts.client, signature, 60000);\n if (result.status !== 'finalized') {\n throw new Error(\n `Credential registration failed with status: ${result.status}${\n result.errorCode !== undefined ? ` (error code: ${result.errorCode})` : ''\n }`\n );\n }\n });\n } catch (error) {\n console.warn('Credential registration failed (non-fatal):', error);\n }\n }\n\n return {\n walletAddress,\n credentialLookupAddress,\n };\n}\n","export const BASE64_URL_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\n\nconst tempMap = new Int16Array(256).fill(-1);\nfor (let i = 0; i < BASE64_URL_ALPHABET.length; i++) {\n tempMap[BASE64_URL_ALPHABET.charCodeAt(i)] = i;\n}\nexport const BASE64_URL_MAP = tempMap\n\n","import { BASE64_URL_ALPHABET, BASE64_URL_MAP } from \"./constants\";\n\nexport function encodeAddress(bytes: Uint8Array): string {\n if (bytes.length !== 32) {\n throw new Error('Expected 32-byte address');\n }\n\n let checksum = 0;\n let accumulator = 0;\n let bitsCollected = 0;\n const output: string[] = ['t', 'a'];\n\n for (let i = 0; i < 30; i++) {\n const byte = bytes[i];\n checksum += byte;\n accumulator = ((accumulator << 8) | byte) >>> 0;\n bitsCollected += 8;\n while (bitsCollected >= 6) {\n const index = (accumulator >> (bitsCollected - 6)) & 0x3f;\n output.push(BASE64_URL_ALPHABET[index]);\n bitsCollected -= 6;\n accumulator &= maskForBits(bitsCollected);\n }\n }\n\n const secondLast = bytes[30];\n checksum += secondLast;\n accumulator = ((accumulator << 8) | secondLast) >>> 0;\n bitsCollected += 8;\n\n const last = bytes[31];\n checksum += last;\n accumulator = ((accumulator << 8) | last) >>> 0;\n bitsCollected += 8;\n\n accumulator = ((accumulator << 8) | (checksum & 0xff)) >>> 0;\n bitsCollected += 8;\n\n while (bitsCollected >= 6) {\n const index = (accumulator >> (bitsCollected - 6)) & 0x3f;\n output.push(BASE64_URL_ALPHABET[index]);\n bitsCollected -= 6;\n accumulator &= maskForBits(bitsCollected);\n }\n\n return output.join('');\n}\n\nexport function decodeAddress(value: string): Uint8Array {\n if (value.length !== 46) {\n throw new Error('Invalid address length');\n }\n if (!value.startsWith('ta')) {\n throw new Error('Address must start with \"ta\"');\n }\n\n const output = new Uint8Array(32);\n let checksum = 0;\n let inIdx = 2;\n let remaining = 40;\n let outIdx = 0;\n\n while (remaining >= 4) {\n const a = BASE64_URL_MAP[value.charCodeAt(inIdx)];\n const b = BASE64_URL_MAP[value.charCodeAt(inIdx + 1)];\n const c = BASE64_URL_MAP[value.charCodeAt(inIdx + 2)];\n const d = BASE64_URL_MAP[value.charCodeAt(inIdx + 3)];\n if (a < 0 || b < 0 || c < 0 || d < 0) {\n throw new Error('Invalid address encoding');\n }\n const triple = (a << 18) | (b << 12) | (c << 6) | d;\n const byte1 = (triple >> 16) & 0xff;\n const byte2 = (triple >> 8) & 0xff;\n const byte3 = triple & 0xff;\n checksum += byte1;\n checksum += byte2;\n checksum += byte3;\n output[outIdx++] = byte1;\n output[outIdx++] = byte2;\n output[outIdx++] = byte3;\n inIdx += 4;\n remaining -= 4;\n }\n\n const a = BASE64_URL_MAP[value.charCodeAt(inIdx)];\n const b = BASE64_URL_MAP[value.charCodeAt(inIdx + 1)];\n const c = BASE64_URL_MAP[value.charCodeAt(inIdx + 2)];\n const d = BASE64_URL_MAP[value.charCodeAt(inIdx + 3)];\n if (a < 0 || b < 0 || c < 0 || d < 0) {\n throw new Error('Invalid address encoding');\n }\n const triple = (a << 18) | (b << 12) | (c << 6) | d;\n const byte1 = (triple >> 16) & 0xff;\n const byte2 = (triple >> 8) & 0xff;\n const incomingChecksum = triple & 0xff;\n\n checksum += byte1;\n checksum += byte2;\n output[outIdx++] = byte1;\n output[outIdx++] = byte2;\n\n checksum &= 0xff;\n if (checksum !== incomingChecksum) {\n throw new Error('Address checksum mismatch');\n }\n\n return output;\n}\n\nfunction maskForBits(bits: number): number {\n return bits === 0 ? 0 : (1 << bits) - 1;\n}\n","import { encodeAddress } from '@thru/helpers';\nimport type { ThruClient, TransactionResult } from './types';\n\nconst feePayerQueueSymbol = Symbol.for('thru.sharedFeePayerQueues');\n\nfunction getFeePayerQueues(): Map<string, Promise<void>> {\n const globalQueues = globalThis as typeof globalThis & {\n [feePayerQueueSymbol]?: Map<string, Promise<void>>;\n };\n\n if (!globalQueues[feePayerQueueSymbol]) {\n globalQueues[feePayerQueueSymbol] = new Map<string, Promise<void>>();\n }\n\n return globalQueues[feePayerQueueSymbol];\n}\n\nexport async function getStateProof(\n client: ThruClient,\n address: string,\n proofType: number = 1,\n targetSlot?: bigint\n): Promise<Uint8Array> {\n const proofRequest: {\n address: string;\n proofType: number;\n targetSlot?: bigint;\n } = {\n address,\n proofType,\n };\n\n if (targetSlot !== undefined) {\n proofRequest.targetSlot = targetSlot;\n }\n\n const proof = await client.proofs.generate(proofRequest);\n\n if (!proof.proof || proof.proof.length === 0) {\n throw new Error(`No state proof returned for ${address}`);\n }\n\n return proof.proof;\n}\n\nexport async function trackTransaction(\n client: ThruClient,\n signature: string,\n timeoutMs: number = 5000\n): Promise<TransactionResult> {\n try {\n let finalizedSeen = false;\n\n for await (const update of client.transactions.track(signature, { timeoutMs })) {\n if (update.executionResult) {\n const vmError =\n update.executionResult.vmError !== undefined && update.executionResult.vmError !== null\n ? BigInt(update.executionResult.vmError)\n : 0n;\n const userErrorCode = update.executionResult.userErrorCode;\n const executionError =\n update.executionResult.executionResult !== undefined &&\n update.executionResult.executionResult !== null\n ? BigInt(update.executionResult.executionResult)\n : 0n;\n const success = vmError === 0n && executionError === 0n && userErrorCode === 0n;\n\n return {\n signature,\n status: success ? 'finalized' : 'failed',\n errorCode: vmError !== 0n ? vmError : executionError !== 0n ? executionError : userErrorCode,\n };\n }\n\n if (update.statusCode === 3) {\n finalizedSeen = true;\n }\n }\n\n if (finalizedSeen) {\n return {\n signature,\n status: 'finalized_without_execution',\n };\n }\n } catch {\n return {\n signature,\n status: 'timeout',\n };\n }\n\n return {\n signature,\n status: 'timeout',\n };\n}\n\nexport function toThruAddress(bytes: Uint8Array): string {\n return encodeAddress(bytes);\n}\n\nexport async function withSerializedFeePayer<T>(\n feePayerPublicKey: Uint8Array,\n work: () => Promise<T>\n): Promise<T> {\n const queueKey = toThruAddress(feePayerPublicKey);\n const feePayerQueues = getFeePayerQueues();\n const previous = feePayerQueues.get(queueKey) ?? Promise.resolve();\n let release!: () => void;\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const tail = previous.then(() => current);\n feePayerQueues.set(queueKey, tail);\n\n await previous;\n\n try {\n return await work();\n } finally {\n release();\n if (feePayerQueues.get(queueKey) === tail) {\n feePayerQueues.delete(queueKey);\n }\n }\n}\n","import {\n bytesToBase64Url,\n createValidateChallenge,\n fetchWalletNonce,\n} from '@thru/passkey-manager';\nimport type { AccountContext } from '@thru/passkey-manager';\nimport type { PasskeyChallengeResult, ThruClient } from './types';\n\nexport async function createPasskeyChallenge(opts: {\n client: ThruClient;\n walletAddress: string;\n accountCtx: AccountContext;\n invokeIx: Uint8Array;\n}): Promise<PasskeyChallengeResult> {\n const nonce = await fetchWalletNonce(opts.client, opts.walletAddress);\n const challenge = await createValidateChallenge(\n nonce,\n opts.accountCtx.accountAddresses,\n opts.invokeIx\n );\n\n return {\n challenge: bytesToBase64Url(challenge),\n nonce: nonce.toString(),\n };\n}\n","import {\n PASSKEY_MANAGER_PROGRAM_ADDRESS,\n concatenateInstructions,\n encodeValidateInstruction,\n hexToBytes,\n} from '@thru/passkey-manager';\nimport type { AccountContext } from '@thru/passkey-manager';\nimport { trackTransaction, withSerializedFeePayer } from './utils';\nimport type {\n PasskeySignaturePayload,\n ThruClient,\n TransactionResult,\n} from './types';\n\nexport async function submitPasskeyTransaction(opts: {\n client: ThruClient;\n adminPublicKey: Uint8Array;\n adminPrivateKey: string;\n walletAddress: string;\n accountCtx: AccountContext;\n invokeIx: Uint8Array;\n} & PasskeySignaturePayload): Promise<TransactionResult> {\n return withSerializedFeePayer(opts.adminPublicKey, async () => {\n const validateIx = encodeValidateInstruction({\n walletAccountIdx: opts.accountCtx.walletAccountIdx,\n authIdx: 0,\n signatureR: hexToBytes(opts.signatureR),\n signatureS: hexToBytes(opts.signatureS),\n authenticatorData: Buffer.from(opts.authenticatorData, 'base64'),\n clientDataJSON: Buffer.from(opts.clientDataJSON, 'base64'),\n });\n\n const instructionData = concatenateInstructions([validateIx, opts.invokeIx]);\n const transaction = await opts.client.transactions.build({\n feePayer: { publicKey: opts.adminPublicKey },\n program: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n instructionData,\n accounts: {\n readWrite: opts.accountCtx.readWriteAddresses,\n readOnly: opts.accountCtx.readOnlyAddresses,\n },\n header: { fee: 0n },\n });\n\n await transaction.sign(opts.adminPrivateKey);\n const signature = await opts.client.transactions.send(transaction.toWire());\n return trackTransaction(opts.client, signature);\n });\n}\n","import type { PasskeyContextResult } from './types';\nimport { createPasskeyChallenge } from './challenge';\nimport { submitPasskeyTransaction } from './submit';\nimport type {\n PasskeyChallengeSubmitPayload,\n ThruClient,\n TransactionResult,\n} from './types';\n\nexport function createPasskeyHandlers<P>(opts: {\n buildContext: (params: P) => Promise<PasskeyContextResult>;\n adminPublicKey: Uint8Array;\n adminPrivateKey: string;\n client: ThruClient;\n challengeTtlMs?: number;\n}) {\n const pendingContexts = new Map<\n string,\n { context: PasskeyContextResult; createdAt: number }\n >();\n const challengeTtlMs = opts.challengeTtlMs ?? 5 * 60_000;\n\n function createPendingContextKey(\n walletAddress: string,\n nonce: string,\n challenge: string\n ): string {\n return `${walletAddress}:${nonce}:${challenge}`;\n }\n\n function prunePendingContexts(now = Date.now()): void {\n for (const [nonce, entry] of pendingContexts.entries()) {\n if (now - entry.createdAt > challengeTtlMs) {\n pendingContexts.delete(nonce);\n }\n }\n }\n\n return {\n challenge: async (walletAddress: string, params: P) => {\n prunePendingContexts();\n\n const context = await opts.buildContext(params);\n const challenge = await createPasskeyChallenge({\n client: opts.client,\n walletAddress,\n accountCtx: context.accountCtx,\n invokeIx: context.invokeIx,\n });\n\n pendingContexts.set(\n createPendingContextKey(walletAddress, challenge.nonce, challenge.challenge),\n {\n context,\n createdAt: Date.now(),\n }\n );\n\n return challenge;\n },\n submit: async (\n walletAddress: string,\n params: P,\n payload: PasskeyChallengeSubmitPayload\n ): Promise<TransactionResult> => {\n void params;\n prunePendingContexts();\n\n const pendingKey = createPendingContextKey(\n walletAddress,\n payload.nonce,\n payload.challenge\n );\n const pending = pendingContexts.get(pendingKey);\n if (!pending) {\n throw new Error('Missing or expired challenge nonce');\n }\n\n pendingContexts.delete(pendingKey);\n const { nonce: _nonce, challenge: _challenge, ...signaturePayload } = payload;\n\n return submitPasskeyTransaction({\n client: opts.client,\n adminPublicKey: opts.adminPublicKey,\n adminPrivateKey: opts.adminPrivateKey,\n walletAddress,\n accountCtx: pending.context.accountCtx,\n invokeIx: pending.context.invokeIx,\n ...signaturePayload,\n });\n },\n };\n}\n"],"mappings":"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","names":["import_passkey_manager","import_passkey_manager"]}
|
|
1
|
+
{"version":3,"sources":["../src/server/index.ts","../src/server/create-wallet.ts","../../helpers/src/constants.ts","../../helpers/src/address.ts","../src/server/utils.ts","../src/server/challenge.ts","../src/server/submit.ts","../src/server/handlers.ts"],"sourcesContent":["export type {\n ThruClient,\n PasskeySignaturePayload,\n PasskeyChallengeSubmitPayload,\n PasskeyTransactionHeaderOverrides,\n BuiltPasskeyTransaction,\n TransactionResult,\n PasskeyChallengeResult,\n PasskeyContextResult,\n} from './types';\n\nexport { createPasskeyWallet } from './create-wallet';\nexport { createPasskeyChallenge } from './challenge';\nexport { buildPasskeyTransaction, submitPasskeyTransaction } from './submit';\nexport { createPasskeyHandlers } from './handlers';\n","import {\n PASSKEY_MANAGER_PROGRAM_ADDRESS,\n base64UrlToBytes,\n buildAccountContext,\n createCredentialLookupSeed,\n createWalletSeed,\n deriveCredentialLookupAddress,\n deriveWalletAddress,\n encodeCreateInstruction,\n encodeRegisterCredentialInstruction,\n} from '@thru/passkey-manager';\nimport {\n toThruAddress,\n getStateProof,\n trackTransaction,\n withSerializedFeePayer,\n} from './utils';\nimport type { ThruClient } from './types';\n\nexport async function createPasskeyWallet(opts: {\n client: ThruClient;\n adminPublicKey: Uint8Array;\n adminPrivateKey: Uint8Array;\n adminAddress: string;\n pubkeyX: Uint8Array;\n pubkeyY: Uint8Array;\n credentialId?: string;\n walletName?: string;\n}): Promise<{ walletAddress: string; credentialLookupAddress?: string }> {\n const walletName = opts.walletName ?? 'default';\n const seed = await createWalletSeed(walletName, opts.pubkeyX, opts.pubkeyY);\n const walletBytes = await deriveWalletAddress(seed, PASSKEY_MANAGER_PROGRAM_ADDRESS);\n const walletAddress = toThruAddress(walletBytes);\n\n await withSerializedFeePayer(opts.adminPublicKey, async () => {\n let walletExists = false;\n try {\n await opts.client.accounts.get(walletAddress);\n walletExists = true;\n } catch {\n walletExists = false;\n }\n\n if (walletExists) return;\n\n const stateProof = await getStateProof(opts.client, walletAddress);\n const accountCtx = buildAccountContext({\n walletAddress,\n readWriteAccounts: [],\n readOnlyAccounts: [],\n feePayerAddress: opts.adminAddress,\n programAddress: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n });\n\n const createIx = encodeCreateInstruction({\n walletAccountIdx: accountCtx.walletAccountIdx,\n authority: {\n tag: 1,\n pubkeyX: opts.pubkeyX,\n pubkeyY: opts.pubkeyY,\n },\n seed,\n stateProof,\n });\n\n const transaction = await opts.client.transactions.build({\n feePayer: { publicKey: opts.adminPublicKey },\n program: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n instructionData: createIx,\n accounts: {\n readWrite: accountCtx.readWriteAddresses,\n readOnly: accountCtx.readOnlyAddresses,\n },\n header: { fee: 0n },\n });\n\n await transaction.sign(opts.adminPrivateKey);\n const signature = await opts.client.transactions.send(transaction.toWire());\n const result = await trackTransaction(opts.client, signature, 60000);\n if (result.status !== 'finalized') {\n throw new Error(\n `Wallet creation failed with status: ${result.status}${\n result.errorCode !== undefined ? ` (error code: ${result.errorCode})` : ''\n }`\n );\n }\n });\n\n let credentialLookupAddress: string | undefined;\n if (opts.credentialId) {\n const credentialIdBytes = base64UrlToBytes(opts.credentialId);\n const lookupAddressBytes = await deriveCredentialLookupAddress(\n credentialIdBytes,\n walletName,\n PASSKEY_MANAGER_PROGRAM_ADDRESS\n );\n const lookupAddress = toThruAddress(lookupAddressBytes);\n\n credentialLookupAddress = lookupAddress;\n\n try {\n await withSerializedFeePayer(opts.adminPublicKey, async () => {\n let lookupExists = false;\n try {\n await opts.client.accounts.get(lookupAddress);\n lookupExists = true;\n } catch {\n lookupExists = false;\n }\n\n if (lookupExists) return;\n\n const credSeed = await createCredentialLookupSeed(credentialIdBytes, walletName);\n const stateProof = await getStateProof(opts.client, lookupAddress);\n const accountCtx = buildAccountContext({\n walletAddress,\n readWriteAccounts: [lookupAddressBytes],\n readOnlyAccounts: [],\n feePayerAddress: opts.adminAddress,\n programAddress: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n });\n\n const registerIx = encodeRegisterCredentialInstruction({\n walletAccountIdx: accountCtx.walletAccountIdx,\n lookupAccountIdx: accountCtx.getAccountIndex(lookupAddressBytes),\n seed: credSeed,\n stateProof,\n });\n\n const transaction = await opts.client.transactions.build({\n feePayer: { publicKey: opts.adminPublicKey },\n program: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n instructionData: registerIx,\n accounts: {\n readWrite: accountCtx.readWriteAddresses,\n readOnly: accountCtx.readOnlyAddresses,\n },\n header: { fee: 0n },\n });\n\n await transaction.sign(opts.adminPrivateKey);\n const signature = await opts.client.transactions.send(transaction.toWire());\n const result = await trackTransaction(opts.client, signature, 60000);\n if (result.status !== 'finalized') {\n throw new Error(\n `Credential registration failed with status: ${result.status}${\n result.errorCode !== undefined ? ` (error code: ${result.errorCode})` : ''\n }`\n );\n }\n });\n } catch (error) {\n console.warn('Credential registration failed (non-fatal):', error);\n }\n }\n\n return {\n walletAddress,\n credentialLookupAddress,\n };\n}\n","export const BASE64_URL_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\n\nconst tempMap = new Int16Array(256).fill(-1);\nfor (let i = 0; i < BASE64_URL_ALPHABET.length; i++) {\n tempMap[BASE64_URL_ALPHABET.charCodeAt(i)] = i;\n}\nexport const BASE64_URL_MAP = tempMap\n\n","import { BASE64_URL_ALPHABET, BASE64_URL_MAP } from \"./constants\";\n\nexport function encodeAddress(bytes: Uint8Array): string {\n if (bytes.length !== 32) {\n throw new Error('Expected 32-byte address');\n }\n\n let checksum = 0;\n let accumulator = 0;\n let bitsCollected = 0;\n const output: string[] = ['t', 'a'];\n\n for (let i = 0; i < 30; i++) {\n const byte = bytes[i];\n checksum += byte;\n accumulator = ((accumulator << 8) | byte) >>> 0;\n bitsCollected += 8;\n while (bitsCollected >= 6) {\n const index = (accumulator >> (bitsCollected - 6)) & 0x3f;\n output.push(BASE64_URL_ALPHABET[index]);\n bitsCollected -= 6;\n accumulator &= maskForBits(bitsCollected);\n }\n }\n\n const secondLast = bytes[30];\n checksum += secondLast;\n accumulator = ((accumulator << 8) | secondLast) >>> 0;\n bitsCollected += 8;\n\n const last = bytes[31];\n checksum += last;\n accumulator = ((accumulator << 8) | last) >>> 0;\n bitsCollected += 8;\n\n accumulator = ((accumulator << 8) | (checksum & 0xff)) >>> 0;\n bitsCollected += 8;\n\n while (bitsCollected >= 6) {\n const index = (accumulator >> (bitsCollected - 6)) & 0x3f;\n output.push(BASE64_URL_ALPHABET[index]);\n bitsCollected -= 6;\n accumulator &= maskForBits(bitsCollected);\n }\n\n return output.join('');\n}\n\nexport function decodeAddress(value: string): Uint8Array {\n if (value.length !== 46) {\n throw new Error('Invalid address length');\n }\n if (!value.startsWith('ta')) {\n throw new Error('Address must start with \"ta\"');\n }\n\n const output = new Uint8Array(32);\n let checksum = 0;\n let inIdx = 2;\n let remaining = 40;\n let outIdx = 0;\n\n while (remaining >= 4) {\n const a = BASE64_URL_MAP[value.charCodeAt(inIdx)];\n const b = BASE64_URL_MAP[value.charCodeAt(inIdx + 1)];\n const c = BASE64_URL_MAP[value.charCodeAt(inIdx + 2)];\n const d = BASE64_URL_MAP[value.charCodeAt(inIdx + 3)];\n if (a < 0 || b < 0 || c < 0 || d < 0) {\n throw new Error('Invalid address encoding');\n }\n const triple = (a << 18) | (b << 12) | (c << 6) | d;\n const byte1 = (triple >> 16) & 0xff;\n const byte2 = (triple >> 8) & 0xff;\n const byte3 = triple & 0xff;\n checksum += byte1;\n checksum += byte2;\n checksum += byte3;\n output[outIdx++] = byte1;\n output[outIdx++] = byte2;\n output[outIdx++] = byte3;\n inIdx += 4;\n remaining -= 4;\n }\n\n const a = BASE64_URL_MAP[value.charCodeAt(inIdx)];\n const b = BASE64_URL_MAP[value.charCodeAt(inIdx + 1)];\n const c = BASE64_URL_MAP[value.charCodeAt(inIdx + 2)];\n const d = BASE64_URL_MAP[value.charCodeAt(inIdx + 3)];\n if (a < 0 || b < 0 || c < 0 || d < 0) {\n throw new Error('Invalid address encoding');\n }\n const triple = (a << 18) | (b << 12) | (c << 6) | d;\n const byte1 = (triple >> 16) & 0xff;\n const byte2 = (triple >> 8) & 0xff;\n const incomingChecksum = triple & 0xff;\n\n checksum += byte1;\n checksum += byte2;\n output[outIdx++] = byte1;\n output[outIdx++] = byte2;\n\n checksum &= 0xff;\n if (checksum !== incomingChecksum) {\n throw new Error('Address checksum mismatch');\n }\n\n return output;\n}\n\nfunction maskForBits(bits: number): number {\n return bits === 0 ? 0 : (1 << bits) - 1;\n}\n","import { encodeAddress } from '@thru/helpers';\nimport type { ThruClient, TransactionResult } from './types';\n\nconst feePayerQueueSymbol = Symbol.for('thru.sharedFeePayerQueues');\n\nfunction getFeePayerQueues(): Map<string, Promise<void>> {\n const globalQueues = globalThis as typeof globalThis & {\n [feePayerQueueSymbol]?: Map<string, Promise<void>>;\n };\n\n if (!globalQueues[feePayerQueueSymbol]) {\n globalQueues[feePayerQueueSymbol] = new Map<string, Promise<void>>();\n }\n\n return globalQueues[feePayerQueueSymbol];\n}\n\nexport async function getStateProof(\n client: ThruClient,\n address: string,\n proofType: number = 1,\n targetSlot?: bigint\n): Promise<Uint8Array> {\n const proofRequest: {\n address: string;\n proofType: number;\n targetSlot?: bigint;\n } = {\n address,\n proofType,\n };\n\n if (targetSlot !== undefined) {\n proofRequest.targetSlot = targetSlot;\n }\n\n const proof = await client.proofs.generate(proofRequest);\n\n if (!proof.proof || proof.proof.length === 0) {\n throw new Error(`No state proof returned for ${address}`);\n }\n\n return proof.proof;\n}\n\nexport async function trackTransaction(\n client: ThruClient,\n signature: string,\n timeoutMs: number = 5000\n): Promise<TransactionResult> {\n let finalizedSeen = false;\n\n try {\n for await (const update of client.transactions.track(signature, { timeoutMs })) {\n if (update.executionResult) {\n const vmError =\n update.executionResult.vmError !== undefined && update.executionResult.vmError !== null\n ? BigInt(update.executionResult.vmError)\n : 0n;\n const userErrorCode = update.executionResult.userErrorCode;\n const executionError =\n update.executionResult.executionResult !== undefined &&\n update.executionResult.executionResult !== null\n ? BigInt(update.executionResult.executionResult)\n : 0n;\n const success = vmError === 0n && executionError === 0n && userErrorCode === 0n;\n\n return {\n signature,\n status: success ? 'finalized' : 'failed',\n errorCode: vmError !== 0n ? vmError : executionError !== 0n ? executionError : userErrorCode,\n };\n }\n\n if (update.statusCode === 3) {\n finalizedSeen = true;\n }\n }\n\n if (finalizedSeen) {\n return {\n signature,\n status: 'finalized_without_execution',\n };\n }\n } catch {\n if (finalizedSeen) {\n return {\n signature,\n status: 'finalized_without_execution',\n };\n }\n\n return {\n signature,\n status: 'timeout',\n };\n }\n\n return {\n signature,\n status: 'timeout',\n };\n}\n\nexport function toThruAddress(bytes: Uint8Array): string {\n return encodeAddress(bytes);\n}\n\nexport async function withSerializedFeePayer<T>(\n feePayerPublicKey: Uint8Array,\n work: () => Promise<T>\n): Promise<T> {\n const queueKey = toThruAddress(feePayerPublicKey);\n const feePayerQueues = getFeePayerQueues();\n const previous = feePayerQueues.get(queueKey) ?? Promise.resolve();\n let release!: () => void;\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const tail = previous.then(() => current);\n feePayerQueues.set(queueKey, tail);\n\n await previous;\n\n try {\n return await work();\n } finally {\n release();\n if (feePayerQueues.get(queueKey) === tail) {\n feePayerQueues.delete(queueKey);\n }\n }\n}\n","import {\n bytesToBase64Url,\n createValidateChallenge,\n fetchWalletNonce,\n} from '@thru/passkey-manager';\nimport type { AccountContext } from '@thru/passkey-manager';\nimport type { PasskeyChallengeResult, ThruClient } from './types';\n\nexport async function createPasskeyChallenge(opts: {\n client: ThruClient;\n walletAddress: string;\n accountCtx: AccountContext;\n invokeIx: Uint8Array;\n}): Promise<PasskeyChallengeResult> {\n const nonce = await fetchWalletNonce(opts.client, opts.walletAddress);\n const challenge = await createValidateChallenge(\n nonce,\n opts.accountCtx.accountAddresses,\n opts.invokeIx\n );\n\n return {\n challenge: bytesToBase64Url(challenge),\n nonce: nonce.toString(),\n };\n}\n","import {\n PASSKEY_MANAGER_PROGRAM_ADDRESS,\n concatenateInstructions,\n encodeValidateInstruction,\n hexToBytes,\n} from '@thru/passkey-manager';\nimport type { AccountContext } from '@thru/passkey-manager';\nimport { trackTransaction, withSerializedFeePayer } from './utils';\nimport type {\n BuiltPasskeyTransaction,\n PasskeySignaturePayload,\n PasskeyTransactionHeaderOverrides,\n ThruClient,\n TransactionResult,\n} from './types';\n\n/**\n * Builds and signs a passkey-manager transaction without submitting it.\n *\n * Callers that override the transaction nonce are responsible for coordinating\n * fee-payer nonce allocation before calling this helper. Use\n * `submitPasskeyTransaction` for the serialized one-off submit path.\n */\nexport async function buildPasskeyTransaction(opts: {\n client: ThruClient;\n adminPublicKey: Uint8Array;\n adminPrivateKey: Uint8Array;\n walletAddress: string;\n accountCtx: AccountContext;\n invokeIx: Uint8Array;\n header?: PasskeyTransactionHeaderOverrides;\n} & PasskeySignaturePayload): Promise<BuiltPasskeyTransaction> {\n const validateIx = encodeValidateInstruction({\n walletAccountIdx: opts.accountCtx.walletAccountIdx,\n authIdx: 0,\n signatureR: hexToBytes(opts.signatureR),\n signatureS: hexToBytes(opts.signatureS),\n authenticatorData: Buffer.from(opts.authenticatorData, 'base64'),\n clientDataJSON: Buffer.from(opts.clientDataJSON, 'base64'),\n });\n\n const instructionData = concatenateInstructions([validateIx, opts.invokeIx]);\n const transaction = await opts.client.transactions.build({\n feePayer: { publicKey: opts.adminPublicKey },\n program: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n instructionData,\n accounts: {\n readWrite: opts.accountCtx.readWriteAddresses,\n readOnly: opts.accountCtx.readOnlyAddresses,\n },\n header: {\n fee: 0n,\n ...opts.header,\n },\n });\n\n await transaction.sign(opts.adminPrivateKey);\n return {\n transaction,\n rawTransaction: transaction.toWire(),\n };\n}\n\nexport async function submitPasskeyTransaction(opts: {\n client: ThruClient;\n adminPublicKey: Uint8Array;\n adminPrivateKey: Uint8Array;\n walletAddress: string;\n accountCtx: AccountContext;\n invokeIx: Uint8Array;\n header?: PasskeyTransactionHeaderOverrides;\n} & PasskeySignaturePayload): Promise<TransactionResult> {\n return withSerializedFeePayer(opts.adminPublicKey, async () => {\n const { rawTransaction } = await buildPasskeyTransaction(opts);\n const signature = await opts.client.transactions.send(rawTransaction);\n return trackTransaction(opts.client, signature);\n });\n}\n","import type { PasskeyContextResult } from './types';\nimport { createPasskeyChallenge } from './challenge';\nimport { submitPasskeyTransaction } from './submit';\nimport type {\n PasskeyChallengeSubmitPayload,\n ThruClient,\n TransactionResult,\n} from './types';\n\nexport function createPasskeyHandlers<P>(opts: {\n buildContext: (params: P) => Promise<PasskeyContextResult>;\n adminPublicKey: Uint8Array;\n adminPrivateKey: Uint8Array;\n client: ThruClient;\n challengeTtlMs?: number;\n}) {\n const pendingContexts = new Map<\n string,\n { context: PasskeyContextResult; createdAt: number }\n >();\n const challengeTtlMs = opts.challengeTtlMs ?? 5 * 60_000;\n\n function createPendingContextKey(\n walletAddress: string,\n nonce: string,\n challenge: string\n ): string {\n return `${walletAddress}:${nonce}:${challenge}`;\n }\n\n function prunePendingContexts(now = Date.now()): void {\n for (const [nonce, entry] of pendingContexts.entries()) {\n if (now - entry.createdAt > challengeTtlMs) {\n pendingContexts.delete(nonce);\n }\n }\n }\n\n return {\n challenge: async (walletAddress: string, params: P) => {\n prunePendingContexts();\n\n const context = await opts.buildContext(params);\n const challenge = await createPasskeyChallenge({\n client: opts.client,\n walletAddress,\n accountCtx: context.accountCtx,\n invokeIx: context.invokeIx,\n });\n\n pendingContexts.set(\n createPendingContextKey(walletAddress, challenge.nonce, challenge.challenge),\n {\n context,\n createdAt: Date.now(),\n }\n );\n\n return challenge;\n },\n submit: async (\n walletAddress: string,\n params: P,\n payload: PasskeyChallengeSubmitPayload\n ): Promise<TransactionResult> => {\n void params;\n prunePendingContexts();\n\n const pendingKey = createPendingContextKey(\n walletAddress,\n payload.nonce,\n payload.challenge\n );\n const pending = pendingContexts.get(pendingKey);\n if (!pending) {\n throw new Error('Missing or expired challenge nonce');\n }\n\n pendingContexts.delete(pendingKey);\n const { nonce: _nonce, challenge: _challenge, ...signaturePayload } = payload;\n\n return submitPasskeyTransaction({\n client: opts.client,\n adminPublicKey: opts.adminPublicKey,\n adminPrivateKey: opts.adminPrivateKey,\n walletAddress,\n accountCtx: pending.context.accountCtx,\n invokeIx: pending.context.invokeIx,\n ...signaturePayload,\n });\n },\n };\n}\n"],"mappings":"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","names":["import_passkey_manager","import_passkey_manager"]}
|
package/dist/server.d.cts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { AccountContext } from '@thru/passkey-manager';
|
|
2
|
+
import { TransactionHeaderConfig } from '@thru/thru-sdk';
|
|
2
3
|
|
|
4
|
+
type PasskeyTransactionHeaderOverrides = TransactionHeaderConfig;
|
|
5
|
+
interface BuiltPasskeyTransaction {
|
|
6
|
+
transaction: {
|
|
7
|
+
sign: (privateKey: Uint8Array) => Promise<unknown>;
|
|
8
|
+
toWire: () => Uint8Array;
|
|
9
|
+
};
|
|
10
|
+
rawTransaction: Uint8Array;
|
|
11
|
+
}
|
|
3
12
|
interface ThruClient {
|
|
4
13
|
accounts: {
|
|
5
14
|
get: (address: string) => Promise<{
|
|
@@ -33,11 +42,9 @@ interface ThruClient {
|
|
|
33
42
|
readWrite: string[];
|
|
34
43
|
readOnly: string[];
|
|
35
44
|
};
|
|
36
|
-
header
|
|
37
|
-
fee: bigint;
|
|
38
|
-
};
|
|
45
|
+
header?: TransactionHeaderConfig;
|
|
39
46
|
}) => Promise<{
|
|
40
|
-
sign: (privateKey:
|
|
47
|
+
sign: (privateKey: Uint8Array) => Promise<unknown>;
|
|
41
48
|
toWire: () => Uint8Array;
|
|
42
49
|
}>;
|
|
43
50
|
send: (transaction: Uint8Array) => Promise<string>;
|
|
@@ -80,7 +87,7 @@ interface PasskeyContextResult {
|
|
|
80
87
|
declare function createPasskeyWallet(opts: {
|
|
81
88
|
client: ThruClient;
|
|
82
89
|
adminPublicKey: Uint8Array;
|
|
83
|
-
adminPrivateKey:
|
|
90
|
+
adminPrivateKey: Uint8Array;
|
|
84
91
|
adminAddress: string;
|
|
85
92
|
pubkeyX: Uint8Array;
|
|
86
93
|
pubkeyY: Uint8Array;
|
|
@@ -98,19 +105,36 @@ declare function createPasskeyChallenge(opts: {
|
|
|
98
105
|
invokeIx: Uint8Array;
|
|
99
106
|
}): Promise<PasskeyChallengeResult>;
|
|
100
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Builds and signs a passkey-manager transaction without submitting it.
|
|
110
|
+
*
|
|
111
|
+
* Callers that override the transaction nonce are responsible for coordinating
|
|
112
|
+
* fee-payer nonce allocation before calling this helper. Use
|
|
113
|
+
* `submitPasskeyTransaction` for the serialized one-off submit path.
|
|
114
|
+
*/
|
|
115
|
+
declare function buildPasskeyTransaction(opts: {
|
|
116
|
+
client: ThruClient;
|
|
117
|
+
adminPublicKey: Uint8Array;
|
|
118
|
+
adminPrivateKey: Uint8Array;
|
|
119
|
+
walletAddress: string;
|
|
120
|
+
accountCtx: AccountContext;
|
|
121
|
+
invokeIx: Uint8Array;
|
|
122
|
+
header?: PasskeyTransactionHeaderOverrides;
|
|
123
|
+
} & PasskeySignaturePayload): Promise<BuiltPasskeyTransaction>;
|
|
101
124
|
declare function submitPasskeyTransaction(opts: {
|
|
102
125
|
client: ThruClient;
|
|
103
126
|
adminPublicKey: Uint8Array;
|
|
104
|
-
adminPrivateKey:
|
|
127
|
+
adminPrivateKey: Uint8Array;
|
|
105
128
|
walletAddress: string;
|
|
106
129
|
accountCtx: AccountContext;
|
|
107
130
|
invokeIx: Uint8Array;
|
|
131
|
+
header?: PasskeyTransactionHeaderOverrides;
|
|
108
132
|
} & PasskeySignaturePayload): Promise<TransactionResult>;
|
|
109
133
|
|
|
110
134
|
declare function createPasskeyHandlers<P>(opts: {
|
|
111
135
|
buildContext: (params: P) => Promise<PasskeyContextResult>;
|
|
112
136
|
adminPublicKey: Uint8Array;
|
|
113
|
-
adminPrivateKey:
|
|
137
|
+
adminPrivateKey: Uint8Array;
|
|
114
138
|
client: ThruClient;
|
|
115
139
|
challengeTtlMs?: number;
|
|
116
140
|
}): {
|
|
@@ -118,4 +142,4 @@ declare function createPasskeyHandlers<P>(opts: {
|
|
|
118
142
|
submit: (walletAddress: string, params: P, payload: PasskeyChallengeSubmitPayload) => Promise<TransactionResult>;
|
|
119
143
|
};
|
|
120
144
|
|
|
121
|
-
export { type PasskeyChallengeResult, type PasskeyChallengeSubmitPayload, type PasskeyContextResult, type PasskeySignaturePayload, type ThruClient, type TransactionResult, createPasskeyChallenge, createPasskeyHandlers, createPasskeyWallet, submitPasskeyTransaction };
|
|
145
|
+
export { type BuiltPasskeyTransaction, type PasskeyChallengeResult, type PasskeyChallengeSubmitPayload, type PasskeyContextResult, type PasskeySignaturePayload, type PasskeyTransactionHeaderOverrides, type ThruClient, type TransactionResult, buildPasskeyTransaction, createPasskeyChallenge, createPasskeyHandlers, createPasskeyWallet, submitPasskeyTransaction };
|
package/dist/server.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { AccountContext } from '@thru/passkey-manager';
|
|
2
|
+
import { TransactionHeaderConfig } from '@thru/thru-sdk';
|
|
2
3
|
|
|
4
|
+
type PasskeyTransactionHeaderOverrides = TransactionHeaderConfig;
|
|
5
|
+
interface BuiltPasskeyTransaction {
|
|
6
|
+
transaction: {
|
|
7
|
+
sign: (privateKey: Uint8Array) => Promise<unknown>;
|
|
8
|
+
toWire: () => Uint8Array;
|
|
9
|
+
};
|
|
10
|
+
rawTransaction: Uint8Array;
|
|
11
|
+
}
|
|
3
12
|
interface ThruClient {
|
|
4
13
|
accounts: {
|
|
5
14
|
get: (address: string) => Promise<{
|
|
@@ -33,11 +42,9 @@ interface ThruClient {
|
|
|
33
42
|
readWrite: string[];
|
|
34
43
|
readOnly: string[];
|
|
35
44
|
};
|
|
36
|
-
header
|
|
37
|
-
fee: bigint;
|
|
38
|
-
};
|
|
45
|
+
header?: TransactionHeaderConfig;
|
|
39
46
|
}) => Promise<{
|
|
40
|
-
sign: (privateKey:
|
|
47
|
+
sign: (privateKey: Uint8Array) => Promise<unknown>;
|
|
41
48
|
toWire: () => Uint8Array;
|
|
42
49
|
}>;
|
|
43
50
|
send: (transaction: Uint8Array) => Promise<string>;
|
|
@@ -80,7 +87,7 @@ interface PasskeyContextResult {
|
|
|
80
87
|
declare function createPasskeyWallet(opts: {
|
|
81
88
|
client: ThruClient;
|
|
82
89
|
adminPublicKey: Uint8Array;
|
|
83
|
-
adminPrivateKey:
|
|
90
|
+
adminPrivateKey: Uint8Array;
|
|
84
91
|
adminAddress: string;
|
|
85
92
|
pubkeyX: Uint8Array;
|
|
86
93
|
pubkeyY: Uint8Array;
|
|
@@ -98,19 +105,36 @@ declare function createPasskeyChallenge(opts: {
|
|
|
98
105
|
invokeIx: Uint8Array;
|
|
99
106
|
}): Promise<PasskeyChallengeResult>;
|
|
100
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Builds and signs a passkey-manager transaction without submitting it.
|
|
110
|
+
*
|
|
111
|
+
* Callers that override the transaction nonce are responsible for coordinating
|
|
112
|
+
* fee-payer nonce allocation before calling this helper. Use
|
|
113
|
+
* `submitPasskeyTransaction` for the serialized one-off submit path.
|
|
114
|
+
*/
|
|
115
|
+
declare function buildPasskeyTransaction(opts: {
|
|
116
|
+
client: ThruClient;
|
|
117
|
+
adminPublicKey: Uint8Array;
|
|
118
|
+
adminPrivateKey: Uint8Array;
|
|
119
|
+
walletAddress: string;
|
|
120
|
+
accountCtx: AccountContext;
|
|
121
|
+
invokeIx: Uint8Array;
|
|
122
|
+
header?: PasskeyTransactionHeaderOverrides;
|
|
123
|
+
} & PasskeySignaturePayload): Promise<BuiltPasskeyTransaction>;
|
|
101
124
|
declare function submitPasskeyTransaction(opts: {
|
|
102
125
|
client: ThruClient;
|
|
103
126
|
adminPublicKey: Uint8Array;
|
|
104
|
-
adminPrivateKey:
|
|
127
|
+
adminPrivateKey: Uint8Array;
|
|
105
128
|
walletAddress: string;
|
|
106
129
|
accountCtx: AccountContext;
|
|
107
130
|
invokeIx: Uint8Array;
|
|
131
|
+
header?: PasskeyTransactionHeaderOverrides;
|
|
108
132
|
} & PasskeySignaturePayload): Promise<TransactionResult>;
|
|
109
133
|
|
|
110
134
|
declare function createPasskeyHandlers<P>(opts: {
|
|
111
135
|
buildContext: (params: P) => Promise<PasskeyContextResult>;
|
|
112
136
|
adminPublicKey: Uint8Array;
|
|
113
|
-
adminPrivateKey:
|
|
137
|
+
adminPrivateKey: Uint8Array;
|
|
114
138
|
client: ThruClient;
|
|
115
139
|
challengeTtlMs?: number;
|
|
116
140
|
}): {
|
|
@@ -118,4 +142,4 @@ declare function createPasskeyHandlers<P>(opts: {
|
|
|
118
142
|
submit: (walletAddress: string, params: P, payload: PasskeyChallengeSubmitPayload) => Promise<TransactionResult>;
|
|
119
143
|
};
|
|
120
144
|
|
|
121
|
-
export { type PasskeyChallengeResult, type PasskeyChallengeSubmitPayload, type PasskeyContextResult, type PasskeySignaturePayload, type ThruClient, type TransactionResult, createPasskeyChallenge, createPasskeyHandlers, createPasskeyWallet, submitPasskeyTransaction };
|
|
145
|
+
export { type BuiltPasskeyTransaction, type PasskeyChallengeResult, type PasskeyChallengeSubmitPayload, type PasskeyContextResult, type PasskeySignaturePayload, type PasskeyTransactionHeaderOverrides, type ThruClient, type TransactionResult, buildPasskeyTransaction, createPasskeyChallenge, createPasskeyHandlers, createPasskeyWallet, submitPasskeyTransaction };
|
package/dist/server.js
CHANGED
|
@@ -85,8 +85,8 @@ async function getStateProof(client, address, proofType = 1, targetSlot) {
|
|
|
85
85
|
return proof.proof;
|
|
86
86
|
}
|
|
87
87
|
async function trackTransaction(client, signature, timeoutMs = 5e3) {
|
|
88
|
+
let finalizedSeen = false;
|
|
88
89
|
try {
|
|
89
|
-
let finalizedSeen = false;
|
|
90
90
|
for await (const update of client.transactions.track(signature, { timeoutMs })) {
|
|
91
91
|
if (update.executionResult) {
|
|
92
92
|
const vmError = update.executionResult.vmError !== void 0 && update.executionResult.vmError !== null ? BigInt(update.executionResult.vmError) : 0n;
|
|
@@ -110,6 +110,12 @@ async function trackTransaction(client, signature, timeoutMs = 5e3) {
|
|
|
110
110
|
};
|
|
111
111
|
}
|
|
112
112
|
} catch {
|
|
113
|
+
if (finalizedSeen) {
|
|
114
|
+
return {
|
|
115
|
+
signature,
|
|
116
|
+
status: "finalized_without_execution"
|
|
117
|
+
};
|
|
118
|
+
}
|
|
113
119
|
return {
|
|
114
120
|
signature,
|
|
115
121
|
status: "timeout"
|
|
@@ -182,8 +188,8 @@ async function createPasskeyWallet(opts) {
|
|
|
182
188
|
program: PASSKEY_MANAGER_PROGRAM_ADDRESS,
|
|
183
189
|
instructionData: createIx,
|
|
184
190
|
accounts: {
|
|
185
|
-
readWrite:
|
|
186
|
-
readOnly:
|
|
191
|
+
readWrite: accountCtx.readWriteAddresses,
|
|
192
|
+
readOnly: accountCtx.readOnlyAddresses
|
|
187
193
|
},
|
|
188
194
|
header: { fee: 0n }
|
|
189
195
|
});
|
|
@@ -236,8 +242,8 @@ async function createPasskeyWallet(opts) {
|
|
|
236
242
|
program: PASSKEY_MANAGER_PROGRAM_ADDRESS,
|
|
237
243
|
instructionData: registerIx,
|
|
238
244
|
accounts: {
|
|
239
|
-
readWrite:
|
|
240
|
-
readOnly:
|
|
245
|
+
readWrite: accountCtx.readWriteAddresses,
|
|
246
|
+
readOnly: accountCtx.readOnlyAddresses
|
|
241
247
|
},
|
|
242
248
|
header: { fee: 0n }
|
|
243
249
|
});
|
|
@@ -286,29 +292,39 @@ import {
|
|
|
286
292
|
encodeValidateInstruction,
|
|
287
293
|
hexToBytes as hexToBytes2
|
|
288
294
|
} from "@thru/passkey-manager";
|
|
295
|
+
async function buildPasskeyTransaction(opts) {
|
|
296
|
+
const validateIx = encodeValidateInstruction({
|
|
297
|
+
walletAccountIdx: opts.accountCtx.walletAccountIdx,
|
|
298
|
+
authIdx: 0,
|
|
299
|
+
signatureR: hexToBytes2(opts.signatureR),
|
|
300
|
+
signatureS: hexToBytes2(opts.signatureS),
|
|
301
|
+
authenticatorData: Buffer.from(opts.authenticatorData, "base64"),
|
|
302
|
+
clientDataJSON: Buffer.from(opts.clientDataJSON, "base64")
|
|
303
|
+
});
|
|
304
|
+
const instructionData = concatenateInstructions([validateIx, opts.invokeIx]);
|
|
305
|
+
const transaction = await opts.client.transactions.build({
|
|
306
|
+
feePayer: { publicKey: opts.adminPublicKey },
|
|
307
|
+
program: PASSKEY_MANAGER_PROGRAM_ADDRESS2,
|
|
308
|
+
instructionData,
|
|
309
|
+
accounts: {
|
|
310
|
+
readWrite: opts.accountCtx.readWriteAddresses,
|
|
311
|
+
readOnly: opts.accountCtx.readOnlyAddresses
|
|
312
|
+
},
|
|
313
|
+
header: {
|
|
314
|
+
fee: 0n,
|
|
315
|
+
...opts.header
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
await transaction.sign(opts.adminPrivateKey);
|
|
319
|
+
return {
|
|
320
|
+
transaction,
|
|
321
|
+
rawTransaction: transaction.toWire()
|
|
322
|
+
};
|
|
323
|
+
}
|
|
289
324
|
async function submitPasskeyTransaction(opts) {
|
|
290
325
|
return withSerializedFeePayer(opts.adminPublicKey, async () => {
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
authIdx: 0,
|
|
294
|
-
signatureR: hexToBytes2(opts.signatureR),
|
|
295
|
-
signatureS: hexToBytes2(opts.signatureS),
|
|
296
|
-
authenticatorData: Buffer.from(opts.authenticatorData, "base64"),
|
|
297
|
-
clientDataJSON: Buffer.from(opts.clientDataJSON, "base64")
|
|
298
|
-
});
|
|
299
|
-
const instructionData = concatenateInstructions([validateIx, opts.invokeIx]);
|
|
300
|
-
const transaction = await opts.client.transactions.build({
|
|
301
|
-
feePayer: { publicKey: opts.adminPublicKey },
|
|
302
|
-
program: PASSKEY_MANAGER_PROGRAM_ADDRESS2,
|
|
303
|
-
instructionData,
|
|
304
|
-
accounts: {
|
|
305
|
-
readWrite: opts.accountCtx.readWriteAddresses,
|
|
306
|
-
readOnly: opts.accountCtx.readOnlyAddresses
|
|
307
|
-
},
|
|
308
|
-
header: { fee: 0n }
|
|
309
|
-
});
|
|
310
|
-
await transaction.sign(opts.adminPrivateKey);
|
|
311
|
-
const signature = await opts.client.transactions.send(transaction.toWire());
|
|
326
|
+
const { rawTransaction } = await buildPasskeyTransaction(opts);
|
|
327
|
+
const signature = await opts.client.transactions.send(rawTransaction);
|
|
312
328
|
return trackTransaction(opts.client, signature);
|
|
313
329
|
});
|
|
314
330
|
}
|
|
@@ -373,6 +389,7 @@ function createPasskeyHandlers(opts) {
|
|
|
373
389
|
};
|
|
374
390
|
}
|
|
375
391
|
export {
|
|
392
|
+
buildPasskeyTransaction,
|
|
376
393
|
createPasskeyChallenge,
|
|
377
394
|
createPasskeyHandlers,
|
|
378
395
|
createPasskeyWallet,
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/create-wallet.ts","../../helpers/src/constants.ts","../../helpers/src/address.ts","../src/server/utils.ts","../src/server/challenge.ts","../src/server/submit.ts","../src/server/handlers.ts"],"sourcesContent":["import {\n PASSKEY_MANAGER_PROGRAM_ADDRESS,\n base64UrlToBytes,\n buildAccountContext,\n createCredentialLookupSeed,\n createWalletSeed,\n deriveCredentialLookupAddress,\n deriveWalletAddress,\n encodeCreateInstruction,\n encodeRegisterCredentialInstruction,\n} from '@thru/passkey-manager';\nimport {\n toThruAddress,\n getStateProof,\n trackTransaction,\n withSerializedFeePayer,\n} from './utils';\nimport type { ThruClient } from './types';\n\nexport async function createPasskeyWallet(opts: {\n client: ThruClient;\n adminPublicKey: Uint8Array;\n adminPrivateKey: string;\n adminAddress: string;\n pubkeyX: Uint8Array;\n pubkeyY: Uint8Array;\n credentialId?: string;\n walletName?: string;\n}): Promise<{ walletAddress: string; credentialLookupAddress?: string }> {\n const walletName = opts.walletName ?? 'default';\n const seed = await createWalletSeed(walletName, opts.pubkeyX, opts.pubkeyY);\n const walletBytes = await deriveWalletAddress(seed, PASSKEY_MANAGER_PROGRAM_ADDRESS);\n const walletAddress = toThruAddress(walletBytes);\n\n await withSerializedFeePayer(opts.adminPublicKey, async () => {\n let walletExists = false;\n try {\n await opts.client.accounts.get(walletAddress);\n walletExists = true;\n } catch {\n walletExists = false;\n }\n\n if (walletExists) return;\n\n const stateProof = await getStateProof(opts.client, walletAddress);\n const accountCtx = buildAccountContext({\n walletAddress,\n readWriteAccounts: [],\n readOnlyAccounts: [],\n feePayerAddress: opts.adminAddress,\n programAddress: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n });\n\n const createIx = encodeCreateInstruction({\n walletAccountIdx: accountCtx.walletAccountIdx,\n authority: {\n tag: 1,\n pubkeyX: opts.pubkeyX,\n pubkeyY: opts.pubkeyY,\n },\n seed,\n stateProof,\n });\n\n const transaction = await opts.client.transactions.build({\n feePayer: { publicKey: opts.adminPublicKey },\n program: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n instructionData: createIx,\n accounts: {\n readWrite: [walletAddress],\n readOnly: [],\n },\n header: { fee: 0n },\n });\n\n await transaction.sign(opts.adminPrivateKey);\n const signature = await opts.client.transactions.send(transaction.toWire());\n const result = await trackTransaction(opts.client, signature, 60000);\n if (result.status !== 'finalized') {\n throw new Error(\n `Wallet creation failed with status: ${result.status}${\n result.errorCode !== undefined ? ` (error code: ${result.errorCode})` : ''\n }`\n );\n }\n });\n\n let credentialLookupAddress: string | undefined;\n if (opts.credentialId) {\n const credentialIdBytes = base64UrlToBytes(opts.credentialId);\n const lookupAddressBytes = await deriveCredentialLookupAddress(\n credentialIdBytes,\n walletName,\n PASSKEY_MANAGER_PROGRAM_ADDRESS\n );\n const lookupAddress = toThruAddress(lookupAddressBytes);\n\n credentialLookupAddress = lookupAddress;\n\n try {\n await withSerializedFeePayer(opts.adminPublicKey, async () => {\n let lookupExists = false;\n try {\n await opts.client.accounts.get(lookupAddress);\n lookupExists = true;\n } catch {\n lookupExists = false;\n }\n\n if (lookupExists) return;\n\n const credSeed = await createCredentialLookupSeed(credentialIdBytes, walletName);\n const stateProof = await getStateProof(opts.client, lookupAddress);\n const accountCtx = buildAccountContext({\n walletAddress,\n readWriteAccounts: [lookupAddressBytes],\n readOnlyAccounts: [],\n feePayerAddress: opts.adminAddress,\n programAddress: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n });\n\n const registerIx = encodeRegisterCredentialInstruction({\n walletAccountIdx: accountCtx.walletAccountIdx,\n lookupAccountIdx: accountCtx.getAccountIndex(lookupAddressBytes),\n seed: credSeed,\n stateProof,\n });\n\n const transaction = await opts.client.transactions.build({\n feePayer: { publicKey: opts.adminPublicKey },\n program: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n instructionData: registerIx,\n accounts: {\n readWrite: [walletAddress, lookupAddress],\n readOnly: [],\n },\n header: { fee: 0n },\n });\n\n await transaction.sign(opts.adminPrivateKey);\n const signature = await opts.client.transactions.send(transaction.toWire());\n const result = await trackTransaction(opts.client, signature, 60000);\n if (result.status !== 'finalized') {\n throw new Error(\n `Credential registration failed with status: ${result.status}${\n result.errorCode !== undefined ? ` (error code: ${result.errorCode})` : ''\n }`\n );\n }\n });\n } catch (error) {\n console.warn('Credential registration failed (non-fatal):', error);\n }\n }\n\n return {\n walletAddress,\n credentialLookupAddress,\n };\n}\n","export const BASE64_URL_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\n\nconst tempMap = new Int16Array(256).fill(-1);\nfor (let i = 0; i < BASE64_URL_ALPHABET.length; i++) {\n tempMap[BASE64_URL_ALPHABET.charCodeAt(i)] = i;\n}\nexport const BASE64_URL_MAP = tempMap\n\n","import { BASE64_URL_ALPHABET, BASE64_URL_MAP } from \"./constants\";\n\nexport function encodeAddress(bytes: Uint8Array): string {\n if (bytes.length !== 32) {\n throw new Error('Expected 32-byte address');\n }\n\n let checksum = 0;\n let accumulator = 0;\n let bitsCollected = 0;\n const output: string[] = ['t', 'a'];\n\n for (let i = 0; i < 30; i++) {\n const byte = bytes[i];\n checksum += byte;\n accumulator = ((accumulator << 8) | byte) >>> 0;\n bitsCollected += 8;\n while (bitsCollected >= 6) {\n const index = (accumulator >> (bitsCollected - 6)) & 0x3f;\n output.push(BASE64_URL_ALPHABET[index]);\n bitsCollected -= 6;\n accumulator &= maskForBits(bitsCollected);\n }\n }\n\n const secondLast = bytes[30];\n checksum += secondLast;\n accumulator = ((accumulator << 8) | secondLast) >>> 0;\n bitsCollected += 8;\n\n const last = bytes[31];\n checksum += last;\n accumulator = ((accumulator << 8) | last) >>> 0;\n bitsCollected += 8;\n\n accumulator = ((accumulator << 8) | (checksum & 0xff)) >>> 0;\n bitsCollected += 8;\n\n while (bitsCollected >= 6) {\n const index = (accumulator >> (bitsCollected - 6)) & 0x3f;\n output.push(BASE64_URL_ALPHABET[index]);\n bitsCollected -= 6;\n accumulator &= maskForBits(bitsCollected);\n }\n\n return output.join('');\n}\n\nexport function decodeAddress(value: string): Uint8Array {\n if (value.length !== 46) {\n throw new Error('Invalid address length');\n }\n if (!value.startsWith('ta')) {\n throw new Error('Address must start with \"ta\"');\n }\n\n const output = new Uint8Array(32);\n let checksum = 0;\n let inIdx = 2;\n let remaining = 40;\n let outIdx = 0;\n\n while (remaining >= 4) {\n const a = BASE64_URL_MAP[value.charCodeAt(inIdx)];\n const b = BASE64_URL_MAP[value.charCodeAt(inIdx + 1)];\n const c = BASE64_URL_MAP[value.charCodeAt(inIdx + 2)];\n const d = BASE64_URL_MAP[value.charCodeAt(inIdx + 3)];\n if (a < 0 || b < 0 || c < 0 || d < 0) {\n throw new Error('Invalid address encoding');\n }\n const triple = (a << 18) | (b << 12) | (c << 6) | d;\n const byte1 = (triple >> 16) & 0xff;\n const byte2 = (triple >> 8) & 0xff;\n const byte3 = triple & 0xff;\n checksum += byte1;\n checksum += byte2;\n checksum += byte3;\n output[outIdx++] = byte1;\n output[outIdx++] = byte2;\n output[outIdx++] = byte3;\n inIdx += 4;\n remaining -= 4;\n }\n\n const a = BASE64_URL_MAP[value.charCodeAt(inIdx)];\n const b = BASE64_URL_MAP[value.charCodeAt(inIdx + 1)];\n const c = BASE64_URL_MAP[value.charCodeAt(inIdx + 2)];\n const d = BASE64_URL_MAP[value.charCodeAt(inIdx + 3)];\n if (a < 0 || b < 0 || c < 0 || d < 0) {\n throw new Error('Invalid address encoding');\n }\n const triple = (a << 18) | (b << 12) | (c << 6) | d;\n const byte1 = (triple >> 16) & 0xff;\n const byte2 = (triple >> 8) & 0xff;\n const incomingChecksum = triple & 0xff;\n\n checksum += byte1;\n checksum += byte2;\n output[outIdx++] = byte1;\n output[outIdx++] = byte2;\n\n checksum &= 0xff;\n if (checksum !== incomingChecksum) {\n throw new Error('Address checksum mismatch');\n }\n\n return output;\n}\n\nfunction maskForBits(bits: number): number {\n return bits === 0 ? 0 : (1 << bits) - 1;\n}\n","import { encodeAddress } from '@thru/helpers';\nimport type { ThruClient, TransactionResult } from './types';\n\nconst feePayerQueueSymbol = Symbol.for('thru.sharedFeePayerQueues');\n\nfunction getFeePayerQueues(): Map<string, Promise<void>> {\n const globalQueues = globalThis as typeof globalThis & {\n [feePayerQueueSymbol]?: Map<string, Promise<void>>;\n };\n\n if (!globalQueues[feePayerQueueSymbol]) {\n globalQueues[feePayerQueueSymbol] = new Map<string, Promise<void>>();\n }\n\n return globalQueues[feePayerQueueSymbol];\n}\n\nexport async function getStateProof(\n client: ThruClient,\n address: string,\n proofType: number = 1,\n targetSlot?: bigint\n): Promise<Uint8Array> {\n const proofRequest: {\n address: string;\n proofType: number;\n targetSlot?: bigint;\n } = {\n address,\n proofType,\n };\n\n if (targetSlot !== undefined) {\n proofRequest.targetSlot = targetSlot;\n }\n\n const proof = await client.proofs.generate(proofRequest);\n\n if (!proof.proof || proof.proof.length === 0) {\n throw new Error(`No state proof returned for ${address}`);\n }\n\n return proof.proof;\n}\n\nexport async function trackTransaction(\n client: ThruClient,\n signature: string,\n timeoutMs: number = 5000\n): Promise<TransactionResult> {\n try {\n let finalizedSeen = false;\n\n for await (const update of client.transactions.track(signature, { timeoutMs })) {\n if (update.executionResult) {\n const vmError =\n update.executionResult.vmError !== undefined && update.executionResult.vmError !== null\n ? BigInt(update.executionResult.vmError)\n : 0n;\n const userErrorCode = update.executionResult.userErrorCode;\n const executionError =\n update.executionResult.executionResult !== undefined &&\n update.executionResult.executionResult !== null\n ? BigInt(update.executionResult.executionResult)\n : 0n;\n const success = vmError === 0n && executionError === 0n && userErrorCode === 0n;\n\n return {\n signature,\n status: success ? 'finalized' : 'failed',\n errorCode: vmError !== 0n ? vmError : executionError !== 0n ? executionError : userErrorCode,\n };\n }\n\n if (update.statusCode === 3) {\n finalizedSeen = true;\n }\n }\n\n if (finalizedSeen) {\n return {\n signature,\n status: 'finalized_without_execution',\n };\n }\n } catch {\n return {\n signature,\n status: 'timeout',\n };\n }\n\n return {\n signature,\n status: 'timeout',\n };\n}\n\nexport function toThruAddress(bytes: Uint8Array): string {\n return encodeAddress(bytes);\n}\n\nexport async function withSerializedFeePayer<T>(\n feePayerPublicKey: Uint8Array,\n work: () => Promise<T>\n): Promise<T> {\n const queueKey = toThruAddress(feePayerPublicKey);\n const feePayerQueues = getFeePayerQueues();\n const previous = feePayerQueues.get(queueKey) ?? Promise.resolve();\n let release!: () => void;\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const tail = previous.then(() => current);\n feePayerQueues.set(queueKey, tail);\n\n await previous;\n\n try {\n return await work();\n } finally {\n release();\n if (feePayerQueues.get(queueKey) === tail) {\n feePayerQueues.delete(queueKey);\n }\n }\n}\n","import {\n bytesToBase64Url,\n createValidateChallenge,\n fetchWalletNonce,\n} from '@thru/passkey-manager';\nimport type { AccountContext } from '@thru/passkey-manager';\nimport type { PasskeyChallengeResult, ThruClient } from './types';\n\nexport async function createPasskeyChallenge(opts: {\n client: ThruClient;\n walletAddress: string;\n accountCtx: AccountContext;\n invokeIx: Uint8Array;\n}): Promise<PasskeyChallengeResult> {\n const nonce = await fetchWalletNonce(opts.client, opts.walletAddress);\n const challenge = await createValidateChallenge(\n nonce,\n opts.accountCtx.accountAddresses,\n opts.invokeIx\n );\n\n return {\n challenge: bytesToBase64Url(challenge),\n nonce: nonce.toString(),\n };\n}\n","import {\n PASSKEY_MANAGER_PROGRAM_ADDRESS,\n concatenateInstructions,\n encodeValidateInstruction,\n hexToBytes,\n} from '@thru/passkey-manager';\nimport type { AccountContext } from '@thru/passkey-manager';\nimport { trackTransaction, withSerializedFeePayer } from './utils';\nimport type {\n PasskeySignaturePayload,\n ThruClient,\n TransactionResult,\n} from './types';\n\nexport async function submitPasskeyTransaction(opts: {\n client: ThruClient;\n adminPublicKey: Uint8Array;\n adminPrivateKey: string;\n walletAddress: string;\n accountCtx: AccountContext;\n invokeIx: Uint8Array;\n} & PasskeySignaturePayload): Promise<TransactionResult> {\n return withSerializedFeePayer(opts.adminPublicKey, async () => {\n const validateIx = encodeValidateInstruction({\n walletAccountIdx: opts.accountCtx.walletAccountIdx,\n authIdx: 0,\n signatureR: hexToBytes(opts.signatureR),\n signatureS: hexToBytes(opts.signatureS),\n authenticatorData: Buffer.from(opts.authenticatorData, 'base64'),\n clientDataJSON: Buffer.from(opts.clientDataJSON, 'base64'),\n });\n\n const instructionData = concatenateInstructions([validateIx, opts.invokeIx]);\n const transaction = await opts.client.transactions.build({\n feePayer: { publicKey: opts.adminPublicKey },\n program: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n instructionData,\n accounts: {\n readWrite: opts.accountCtx.readWriteAddresses,\n readOnly: opts.accountCtx.readOnlyAddresses,\n },\n header: { fee: 0n },\n });\n\n await transaction.sign(opts.adminPrivateKey);\n const signature = await opts.client.transactions.send(transaction.toWire());\n return trackTransaction(opts.client, signature);\n });\n}\n","import type { PasskeyContextResult } from './types';\nimport { createPasskeyChallenge } from './challenge';\nimport { submitPasskeyTransaction } from './submit';\nimport type {\n PasskeyChallengeSubmitPayload,\n ThruClient,\n TransactionResult,\n} from './types';\n\nexport function createPasskeyHandlers<P>(opts: {\n buildContext: (params: P) => Promise<PasskeyContextResult>;\n adminPublicKey: Uint8Array;\n adminPrivateKey: string;\n client: ThruClient;\n challengeTtlMs?: number;\n}) {\n const pendingContexts = new Map<\n string,\n { context: PasskeyContextResult; createdAt: number }\n >();\n const challengeTtlMs = opts.challengeTtlMs ?? 5 * 60_000;\n\n function createPendingContextKey(\n walletAddress: string,\n nonce: string,\n challenge: string\n ): string {\n return `${walletAddress}:${nonce}:${challenge}`;\n }\n\n function prunePendingContexts(now = Date.now()): void {\n for (const [nonce, entry] of pendingContexts.entries()) {\n if (now - entry.createdAt > challengeTtlMs) {\n pendingContexts.delete(nonce);\n }\n }\n }\n\n return {\n challenge: async (walletAddress: string, params: P) => {\n prunePendingContexts();\n\n const context = await opts.buildContext(params);\n const challenge = await createPasskeyChallenge({\n client: opts.client,\n walletAddress,\n accountCtx: context.accountCtx,\n invokeIx: context.invokeIx,\n });\n\n pendingContexts.set(\n createPendingContextKey(walletAddress, challenge.nonce, challenge.challenge),\n {\n context,\n createdAt: Date.now(),\n }\n );\n\n return challenge;\n },\n submit: async (\n walletAddress: string,\n params: P,\n payload: PasskeyChallengeSubmitPayload\n ): Promise<TransactionResult> => {\n void params;\n prunePendingContexts();\n\n const pendingKey = createPendingContextKey(\n walletAddress,\n payload.nonce,\n payload.challenge\n );\n const pending = pendingContexts.get(pendingKey);\n if (!pending) {\n throw new Error('Missing or expired challenge nonce');\n }\n\n pendingContexts.delete(pendingKey);\n const { nonce: _nonce, challenge: _challenge, ...signaturePayload } = payload;\n\n return submitPasskeyTransaction({\n client: opts.client,\n adminPublicKey: opts.adminPublicKey,\n adminPrivateKey: opts.adminPrivateKey,\n walletAddress,\n accountCtx: pending.context.accountCtx,\n invokeIx: pending.context.invokeIx,\n ...signaturePayload,\n });\n },\n };\n}\n"],"mappings":"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","names":["PASSKEY_MANAGER_PROGRAM_ADDRESS","hexToBytes","hexToBytes","PASSKEY_MANAGER_PROGRAM_ADDRESS"]}
|
|
1
|
+
{"version":3,"sources":["../src/server/create-wallet.ts","../../helpers/src/constants.ts","../../helpers/src/address.ts","../src/server/utils.ts","../src/server/challenge.ts","../src/server/submit.ts","../src/server/handlers.ts"],"sourcesContent":["import {\n PASSKEY_MANAGER_PROGRAM_ADDRESS,\n base64UrlToBytes,\n buildAccountContext,\n createCredentialLookupSeed,\n createWalletSeed,\n deriveCredentialLookupAddress,\n deriveWalletAddress,\n encodeCreateInstruction,\n encodeRegisterCredentialInstruction,\n} from '@thru/passkey-manager';\nimport {\n toThruAddress,\n getStateProof,\n trackTransaction,\n withSerializedFeePayer,\n} from './utils';\nimport type { ThruClient } from './types';\n\nexport async function createPasskeyWallet(opts: {\n client: ThruClient;\n adminPublicKey: Uint8Array;\n adminPrivateKey: Uint8Array;\n adminAddress: string;\n pubkeyX: Uint8Array;\n pubkeyY: Uint8Array;\n credentialId?: string;\n walletName?: string;\n}): Promise<{ walletAddress: string; credentialLookupAddress?: string }> {\n const walletName = opts.walletName ?? 'default';\n const seed = await createWalletSeed(walletName, opts.pubkeyX, opts.pubkeyY);\n const walletBytes = await deriveWalletAddress(seed, PASSKEY_MANAGER_PROGRAM_ADDRESS);\n const walletAddress = toThruAddress(walletBytes);\n\n await withSerializedFeePayer(opts.adminPublicKey, async () => {\n let walletExists = false;\n try {\n await opts.client.accounts.get(walletAddress);\n walletExists = true;\n } catch {\n walletExists = false;\n }\n\n if (walletExists) return;\n\n const stateProof = await getStateProof(opts.client, walletAddress);\n const accountCtx = buildAccountContext({\n walletAddress,\n readWriteAccounts: [],\n readOnlyAccounts: [],\n feePayerAddress: opts.adminAddress,\n programAddress: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n });\n\n const createIx = encodeCreateInstruction({\n walletAccountIdx: accountCtx.walletAccountIdx,\n authority: {\n tag: 1,\n pubkeyX: opts.pubkeyX,\n pubkeyY: opts.pubkeyY,\n },\n seed,\n stateProof,\n });\n\n const transaction = await opts.client.transactions.build({\n feePayer: { publicKey: opts.adminPublicKey },\n program: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n instructionData: createIx,\n accounts: {\n readWrite: accountCtx.readWriteAddresses,\n readOnly: accountCtx.readOnlyAddresses,\n },\n header: { fee: 0n },\n });\n\n await transaction.sign(opts.adminPrivateKey);\n const signature = await opts.client.transactions.send(transaction.toWire());\n const result = await trackTransaction(opts.client, signature, 60000);\n if (result.status !== 'finalized') {\n throw new Error(\n `Wallet creation failed with status: ${result.status}${\n result.errorCode !== undefined ? ` (error code: ${result.errorCode})` : ''\n }`\n );\n }\n });\n\n let credentialLookupAddress: string | undefined;\n if (opts.credentialId) {\n const credentialIdBytes = base64UrlToBytes(opts.credentialId);\n const lookupAddressBytes = await deriveCredentialLookupAddress(\n credentialIdBytes,\n walletName,\n PASSKEY_MANAGER_PROGRAM_ADDRESS\n );\n const lookupAddress = toThruAddress(lookupAddressBytes);\n\n credentialLookupAddress = lookupAddress;\n\n try {\n await withSerializedFeePayer(opts.adminPublicKey, async () => {\n let lookupExists = false;\n try {\n await opts.client.accounts.get(lookupAddress);\n lookupExists = true;\n } catch {\n lookupExists = false;\n }\n\n if (lookupExists) return;\n\n const credSeed = await createCredentialLookupSeed(credentialIdBytes, walletName);\n const stateProof = await getStateProof(opts.client, lookupAddress);\n const accountCtx = buildAccountContext({\n walletAddress,\n readWriteAccounts: [lookupAddressBytes],\n readOnlyAccounts: [],\n feePayerAddress: opts.adminAddress,\n programAddress: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n });\n\n const registerIx = encodeRegisterCredentialInstruction({\n walletAccountIdx: accountCtx.walletAccountIdx,\n lookupAccountIdx: accountCtx.getAccountIndex(lookupAddressBytes),\n seed: credSeed,\n stateProof,\n });\n\n const transaction = await opts.client.transactions.build({\n feePayer: { publicKey: opts.adminPublicKey },\n program: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n instructionData: registerIx,\n accounts: {\n readWrite: accountCtx.readWriteAddresses,\n readOnly: accountCtx.readOnlyAddresses,\n },\n header: { fee: 0n },\n });\n\n await transaction.sign(opts.adminPrivateKey);\n const signature = await opts.client.transactions.send(transaction.toWire());\n const result = await trackTransaction(opts.client, signature, 60000);\n if (result.status !== 'finalized') {\n throw new Error(\n `Credential registration failed with status: ${result.status}${\n result.errorCode !== undefined ? ` (error code: ${result.errorCode})` : ''\n }`\n );\n }\n });\n } catch (error) {\n console.warn('Credential registration failed (non-fatal):', error);\n }\n }\n\n return {\n walletAddress,\n credentialLookupAddress,\n };\n}\n","export const BASE64_URL_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\n\nconst tempMap = new Int16Array(256).fill(-1);\nfor (let i = 0; i < BASE64_URL_ALPHABET.length; i++) {\n tempMap[BASE64_URL_ALPHABET.charCodeAt(i)] = i;\n}\nexport const BASE64_URL_MAP = tempMap\n\n","import { BASE64_URL_ALPHABET, BASE64_URL_MAP } from \"./constants\";\n\nexport function encodeAddress(bytes: Uint8Array): string {\n if (bytes.length !== 32) {\n throw new Error('Expected 32-byte address');\n }\n\n let checksum = 0;\n let accumulator = 0;\n let bitsCollected = 0;\n const output: string[] = ['t', 'a'];\n\n for (let i = 0; i < 30; i++) {\n const byte = bytes[i];\n checksum += byte;\n accumulator = ((accumulator << 8) | byte) >>> 0;\n bitsCollected += 8;\n while (bitsCollected >= 6) {\n const index = (accumulator >> (bitsCollected - 6)) & 0x3f;\n output.push(BASE64_URL_ALPHABET[index]);\n bitsCollected -= 6;\n accumulator &= maskForBits(bitsCollected);\n }\n }\n\n const secondLast = bytes[30];\n checksum += secondLast;\n accumulator = ((accumulator << 8) | secondLast) >>> 0;\n bitsCollected += 8;\n\n const last = bytes[31];\n checksum += last;\n accumulator = ((accumulator << 8) | last) >>> 0;\n bitsCollected += 8;\n\n accumulator = ((accumulator << 8) | (checksum & 0xff)) >>> 0;\n bitsCollected += 8;\n\n while (bitsCollected >= 6) {\n const index = (accumulator >> (bitsCollected - 6)) & 0x3f;\n output.push(BASE64_URL_ALPHABET[index]);\n bitsCollected -= 6;\n accumulator &= maskForBits(bitsCollected);\n }\n\n return output.join('');\n}\n\nexport function decodeAddress(value: string): Uint8Array {\n if (value.length !== 46) {\n throw new Error('Invalid address length');\n }\n if (!value.startsWith('ta')) {\n throw new Error('Address must start with \"ta\"');\n }\n\n const output = new Uint8Array(32);\n let checksum = 0;\n let inIdx = 2;\n let remaining = 40;\n let outIdx = 0;\n\n while (remaining >= 4) {\n const a = BASE64_URL_MAP[value.charCodeAt(inIdx)];\n const b = BASE64_URL_MAP[value.charCodeAt(inIdx + 1)];\n const c = BASE64_URL_MAP[value.charCodeAt(inIdx + 2)];\n const d = BASE64_URL_MAP[value.charCodeAt(inIdx + 3)];\n if (a < 0 || b < 0 || c < 0 || d < 0) {\n throw new Error('Invalid address encoding');\n }\n const triple = (a << 18) | (b << 12) | (c << 6) | d;\n const byte1 = (triple >> 16) & 0xff;\n const byte2 = (triple >> 8) & 0xff;\n const byte3 = triple & 0xff;\n checksum += byte1;\n checksum += byte2;\n checksum += byte3;\n output[outIdx++] = byte1;\n output[outIdx++] = byte2;\n output[outIdx++] = byte3;\n inIdx += 4;\n remaining -= 4;\n }\n\n const a = BASE64_URL_MAP[value.charCodeAt(inIdx)];\n const b = BASE64_URL_MAP[value.charCodeAt(inIdx + 1)];\n const c = BASE64_URL_MAP[value.charCodeAt(inIdx + 2)];\n const d = BASE64_URL_MAP[value.charCodeAt(inIdx + 3)];\n if (a < 0 || b < 0 || c < 0 || d < 0) {\n throw new Error('Invalid address encoding');\n }\n const triple = (a << 18) | (b << 12) | (c << 6) | d;\n const byte1 = (triple >> 16) & 0xff;\n const byte2 = (triple >> 8) & 0xff;\n const incomingChecksum = triple & 0xff;\n\n checksum += byte1;\n checksum += byte2;\n output[outIdx++] = byte1;\n output[outIdx++] = byte2;\n\n checksum &= 0xff;\n if (checksum !== incomingChecksum) {\n throw new Error('Address checksum mismatch');\n }\n\n return output;\n}\n\nfunction maskForBits(bits: number): number {\n return bits === 0 ? 0 : (1 << bits) - 1;\n}\n","import { encodeAddress } from '@thru/helpers';\nimport type { ThruClient, TransactionResult } from './types';\n\nconst feePayerQueueSymbol = Symbol.for('thru.sharedFeePayerQueues');\n\nfunction getFeePayerQueues(): Map<string, Promise<void>> {\n const globalQueues = globalThis as typeof globalThis & {\n [feePayerQueueSymbol]?: Map<string, Promise<void>>;\n };\n\n if (!globalQueues[feePayerQueueSymbol]) {\n globalQueues[feePayerQueueSymbol] = new Map<string, Promise<void>>();\n }\n\n return globalQueues[feePayerQueueSymbol];\n}\n\nexport async function getStateProof(\n client: ThruClient,\n address: string,\n proofType: number = 1,\n targetSlot?: bigint\n): Promise<Uint8Array> {\n const proofRequest: {\n address: string;\n proofType: number;\n targetSlot?: bigint;\n } = {\n address,\n proofType,\n };\n\n if (targetSlot !== undefined) {\n proofRequest.targetSlot = targetSlot;\n }\n\n const proof = await client.proofs.generate(proofRequest);\n\n if (!proof.proof || proof.proof.length === 0) {\n throw new Error(`No state proof returned for ${address}`);\n }\n\n return proof.proof;\n}\n\nexport async function trackTransaction(\n client: ThruClient,\n signature: string,\n timeoutMs: number = 5000\n): Promise<TransactionResult> {\n let finalizedSeen = false;\n\n try {\n for await (const update of client.transactions.track(signature, { timeoutMs })) {\n if (update.executionResult) {\n const vmError =\n update.executionResult.vmError !== undefined && update.executionResult.vmError !== null\n ? BigInt(update.executionResult.vmError)\n : 0n;\n const userErrorCode = update.executionResult.userErrorCode;\n const executionError =\n update.executionResult.executionResult !== undefined &&\n update.executionResult.executionResult !== null\n ? BigInt(update.executionResult.executionResult)\n : 0n;\n const success = vmError === 0n && executionError === 0n && userErrorCode === 0n;\n\n return {\n signature,\n status: success ? 'finalized' : 'failed',\n errorCode: vmError !== 0n ? vmError : executionError !== 0n ? executionError : userErrorCode,\n };\n }\n\n if (update.statusCode === 3) {\n finalizedSeen = true;\n }\n }\n\n if (finalizedSeen) {\n return {\n signature,\n status: 'finalized_without_execution',\n };\n }\n } catch {\n if (finalizedSeen) {\n return {\n signature,\n status: 'finalized_without_execution',\n };\n }\n\n return {\n signature,\n status: 'timeout',\n };\n }\n\n return {\n signature,\n status: 'timeout',\n };\n}\n\nexport function toThruAddress(bytes: Uint8Array): string {\n return encodeAddress(bytes);\n}\n\nexport async function withSerializedFeePayer<T>(\n feePayerPublicKey: Uint8Array,\n work: () => Promise<T>\n): Promise<T> {\n const queueKey = toThruAddress(feePayerPublicKey);\n const feePayerQueues = getFeePayerQueues();\n const previous = feePayerQueues.get(queueKey) ?? Promise.resolve();\n let release!: () => void;\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const tail = previous.then(() => current);\n feePayerQueues.set(queueKey, tail);\n\n await previous;\n\n try {\n return await work();\n } finally {\n release();\n if (feePayerQueues.get(queueKey) === tail) {\n feePayerQueues.delete(queueKey);\n }\n }\n}\n","import {\n bytesToBase64Url,\n createValidateChallenge,\n fetchWalletNonce,\n} from '@thru/passkey-manager';\nimport type { AccountContext } from '@thru/passkey-manager';\nimport type { PasskeyChallengeResult, ThruClient } from './types';\n\nexport async function createPasskeyChallenge(opts: {\n client: ThruClient;\n walletAddress: string;\n accountCtx: AccountContext;\n invokeIx: Uint8Array;\n}): Promise<PasskeyChallengeResult> {\n const nonce = await fetchWalletNonce(opts.client, opts.walletAddress);\n const challenge = await createValidateChallenge(\n nonce,\n opts.accountCtx.accountAddresses,\n opts.invokeIx\n );\n\n return {\n challenge: bytesToBase64Url(challenge),\n nonce: nonce.toString(),\n };\n}\n","import {\n PASSKEY_MANAGER_PROGRAM_ADDRESS,\n concatenateInstructions,\n encodeValidateInstruction,\n hexToBytes,\n} from '@thru/passkey-manager';\nimport type { AccountContext } from '@thru/passkey-manager';\nimport { trackTransaction, withSerializedFeePayer } from './utils';\nimport type {\n BuiltPasskeyTransaction,\n PasskeySignaturePayload,\n PasskeyTransactionHeaderOverrides,\n ThruClient,\n TransactionResult,\n} from './types';\n\n/**\n * Builds and signs a passkey-manager transaction without submitting it.\n *\n * Callers that override the transaction nonce are responsible for coordinating\n * fee-payer nonce allocation before calling this helper. Use\n * `submitPasskeyTransaction` for the serialized one-off submit path.\n */\nexport async function buildPasskeyTransaction(opts: {\n client: ThruClient;\n adminPublicKey: Uint8Array;\n adminPrivateKey: Uint8Array;\n walletAddress: string;\n accountCtx: AccountContext;\n invokeIx: Uint8Array;\n header?: PasskeyTransactionHeaderOverrides;\n} & PasskeySignaturePayload): Promise<BuiltPasskeyTransaction> {\n const validateIx = encodeValidateInstruction({\n walletAccountIdx: opts.accountCtx.walletAccountIdx,\n authIdx: 0,\n signatureR: hexToBytes(opts.signatureR),\n signatureS: hexToBytes(opts.signatureS),\n authenticatorData: Buffer.from(opts.authenticatorData, 'base64'),\n clientDataJSON: Buffer.from(opts.clientDataJSON, 'base64'),\n });\n\n const instructionData = concatenateInstructions([validateIx, opts.invokeIx]);\n const transaction = await opts.client.transactions.build({\n feePayer: { publicKey: opts.adminPublicKey },\n program: PASSKEY_MANAGER_PROGRAM_ADDRESS,\n instructionData,\n accounts: {\n readWrite: opts.accountCtx.readWriteAddresses,\n readOnly: opts.accountCtx.readOnlyAddresses,\n },\n header: {\n fee: 0n,\n ...opts.header,\n },\n });\n\n await transaction.sign(opts.adminPrivateKey);\n return {\n transaction,\n rawTransaction: transaction.toWire(),\n };\n}\n\nexport async function submitPasskeyTransaction(opts: {\n client: ThruClient;\n adminPublicKey: Uint8Array;\n adminPrivateKey: Uint8Array;\n walletAddress: string;\n accountCtx: AccountContext;\n invokeIx: Uint8Array;\n header?: PasskeyTransactionHeaderOverrides;\n} & PasskeySignaturePayload): Promise<TransactionResult> {\n return withSerializedFeePayer(opts.adminPublicKey, async () => {\n const { rawTransaction } = await buildPasskeyTransaction(opts);\n const signature = await opts.client.transactions.send(rawTransaction);\n return trackTransaction(opts.client, signature);\n });\n}\n","import type { PasskeyContextResult } from './types';\nimport { createPasskeyChallenge } from './challenge';\nimport { submitPasskeyTransaction } from './submit';\nimport type {\n PasskeyChallengeSubmitPayload,\n ThruClient,\n TransactionResult,\n} from './types';\n\nexport function createPasskeyHandlers<P>(opts: {\n buildContext: (params: P) => Promise<PasskeyContextResult>;\n adminPublicKey: Uint8Array;\n adminPrivateKey: Uint8Array;\n client: ThruClient;\n challengeTtlMs?: number;\n}) {\n const pendingContexts = new Map<\n string,\n { context: PasskeyContextResult; createdAt: number }\n >();\n const challengeTtlMs = opts.challengeTtlMs ?? 5 * 60_000;\n\n function createPendingContextKey(\n walletAddress: string,\n nonce: string,\n challenge: string\n ): string {\n return `${walletAddress}:${nonce}:${challenge}`;\n }\n\n function prunePendingContexts(now = Date.now()): void {\n for (const [nonce, entry] of pendingContexts.entries()) {\n if (now - entry.createdAt > challengeTtlMs) {\n pendingContexts.delete(nonce);\n }\n }\n }\n\n return {\n challenge: async (walletAddress: string, params: P) => {\n prunePendingContexts();\n\n const context = await opts.buildContext(params);\n const challenge = await createPasskeyChallenge({\n client: opts.client,\n walletAddress,\n accountCtx: context.accountCtx,\n invokeIx: context.invokeIx,\n });\n\n pendingContexts.set(\n createPendingContextKey(walletAddress, challenge.nonce, challenge.challenge),\n {\n context,\n createdAt: Date.now(),\n }\n );\n\n return challenge;\n },\n submit: async (\n walletAddress: string,\n params: P,\n payload: PasskeyChallengeSubmitPayload\n ): Promise<TransactionResult> => {\n void params;\n prunePendingContexts();\n\n const pendingKey = createPendingContextKey(\n walletAddress,\n payload.nonce,\n payload.challenge\n );\n const pending = pendingContexts.get(pendingKey);\n if (!pending) {\n throw new Error('Missing or expired challenge nonce');\n }\n\n pendingContexts.delete(pendingKey);\n const { nonce: _nonce, challenge: _challenge, ...signaturePayload } = payload;\n\n return submitPasskeyTransaction({\n client: opts.client,\n adminPublicKey: opts.adminPublicKey,\n adminPrivateKey: opts.adminPrivateKey,\n walletAddress,\n accountCtx: pending.context.accountCtx,\n invokeIx: pending.context.invokeIx,\n ...signaturePayload,\n });\n },\n };\n}\n"],"mappings":"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","names":["PASSKEY_MANAGER_PROGRAM_ADDRESS","hexToBytes","hexToBytes","PASSKEY_MANAGER_PROGRAM_ADDRESS"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thru/passkey",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.18",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@thru/passkey-manager": "0.2.
|
|
41
|
+
"@thru/passkey-manager": "0.2.18",
|
|
42
|
+
"@thru/thru-sdk": "0.2.18"
|
|
42
43
|
},
|
|
43
44
|
"peerDependencies": {
|
|
44
45
|
"expo-secure-store": "*",
|
|
@@ -13,9 +13,14 @@ vi.mock('@thru/helpers', () => ({
|
|
|
13
13
|
vi.mock('@thru/passkey-manager', () => ({
|
|
14
14
|
PASSKEY_MANAGER_PROGRAM_ADDRESS: 'passkey-program',
|
|
15
15
|
base64UrlToBytes: () => new Uint8Array([7]),
|
|
16
|
-
buildAccountContext: () => ({
|
|
17
|
-
walletAccountIdx: 0,
|
|
18
|
-
|
|
16
|
+
buildAccountContext: (params: { readWriteAccounts: Uint8Array[] }) => ({
|
|
17
|
+
walletAccountIdx: params.readWriteAccounts.length === 0 ? 2 : 3,
|
|
18
|
+
readWriteAddresses:
|
|
19
|
+
params.readWriteAccounts.length === 0
|
|
20
|
+
? ['wallet-address']
|
|
21
|
+
: ['lookup-address', 'wallet-address'],
|
|
22
|
+
readOnlyAddresses: [],
|
|
23
|
+
getAccountIndex: () => 2,
|
|
19
24
|
}),
|
|
20
25
|
createCredentialLookupSeed: async () => new Uint8Array([8]),
|
|
21
26
|
createWalletSeed: async () => new Uint8Array([1]),
|
|
@@ -130,7 +135,7 @@ describe('createPasskeyWallet', () => {
|
|
|
130
135
|
const opts = {
|
|
131
136
|
client,
|
|
132
137
|
adminPublicKey: new Uint8Array([1, 2, 3]),
|
|
133
|
-
adminPrivateKey:
|
|
138
|
+
adminPrivateKey: new Uint8Array([9, 9, 9]),
|
|
134
139
|
adminAddress: 'admin-address',
|
|
135
140
|
pubkeyX: new Uint8Array([4]),
|
|
136
141
|
pubkeyY: new Uint8Array([5]),
|
|
@@ -165,6 +170,15 @@ describe('createPasskeyWallet', () => {
|
|
|
165
170
|
expect(state.walletTrackCount).toBe(1);
|
|
166
171
|
expect(state.lookupTrackCount).toBe(1);
|
|
167
172
|
|
|
173
|
+
expect(vi.mocked(client.transactions.build).mock.calls[0]?.[0].accounts).toEqual({
|
|
174
|
+
readWrite: ['wallet-address'],
|
|
175
|
+
readOnly: [],
|
|
176
|
+
});
|
|
177
|
+
expect(vi.mocked(client.transactions.build).mock.calls[1]?.[0].accounts).toEqual({
|
|
178
|
+
readWrite: ['lookup-address', 'wallet-address'],
|
|
179
|
+
readOnly: [],
|
|
180
|
+
});
|
|
181
|
+
|
|
168
182
|
const accountChecks = vi.mocked(client.accounts.get).mock.calls.map(([address]) => address);
|
|
169
183
|
expect(accountChecks.filter((address) => address === 'wallet-address')).toHaveLength(2);
|
|
170
184
|
expect(accountChecks.filter((address) => address === 'lookup-address')).toHaveLength(2);
|
|
@@ -20,7 +20,7 @@ import type { ThruClient } from './types';
|
|
|
20
20
|
export async function createPasskeyWallet(opts: {
|
|
21
21
|
client: ThruClient;
|
|
22
22
|
adminPublicKey: Uint8Array;
|
|
23
|
-
adminPrivateKey:
|
|
23
|
+
adminPrivateKey: Uint8Array;
|
|
24
24
|
adminAddress: string;
|
|
25
25
|
pubkeyX: Uint8Array;
|
|
26
26
|
pubkeyY: Uint8Array;
|
|
@@ -68,8 +68,8 @@ export async function createPasskeyWallet(opts: {
|
|
|
68
68
|
program: PASSKEY_MANAGER_PROGRAM_ADDRESS,
|
|
69
69
|
instructionData: createIx,
|
|
70
70
|
accounts: {
|
|
71
|
-
readWrite:
|
|
72
|
-
readOnly:
|
|
71
|
+
readWrite: accountCtx.readWriteAddresses,
|
|
72
|
+
readOnly: accountCtx.readOnlyAddresses,
|
|
73
73
|
},
|
|
74
74
|
header: { fee: 0n },
|
|
75
75
|
});
|
|
@@ -132,8 +132,8 @@ export async function createPasskeyWallet(opts: {
|
|
|
132
132
|
program: PASSKEY_MANAGER_PROGRAM_ADDRESS,
|
|
133
133
|
instructionData: registerIx,
|
|
134
134
|
accounts: {
|
|
135
|
-
readWrite:
|
|
136
|
-
readOnly:
|
|
135
|
+
readWrite: accountCtx.readWriteAddresses,
|
|
136
|
+
readOnly: accountCtx.readOnlyAddresses,
|
|
137
137
|
},
|
|
138
138
|
header: { fee: 0n },
|
|
139
139
|
});
|
package/src/server/handlers.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
export function createPasskeyHandlers<P>(opts: {
|
|
11
11
|
buildContext: (params: P) => Promise<PasskeyContextResult>;
|
|
12
12
|
adminPublicKey: Uint8Array;
|
|
13
|
-
adminPrivateKey:
|
|
13
|
+
adminPrivateKey: Uint8Array;
|
|
14
14
|
client: ThruClient;
|
|
15
15
|
challengeTtlMs?: number;
|
|
16
16
|
}) {
|
package/src/server/index.ts
CHANGED
|
@@ -2,6 +2,8 @@ export type {
|
|
|
2
2
|
ThruClient,
|
|
3
3
|
PasskeySignaturePayload,
|
|
4
4
|
PasskeyChallengeSubmitPayload,
|
|
5
|
+
PasskeyTransactionHeaderOverrides,
|
|
6
|
+
BuiltPasskeyTransaction,
|
|
5
7
|
TransactionResult,
|
|
6
8
|
PasskeyChallengeResult,
|
|
7
9
|
PasskeyContextResult,
|
|
@@ -9,5 +11,5 @@ export type {
|
|
|
9
11
|
|
|
10
12
|
export { createPasskeyWallet } from './create-wallet';
|
|
11
13
|
export { createPasskeyChallenge } from './challenge';
|
|
12
|
-
export { submitPasskeyTransaction } from './submit';
|
|
14
|
+
export { buildPasskeyTransaction, submitPasskeyTransaction } from './submit';
|
|
13
15
|
export { createPasskeyHandlers } from './handlers';
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import type { AccountContext } from '@thru/passkey-manager';
|
|
3
|
+
import type { ThruClient } from './types';
|
|
4
|
+
|
|
5
|
+
vi.mock('@thru/passkey-manager', () => ({
|
|
6
|
+
PASSKEY_MANAGER_PROGRAM_ADDRESS: 'passkey-program',
|
|
7
|
+
concatenateInstructions: (instructions: Uint8Array[]) => new Uint8Array(instructions.flatMap((ix) => Array.from(ix))),
|
|
8
|
+
encodeValidateInstruction: () => new Uint8Array([1, 2]),
|
|
9
|
+
hexToBytes: (value: string) => new Uint8Array(Buffer.from(value, 'hex')),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
import { buildPasskeyTransaction, submitPasskeyTransaction } from './submit';
|
|
13
|
+
|
|
14
|
+
const accountCtx = {
|
|
15
|
+
walletAccountIdx: 3,
|
|
16
|
+
readWriteAddresses: ['lookup-address', 'wallet-address'],
|
|
17
|
+
readOnlyAddresses: ['readonly-address'],
|
|
18
|
+
} as AccountContext;
|
|
19
|
+
|
|
20
|
+
const signaturePayload = {
|
|
21
|
+
signatureR: '11'.repeat(32),
|
|
22
|
+
signatureS: '22'.repeat(32),
|
|
23
|
+
authenticatorData: Buffer.from('authenticator').toString('base64'),
|
|
24
|
+
clientDataJSON: Buffer.from('client').toString('base64'),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function createClient() {
|
|
28
|
+
const transaction = {
|
|
29
|
+
sign: vi.fn(async () => {}),
|
|
30
|
+
toWire: vi.fn(() => new Uint8Array([9, 9, 9])),
|
|
31
|
+
};
|
|
32
|
+
const client = {
|
|
33
|
+
transactions: {
|
|
34
|
+
build: vi.fn(async () => transaction),
|
|
35
|
+
send: vi.fn(async () => 'tx-signature'),
|
|
36
|
+
track: vi.fn(async function* () {
|
|
37
|
+
yield {
|
|
38
|
+
executionResult: {
|
|
39
|
+
userErrorCode: 0n,
|
|
40
|
+
vmError: 0,
|
|
41
|
+
executionResult: 0n,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}),
|
|
45
|
+
},
|
|
46
|
+
} as unknown as ThruClient;
|
|
47
|
+
|
|
48
|
+
return { client, transaction };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
describe('passkey submit', () => {
|
|
52
|
+
it('builds and signs a passkey transaction with explicit header overrides', async () => {
|
|
53
|
+
const { client, transaction } = createClient();
|
|
54
|
+
|
|
55
|
+
const result = await buildPasskeyTransaction({
|
|
56
|
+
client,
|
|
57
|
+
adminPublicKey: new Uint8Array(32).fill(1),
|
|
58
|
+
adminPrivateKey: new Uint8Array(32).fill(2),
|
|
59
|
+
walletAddress: 'wallet-address',
|
|
60
|
+
accountCtx,
|
|
61
|
+
invokeIx: new Uint8Array([3, 4]),
|
|
62
|
+
header: {
|
|
63
|
+
fee: 0n,
|
|
64
|
+
nonce: 42n,
|
|
65
|
+
},
|
|
66
|
+
...signaturePayload,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
expect(client.transactions.build).toHaveBeenCalledWith(expect.objectContaining({
|
|
70
|
+
program: 'passkey-program',
|
|
71
|
+
accounts: {
|
|
72
|
+
readWrite: ['lookup-address', 'wallet-address'],
|
|
73
|
+
readOnly: ['readonly-address'],
|
|
74
|
+
},
|
|
75
|
+
header: {
|
|
76
|
+
fee: 0n,
|
|
77
|
+
nonce: 42n,
|
|
78
|
+
},
|
|
79
|
+
}));
|
|
80
|
+
expect(transaction.sign).toHaveBeenCalledWith(new Uint8Array(32).fill(2));
|
|
81
|
+
expect(result.rawTransaction).toEqual(new Uint8Array([9, 9, 9]));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('keeps submitPasskeyTransaction as a send and track convenience wrapper', async () => {
|
|
85
|
+
const { client } = createClient();
|
|
86
|
+
|
|
87
|
+
await expect(submitPasskeyTransaction({
|
|
88
|
+
client,
|
|
89
|
+
adminPublicKey: new Uint8Array(32).fill(1),
|
|
90
|
+
adminPrivateKey: new Uint8Array(32).fill(2),
|
|
91
|
+
walletAddress: 'wallet-address',
|
|
92
|
+
accountCtx,
|
|
93
|
+
invokeIx: new Uint8Array([3, 4]),
|
|
94
|
+
...signaturePayload,
|
|
95
|
+
})).resolves.toEqual({
|
|
96
|
+
signature: 'tx-signature',
|
|
97
|
+
status: 'finalized',
|
|
98
|
+
errorCode: 0n,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
expect(client.transactions.send).toHaveBeenCalledWith(new Uint8Array([9, 9, 9]));
|
|
102
|
+
});
|
|
103
|
+
});
|
package/src/server/submit.ts
CHANGED
|
@@ -7,43 +7,72 @@ import {
|
|
|
7
7
|
import type { AccountContext } from '@thru/passkey-manager';
|
|
8
8
|
import { trackTransaction, withSerializedFeePayer } from './utils';
|
|
9
9
|
import type {
|
|
10
|
+
BuiltPasskeyTransaction,
|
|
10
11
|
PasskeySignaturePayload,
|
|
12
|
+
PasskeyTransactionHeaderOverrides,
|
|
11
13
|
ThruClient,
|
|
12
14
|
TransactionResult,
|
|
13
15
|
} from './types';
|
|
14
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Builds and signs a passkey-manager transaction without submitting it.
|
|
19
|
+
*
|
|
20
|
+
* Callers that override the transaction nonce are responsible for coordinating
|
|
21
|
+
* fee-payer nonce allocation before calling this helper. Use
|
|
22
|
+
* `submitPasskeyTransaction` for the serialized one-off submit path.
|
|
23
|
+
*/
|
|
24
|
+
export async function buildPasskeyTransaction(opts: {
|
|
25
|
+
client: ThruClient;
|
|
26
|
+
adminPublicKey: Uint8Array;
|
|
27
|
+
adminPrivateKey: Uint8Array;
|
|
28
|
+
walletAddress: string;
|
|
29
|
+
accountCtx: AccountContext;
|
|
30
|
+
invokeIx: Uint8Array;
|
|
31
|
+
header?: PasskeyTransactionHeaderOverrides;
|
|
32
|
+
} & PasskeySignaturePayload): Promise<BuiltPasskeyTransaction> {
|
|
33
|
+
const validateIx = encodeValidateInstruction({
|
|
34
|
+
walletAccountIdx: opts.accountCtx.walletAccountIdx,
|
|
35
|
+
authIdx: 0,
|
|
36
|
+
signatureR: hexToBytes(opts.signatureR),
|
|
37
|
+
signatureS: hexToBytes(opts.signatureS),
|
|
38
|
+
authenticatorData: Buffer.from(opts.authenticatorData, 'base64'),
|
|
39
|
+
clientDataJSON: Buffer.from(opts.clientDataJSON, 'base64'),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const instructionData = concatenateInstructions([validateIx, opts.invokeIx]);
|
|
43
|
+
const transaction = await opts.client.transactions.build({
|
|
44
|
+
feePayer: { publicKey: opts.adminPublicKey },
|
|
45
|
+
program: PASSKEY_MANAGER_PROGRAM_ADDRESS,
|
|
46
|
+
instructionData,
|
|
47
|
+
accounts: {
|
|
48
|
+
readWrite: opts.accountCtx.readWriteAddresses,
|
|
49
|
+
readOnly: opts.accountCtx.readOnlyAddresses,
|
|
50
|
+
},
|
|
51
|
+
header: {
|
|
52
|
+
fee: 0n,
|
|
53
|
+
...opts.header,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
await transaction.sign(opts.adminPrivateKey);
|
|
58
|
+
return {
|
|
59
|
+
transaction,
|
|
60
|
+
rawTransaction: transaction.toWire(),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
15
64
|
export async function submitPasskeyTransaction(opts: {
|
|
16
65
|
client: ThruClient;
|
|
17
66
|
adminPublicKey: Uint8Array;
|
|
18
|
-
adminPrivateKey:
|
|
67
|
+
adminPrivateKey: Uint8Array;
|
|
19
68
|
walletAddress: string;
|
|
20
69
|
accountCtx: AccountContext;
|
|
21
70
|
invokeIx: Uint8Array;
|
|
71
|
+
header?: PasskeyTransactionHeaderOverrides;
|
|
22
72
|
} & PasskeySignaturePayload): Promise<TransactionResult> {
|
|
23
73
|
return withSerializedFeePayer(opts.adminPublicKey, async () => {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
authIdx: 0,
|
|
27
|
-
signatureR: hexToBytes(opts.signatureR),
|
|
28
|
-
signatureS: hexToBytes(opts.signatureS),
|
|
29
|
-
authenticatorData: Buffer.from(opts.authenticatorData, 'base64'),
|
|
30
|
-
clientDataJSON: Buffer.from(opts.clientDataJSON, 'base64'),
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const instructionData = concatenateInstructions([validateIx, opts.invokeIx]);
|
|
34
|
-
const transaction = await opts.client.transactions.build({
|
|
35
|
-
feePayer: { publicKey: opts.adminPublicKey },
|
|
36
|
-
program: PASSKEY_MANAGER_PROGRAM_ADDRESS,
|
|
37
|
-
instructionData,
|
|
38
|
-
accounts: {
|
|
39
|
-
readWrite: opts.accountCtx.readWriteAddresses,
|
|
40
|
-
readOnly: opts.accountCtx.readOnlyAddresses,
|
|
41
|
-
},
|
|
42
|
-
header: { fee: 0n },
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
await transaction.sign(opts.adminPrivateKey);
|
|
46
|
-
const signature = await opts.client.transactions.send(transaction.toWire());
|
|
74
|
+
const { rawTransaction } = await buildPasskeyTransaction(opts);
|
|
75
|
+
const signature = await opts.client.transactions.send(rawTransaction);
|
|
47
76
|
return trackTransaction(opts.client, signature);
|
|
48
77
|
});
|
|
49
78
|
}
|
package/src/server/types.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import type { AccountContext } from '@thru/passkey-manager';
|
|
2
|
+
import type { TransactionHeaderConfig } from '@thru/thru-sdk';
|
|
3
|
+
|
|
4
|
+
export type PasskeyTransactionHeaderOverrides = TransactionHeaderConfig;
|
|
5
|
+
|
|
6
|
+
export interface BuiltPasskeyTransaction {
|
|
7
|
+
transaction: {
|
|
8
|
+
sign: (privateKey: Uint8Array) => Promise<unknown>;
|
|
9
|
+
toWire: () => Uint8Array;
|
|
10
|
+
};
|
|
11
|
+
rawTransaction: Uint8Array;
|
|
12
|
+
}
|
|
2
13
|
|
|
3
14
|
export interface ThruClient {
|
|
4
15
|
accounts: {
|
|
@@ -23,9 +34,9 @@ export interface ThruClient {
|
|
|
23
34
|
readWrite: string[];
|
|
24
35
|
readOnly: string[];
|
|
25
36
|
};
|
|
26
|
-
header
|
|
37
|
+
header?: TransactionHeaderConfig;
|
|
27
38
|
}) => Promise<{
|
|
28
|
-
sign: (privateKey:
|
|
39
|
+
sign: (privateKey: Uint8Array) => Promise<unknown>;
|
|
29
40
|
toWire: () => Uint8Array;
|
|
30
41
|
}>;
|
|
31
42
|
send: (transaction: Uint8Array) => Promise<string>;
|
package/src/server/utils.test.ts
CHANGED
|
@@ -32,4 +32,20 @@ describe('trackTransaction', () => {
|
|
|
32
32
|
status: 'finalized_without_execution',
|
|
33
33
|
});
|
|
34
34
|
});
|
|
35
|
+
|
|
36
|
+
it('preserves finalized status if the tracking stream errors afterward', async () => {
|
|
37
|
+
const client = {
|
|
38
|
+
transactions: {
|
|
39
|
+
track: async function* () {
|
|
40
|
+
yield { statusCode: 3 };
|
|
41
|
+
throw new Error('stream closed');
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
} as ThruClient;
|
|
45
|
+
|
|
46
|
+
await expect(trackTransaction(client, 'sig-2')).resolves.toEqual({
|
|
47
|
+
signature: 'sig-2',
|
|
48
|
+
status: 'finalized_without_execution',
|
|
49
|
+
});
|
|
50
|
+
});
|
|
35
51
|
});
|
package/src/server/utils.ts
CHANGED
|
@@ -48,9 +48,9 @@ export async function trackTransaction(
|
|
|
48
48
|
signature: string,
|
|
49
49
|
timeoutMs: number = 5000
|
|
50
50
|
): Promise<TransactionResult> {
|
|
51
|
-
|
|
52
|
-
let finalizedSeen = false;
|
|
51
|
+
let finalizedSeen = false;
|
|
53
52
|
|
|
53
|
+
try {
|
|
54
54
|
for await (const update of client.transactions.track(signature, { timeoutMs })) {
|
|
55
55
|
if (update.executionResult) {
|
|
56
56
|
const vmError =
|
|
@@ -84,6 +84,13 @@ export async function trackTransaction(
|
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
} catch {
|
|
87
|
+
if (finalizedSeen) {
|
|
88
|
+
return {
|
|
89
|
+
signature,
|
|
90
|
+
status: 'finalized_without_execution',
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
87
94
|
return {
|
|
88
95
|
signature,
|
|
89
96
|
status: 'timeout',
|