@thru/passkey 0.2.35 → 0.2.37

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.
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
2
  import type { AccountContext } from '@thru/programs/passkey-manager';
3
+ import { encodeSignature } from '@thru/sdk/helpers';
3
4
  import type { ThruClient } from './types';
4
5
 
5
6
  const passkeyManagerMocks = vi.hoisted(() => ({
@@ -29,9 +30,11 @@ const signaturePayload = {
29
30
  };
30
31
 
31
32
  function createClient() {
33
+ const signatureBytes = new Uint8Array(64).fill(7);
34
+ const rawTransaction = new Uint8Array(64).fill(9);
32
35
  const transaction = {
33
36
  sign: vi.fn(async () => {}),
34
- toWire: vi.fn(() => new Uint8Array([9, 9, 9])),
37
+ toWire: vi.fn(() => rawTransaction),
35
38
  };
36
39
  const client = {
37
40
  transactions: {
@@ -46,15 +49,26 @@ function createClient() {
46
49
  },
47
50
  };
48
51
  }),
52
+ sendAndTrack: vi.fn(async function* () {
53
+ yield {
54
+ status: 2,
55
+ signature: { value: signatureBytes },
56
+ executionResult: {
57
+ userErrorCode: 0n,
58
+ vmError: 0,
59
+ executionResult: 0n,
60
+ },
61
+ };
62
+ }),
49
63
  },
50
64
  } as unknown as ThruClient;
51
65
 
52
- return { client, transaction };
66
+ return { client, transaction, rawTransaction, signatureBytes };
53
67
  }
54
68
 
55
69
  describe('passkey submit', () => {
56
70
  it('builds and signs a passkey transaction with explicit header overrides', async () => {
57
- const { client, transaction } = createClient();
71
+ const { client, rawTransaction, transaction } = createClient();
58
72
 
59
73
  const result = await buildPasskeyTransaction({
60
74
  client,
@@ -95,11 +109,11 @@ describe('passkey submit', () => {
95
109
  })
96
110
  );
97
111
  expect(transaction.sign).toHaveBeenCalledWith(new Uint8Array(32).fill(2));
98
- expect(result.rawTransaction).toEqual(new Uint8Array([9, 9, 9]));
112
+ expect(result.rawTransaction).toEqual(rawTransaction);
99
113
  });
100
114
 
101
- it('keeps submitPasskeyTransaction as a send and track convenience wrapper', async () => {
102
- const { client } = createClient();
115
+ it('submits and tracks passkey transactions through one streaming RPC', async () => {
116
+ const { client, rawTransaction, signatureBytes } = createClient();
103
117
 
104
118
  await expect(submitPasskeyTransaction({
105
119
  client,
@@ -111,11 +125,15 @@ describe('passkey submit', () => {
111
125
  instructionData: new Uint8Array([3, 4]),
112
126
  ...signaturePayload,
113
127
  })).resolves.toEqual({
114
- signature: 'tx-signature',
128
+ signature: encodeSignature(signatureBytes),
115
129
  status: 'finalized',
116
130
  errorCode: 0n,
117
131
  });
118
132
 
119
- expect(client.transactions.send).toHaveBeenCalledWith(new Uint8Array([9, 9, 9]));
133
+ expect(client.transactions.sendAndTrack).toHaveBeenCalledWith(rawTransaction, {
134
+ timeoutMs: 5000,
135
+ });
136
+ expect(client.transactions.send).not.toHaveBeenCalled();
137
+ expect(client.transactions.track).not.toHaveBeenCalled();
120
138
  });
121
139
  });
@@ -5,7 +5,7 @@ import {
5
5
  hexToBytes,
6
6
  } from '@thru/programs/passkey-manager';
7
7
  import type { AccountContext } from '@thru/programs/passkey-manager';
8
- import { trackTransaction, withSerializedFeePayer } from './utils';
8
+ import { sendAndTrackTransaction, withSerializedFeePayer } from './utils';
9
9
  import type {
10
10
  BuiltPasskeyTransaction,
11
11
  PasskeySignaturePayload,
@@ -103,7 +103,6 @@ export async function submitPasskeyTransaction(opts: {
103
103
  } & PasskeySignaturePayload): Promise<TransactionResult> {
104
104
  return withSerializedFeePayer(opts.adminPublicKey, async () => {
105
105
  const { rawTransaction } = await buildPasskeyTransaction(opts);
106
- const signature = await opts.client.transactions.send(rawTransaction);
107
- return trackTransaction(opts.client, signature);
106
+ return sendAndTrackTransaction(opts.client, rawTransaction);
108
107
  });
109
108
  }
@@ -11,6 +11,13 @@ export interface BuiltPasskeyTransaction {
11
11
  rawTransaction: Uint8Array;
12
12
  }
13
13
 
14
+ export interface TransactionExecutionResultLike {
15
+ userErrorCode?: bigint | number | null;
16
+ vmError?: bigint | number | null;
17
+ executionResult?: bigint | number | null;
18
+ consumedComputeUnits?: number;
19
+ }
20
+
14
21
  export interface ThruClient {
15
22
  accounts: {
16
23
  get: (address: string) => Promise<{ data?: { data?: Uint8Array } }>;
@@ -44,13 +51,18 @@ export interface ThruClient {
44
51
  signature: string,
45
52
  opts: { timeoutMs: number }
46
53
  ) => AsyncIterable<{
47
- executionResult?: {
48
- userErrorCode: bigint;
49
- vmError?: bigint | number | null;
50
- executionResult?: bigint | number | null;
51
- };
54
+ executionResult?: TransactionExecutionResultLike;
52
55
  statusCode?: number;
53
56
  }>;
57
+ sendAndTrack: (
58
+ transaction: Uint8Array,
59
+ opts: { timeoutMs: number }
60
+ ) => AsyncIterable<{
61
+ status?: number;
62
+ signature?: { value: Uint8Array };
63
+ consensusStatus?: number;
64
+ executionResult?: TransactionExecutionResultLike;
65
+ }>;
54
66
  };
55
67
  }
56
68
 
@@ -1,6 +1,7 @@
1
1
  import { beforeEach, describe, expect, it } from 'vitest';
2
+ import { encodeSignature } from '@thru/sdk/helpers';
2
3
  import type { ThruClient } from './types';
3
- import { trackTransaction } from './utils';
4
+ import { sendAndTrackTransaction, trackTransaction } from './utils';
4
5
 
5
6
  const feePayerQueueSymbol = Symbol.for('thru.sharedFeePayerQueues');
6
7
 
@@ -49,3 +50,76 @@ describe('trackTransaction', () => {
49
50
  });
50
51
  });
51
52
  });
53
+
54
+ describe('sendAndTrackTransaction', () => {
55
+ beforeEach(() => {
56
+ clearFeePayerQueues();
57
+ });
58
+
59
+ const rawTransaction = new Uint8Array(64).fill(9);
60
+
61
+ it('returns execution results from the send-and-track stream', async () => {
62
+ const signatureBytes = new Uint8Array(64).fill(3);
63
+ const client = {
64
+ transactions: {
65
+ sendAndTrack: async function* () {
66
+ yield {
67
+ status: 2,
68
+ signature: { value: signatureBytes },
69
+ executionResult: {
70
+ userErrorCode: 0n,
71
+ vmError: 0,
72
+ executionResult: 0n,
73
+ },
74
+ };
75
+ },
76
+ },
77
+ } as ThruClient;
78
+
79
+ await expect(
80
+ sendAndTrackTransaction(client, rawTransaction)
81
+ ).resolves.toEqual({
82
+ signature: encodeSignature(signatureBytes),
83
+ status: 'finalized',
84
+ errorCode: 0n,
85
+ });
86
+ });
87
+
88
+ it('preserves an accepted transaction as a timeout if the stream closes early', async () => {
89
+ const signatureBytes = new Uint8Array(64).fill(4);
90
+ const client = {
91
+ transactions: {
92
+ sendAndTrack: async function* () {
93
+ yield {
94
+ status: 2,
95
+ signature: { value: signatureBytes },
96
+ };
97
+ throw new Error('stream closed');
98
+ },
99
+ },
100
+ } as ThruClient;
101
+
102
+ await expect(
103
+ sendAndTrackTransaction(client, rawTransaction)
104
+ ).resolves.toEqual({
105
+ signature: encodeSignature(signatureBytes),
106
+ status: 'timeout',
107
+ });
108
+ });
109
+
110
+ it('rejects malformed raw transactions before submitting them', async () => {
111
+ const client = {
112
+ transactions: {
113
+ sendAndTrack: async function* () {
114
+ yield {
115
+ status: 2,
116
+ };
117
+ },
118
+ },
119
+ } as ThruClient;
120
+
121
+ await expect(
122
+ sendAndTrackTransaction(client, new Uint8Array([1, 2, 3]))
123
+ ).rejects.toThrow('Raw transaction too short to contain a signature: 3 bytes');
124
+ });
125
+ });
@@ -1,7 +1,14 @@
1
- import { encodeAddress } from '@thru/sdk/helpers';
2
- import type { ThruClient, TransactionResult } from './types';
1
+ import { encodeAddress, encodeSignature } from '@thru/sdk/helpers';
2
+ import type {
3
+ ThruClient,
4
+ TransactionExecutionResultLike,
5
+ TransactionResult,
6
+ } from './types';
3
7
 
4
8
  const feePayerQueueSymbol = Symbol.for('thru.sharedFeePayerQueues');
9
+ const SUBMISSION_STATUS_ACCEPTED = 2;
10
+ const CONSENSUS_STATUS_FINALIZED = 3;
11
+ const CONSENSUS_STATUS_CLUSTER_EXECUTED = 5;
5
12
 
6
13
  function getFeePayerQueues(): Map<string, Promise<void>> {
7
14
  const globalQueues = globalThis as typeof globalThis & {
@@ -53,26 +60,10 @@ export async function trackTransaction(
53
60
  try {
54
61
  for await (const update of client.transactions.track(signature, { timeoutMs })) {
55
62
  if (update.executionResult) {
56
- const vmError =
57
- update.executionResult.vmError !== undefined && update.executionResult.vmError !== null
58
- ? BigInt(update.executionResult.vmError)
59
- : 0n;
60
- const userErrorCode = update.executionResult.userErrorCode;
61
- const executionError =
62
- update.executionResult.executionResult !== undefined &&
63
- update.executionResult.executionResult !== null
64
- ? BigInt(update.executionResult.executionResult)
65
- : 0n;
66
- const success = vmError === 0n && executionError === 0n && userErrorCode === 0n;
67
-
68
- return {
69
- signature,
70
- status: success ? 'finalized' : 'failed',
71
- errorCode: vmError !== 0n ? vmError : executionError !== 0n ? executionError : userErrorCode,
72
- };
63
+ return executionResultToTransactionResult(signature, update.executionResult);
73
64
  }
74
65
 
75
- if (update.statusCode === 3) {
66
+ if (isFinalConsensusStatus(update.statusCode)) {
76
67
  finalizedSeen = true;
77
68
  }
78
69
  }
@@ -103,10 +94,106 @@ export async function trackTransaction(
103
94
  };
104
95
  }
105
96
 
97
+ export async function sendAndTrackTransaction(
98
+ client: ThruClient,
99
+ rawTransaction: Uint8Array,
100
+ timeoutMs: number = 5000
101
+ ): Promise<TransactionResult> {
102
+ let signature = signatureFromRawTransaction(rawTransaction);
103
+ let accepted = false;
104
+ let finalizedSeen = false;
105
+
106
+ try {
107
+ for await (const update of client.transactions.sendAndTrack(rawTransaction, { timeoutMs })) {
108
+ if (update.signature?.value) {
109
+ signature = encodeSignature(update.signature.value);
110
+ }
111
+ if (update.status === SUBMISSION_STATUS_ACCEPTED) {
112
+ accepted = true;
113
+ }
114
+ if (isFinalConsensusStatus(update.consensusStatus)) {
115
+ finalizedSeen = true;
116
+ }
117
+ if (update.executionResult) {
118
+ return executionResultToTransactionResult(signature, update.executionResult);
119
+ }
120
+ }
121
+ } catch (error) {
122
+ if (finalizedSeen && signature) {
123
+ return {
124
+ signature,
125
+ status: 'finalized_without_execution',
126
+ };
127
+ }
128
+
129
+ if (accepted && signature) {
130
+ return {
131
+ signature,
132
+ status: 'timeout',
133
+ };
134
+ }
135
+
136
+ throw error;
137
+ }
138
+
139
+ if (finalizedSeen && signature) {
140
+ return {
141
+ signature,
142
+ status: 'finalized_without_execution',
143
+ };
144
+ }
145
+
146
+ if (accepted && signature) {
147
+ return {
148
+ signature,
149
+ status: 'timeout',
150
+ };
151
+ }
152
+
153
+ throw new Error('SendAndTrackTxn did not accept the transaction');
154
+ }
155
+
106
156
  export function toThruAddress(bytes: Uint8Array): string {
107
157
  return encodeAddress(bytes);
108
158
  }
109
159
 
160
+ function executionResultToTransactionResult(
161
+ signature: string,
162
+ executionResult: TransactionExecutionResultLike
163
+ ): TransactionResult {
164
+ const vmError =
165
+ executionResult.vmError !== undefined && executionResult.vmError !== null
166
+ ? BigInt(executionResult.vmError)
167
+ : 0n;
168
+ const userErrorCode =
169
+ executionResult.userErrorCode !== undefined && executionResult.userErrorCode !== null
170
+ ? BigInt(executionResult.userErrorCode)
171
+ : 0n;
172
+ const executionError =
173
+ executionResult.executionResult !== undefined &&
174
+ executionResult.executionResult !== null
175
+ ? BigInt(executionResult.executionResult)
176
+ : 0n;
177
+ const success = vmError === 0n && executionError === 0n && userErrorCode === 0n;
178
+
179
+ return {
180
+ signature,
181
+ status: success ? 'finalized' : 'failed',
182
+ errorCode: vmError !== 0n ? vmError : executionError !== 0n ? executionError : userErrorCode,
183
+ };
184
+ }
185
+
186
+ function isFinalConsensusStatus(status?: number): boolean {
187
+ return status === CONSENSUS_STATUS_FINALIZED || status === CONSENSUS_STATUS_CLUSTER_EXECUTED;
188
+ }
189
+
190
+ function signatureFromRawTransaction(rawTransaction: Uint8Array): string {
191
+ if (rawTransaction.length < 64) {
192
+ throw new Error(`Raw transaction too short to contain a signature: ${rawTransaction.length} bytes`);
193
+ }
194
+ return encodeSignature(rawTransaction.slice(rawTransaction.length - 64));
195
+ }
196
+
110
197
  export async function withSerializedFeePayer<T>(
111
198
  feePayerPublicKey: Uint8Array,
112
199
  work: () => Promise<T>
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/auth/add-device.ts"],"sourcesContent":["/* Add-passkey-to-account transaction builder, lifted from\n `web/wallet-auth-manager/app/page.tsx` (`runValidateThen`,\n `handleSubmitAddPasskey`) so the wallet's `/embedded` post-connect\n step can reuse the exact same flow.\n\n Builds the on-chain transaction:\n VALIDATE(existingAuthority -> ADD_AUTHORITY(newPasskey))\n or VALIDATE(existingAuthority -> multicall[ADD_AUTHORITY, REGISTER_CREDENTIAL])\n asks the caller's existing passkey to sign the challenge, then asks\n the caller's wallet signer to sign the assembled transaction, sends\n it, and returns the result. */\n\nimport {\n type Authority,\n type AuthorityRecord,\n buildAccountContext,\n createAuthorityRecord,\n createCredentialLookupSeed,\n createValidateChallenge,\n decodeAddress,\n deriveWalletAddress,\n encodeAddAuthorityInstruction,\n encodeLegacyAddAuthorityInstruction,\n encodeRegisterCredentialInstruction,\n encodeValidateInstruction,\n parseWalletAuthorities,\n type ParsedAuthority,\n type WalletSigner,\n} from \"@thru/programs/passkey-manager\";\nimport {\n MULTICALL_PROGRAM_PUBKEY,\n buildMulticallInstruction,\n} from \"@thru/programs/multicall\";\n\n/** Minimal shape required from a passkey signer. Both web's\n `signWithDiscoverablePasskey`/`signWithPasskey` and mobile's\n counterparts conform. */\nexport interface PasskeyChallengeSigner {\n signChallenge: (challenge: Uint8Array) => Promise<{\n signatureR: Uint8Array;\n signatureS: Uint8Array;\n authenticatorData: Uint8Array;\n clientDataJSON: Uint8Array;\n }>;\n}\n\n/** Minimal shape required from a Thru chain client. Loosely-typed\n because @thru/sdk's DTS emit is currently broken in this\n repo. The caller passes the real Thru and we narrow operationally. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyThruClient = any;\n\nexport interface AddDeviceParams {\n /** Loosely-typed Thru chain client (`@thru/sdk/client`). */\n thru: AnyThruClient;\n /** Wallet (the on-chain WalletAccount) to attach the passkey to. */\n walletAddress: string;\n /** Index of the existing authority that approves this change. Must\n currently be a passkey authority. */\n authIdx: number;\n /** New passkey to attach. tag = 1 (passkey). */\n newAuthority: Authority;\n newAuthorityRecord?: AuthorityRecord;\n /** Optional credential-lookup registration so the new passkey is\n discoverable on subsequent sign-ins. */\n credentialId?: Uint8Array;\n walletName?: string;\n /** Existing-passkey challenge signer (web or mobile). */\n passkey: PasskeyChallengeSigner;\n /** Wallet transaction signer that returns base64(signed bytes). */\n walletSigner: WalletSigner;\n /** Passkey program address (base58). */\n programAddress: string;\n /** Sign-and-send executor (lifted from passkey-transaction.ts in the\n wallet-auth-manager - wallet apps own this because it depends on\n the `Thru` client's transaction builder). */\n executor: TxExecutor;\n /** Optional status callback so UIs can show progress. */\n onStatus?: (message: string) => void;\n}\n\nexport interface TxExecutorParams {\n thru: AnyThruClient;\n walletSigner: WalletSigner;\n instructionData: Uint8Array;\n readWriteAddresses: string[];\n readOnlyAddresses: string[];\n label: string;\n}\n\nexport interface TxExecutorResult {\n signature: string;\n /** Loosely-typed because @thru/sdk types aren't available. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n execution: any;\n}\n\nexport type TxExecutor = (\n params: TxExecutorParams,\n) => Promise<TxExecutorResult>;\n\nexport interface AddDeviceResult extends TxExecutorResult {\n /** The new passkey's authority index after the transaction lands. */\n newAuthorityIdx: number;\n}\n\nexport interface AddAuthorityParams\n extends Omit<AddDeviceParams, \"newAuthority\" | \"newAuthorityRecord\" | \"credentialId\" | \"walletName\"> {\n /** Complete authority record to append. */\n authorityRecord: AuthorityRecord;\n}\n\n/**\n * Run VALIDATE + ADD_AUTHORITY [+ REGISTER_CREDENTIAL] to attach a new\n * passkey to an on-chain WalletAccount.\n */\nexport async function addDeviceToAccount(\n params: AddDeviceParams,\n): Promise<AddDeviceResult> {\n return addAuthorityToAccount({\n ...params,\n authorityRecord:\n params.newAuthorityRecord ?? createAuthorityRecord(params.newAuthority),\n });\n}\n\n/**\n * Run VALIDATE + ADD_AUTHORITY to attach a complete authority record to an\n * on-chain WalletAccount.\n */\nexport async function addAuthorityToAccount(\n params: AddAuthorityParams & Pick<AddDeviceParams, \"credentialId\" | \"walletName\">,\n): Promise<AddDeviceResult> {\n const status = params.onStatus ?? (() => {});\n\n const walletAccount = await params.thru.accounts.get(params.walletAddress);\n const walletData: Uint8Array | undefined = walletAccount?.data?.data;\n if (!walletData) {\n throw new Error(\"Wallet account data missing\");\n }\n\n const parsed = parseWalletAuthorities(walletData);\n const authorizing: ParsedAuthority | undefined =\n parsed.authorities[params.authIdx];\n if (!authorizing) {\n throw new Error(\"Authorization index out of bounds\");\n }\n if (authorizing.kind !== \"passkey\") {\n throw new Error(\n \"addDeviceToAccount currently requires a passkey authority for VALIDATE\",\n );\n }\n\n /* The new authority will land at the next free slot. */\n const newAuthorityIdx = parsed.authorities.length;\n\n let readWriteAccounts: Uint8Array[] = [];\n let lookupSeed: Uint8Array | undefined;\n let lookupAddressBytes: Uint8Array | undefined;\n let lookupProof: Uint8Array | undefined;\n\n if (params.credentialId) {\n lookupSeed = await createCredentialLookupSeed(params.credentialId);\n lookupAddressBytes = await deriveWalletAddress(\n lookupSeed,\n params.programAddress,\n );\n\n status(\"Fetching state proof for credential lookup...\");\n const proofResult = await params.thru.proofs.generate({\n proofType: 1 /* StateProofType.CREATING */,\n address: lookupAddressBytes,\n });\n lookupProof = proofResult.proof;\n readWriteAccounts = [lookupAddressBytes];\n }\n\n const ctx = buildAccountContext({\n walletAddress: params.walletAddress,\n readWriteAccounts,\n readOnlyAccounts: params.credentialId ? [MULTICALL_PROGRAM_PUBKEY] : [],\n programAddress: params.programAddress,\n });\n\n const passkeyProgramPubkey = decodeAddress(params.programAddress);\n const encodeAuthorityInstruction =\n parsed.layout === \"legacyAuthority\"\n ? encodeLegacyAddAuthorityInstruction\n : encodeAddAuthorityInstruction;\n const addAuthorityInstruction = encodeAuthorityInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n authorityRecord: params.authorityRecord,\n });\n\n let targetProgramIdx = ctx.getAccountIndex(passkeyProgramPubkey);\n let targetInstructionData = addAuthorityInstruction;\n\n if (params.credentialId) {\n if (!lookupSeed || !lookupAddressBytes || !lookupProof) {\n throw new Error(\"Credential lookup proof data missing\");\n }\n\n const registerCredentialInstruction = encodeRegisterCredentialInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n lookupAccountIdx: ctx.getAccountIndex(lookupAddressBytes),\n seed: lookupSeed,\n stateProof: lookupProof,\n });\n\n targetProgramIdx = ctx.getAccountIndex(MULTICALL_PROGRAM_PUBKEY);\n targetInstructionData = buildMulticallInstruction([\n {\n programIdx: ctx.getAccountIndex(passkeyProgramPubkey),\n instructionData: addAuthorityInstruction,\n },\n {\n programIdx: ctx.getAccountIndex(passkeyProgramPubkey),\n instructionData: registerCredentialInstruction,\n },\n ]);\n }\n\n /* Build the VALIDATE challenge over the target CPI and ask the caller's passkey to sign. */\n const challenge = await createValidateChallenge(\n parsed.nonce,\n ctx.accountAddresses,\n ctx.walletAccountIdx,\n params.authIdx,\n {\n programIdx: targetProgramIdx,\n instructionData: targetInstructionData,\n },\n );\n\n status(\"Waiting for passkey approval...\");\n const signature = await params.passkey.signChallenge(challenge);\n\n const validateInstruction = encodeValidateInstruction({\n walletAccountIdx: ctx.walletAccountIdx,\n authIdx: params.authIdx,\n targetInstruction: {\n programIdx: targetProgramIdx,\n instructionData: targetInstructionData,\n },\n signatureR: signature.signatureR,\n signatureS: signature.signatureS,\n authenticatorData: signature.authenticatorData,\n clientDataJSON: signature.clientDataJSON,\n });\n\n status(\"Sending transaction...\");\n const result = await params.executor({\n thru: params.thru,\n walletSigner: params.walletSigner,\n instructionData: validateInstruction,\n readWriteAddresses: ctx.readWriteAddresses,\n readOnlyAddresses: ctx.readOnlyAddresses,\n label: params.credentialId\n ? \"VALIDATE -> MULTICALL(ADD_AUTHORITY, REGISTER_CREDENTIAL)\"\n : \"VALIDATE -> ADD_AUTHORITY\",\n });\n\n return { ...result, newAuthorityIdx };\n}\n"],"mappings":"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","names":[]}