accounts 0.12.2 → 0.14.0

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.
Files changed (128) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +6 -5
  3. package/dist/cli/adapter.d.ts.map +1 -1
  4. package/dist/cli/adapter.js +76 -69
  5. package/dist/cli/adapter.js.map +1 -1
  6. package/dist/core/AccessKey.d.ts +89 -106
  7. package/dist/core/AccessKey.d.ts.map +1 -1
  8. package/dist/core/AccessKey.js +230 -210
  9. package/dist/core/AccessKey.js.map +1 -1
  10. package/dist/core/Account.d.ts +0 -9
  11. package/dist/core/Account.d.ts.map +1 -1
  12. package/dist/core/Account.js +1 -13
  13. package/dist/core/Account.js.map +1 -1
  14. package/dist/core/Adapter.d.ts +24 -4
  15. package/dist/core/Adapter.d.ts.map +1 -1
  16. package/dist/core/Client.d.ts +1 -1
  17. package/dist/core/Client.d.ts.map +1 -1
  18. package/dist/core/Client.js.map +1 -1
  19. package/dist/core/Provider.d.ts +6 -7
  20. package/dist/core/Provider.d.ts.map +1 -1
  21. package/dist/core/Provider.js +73 -77
  22. package/dist/core/Provider.js.map +1 -1
  23. package/dist/core/Remote.d.ts +2 -0
  24. package/dist/core/Remote.d.ts.map +1 -1
  25. package/dist/core/Remote.js +3 -1
  26. package/dist/core/Remote.js.map +1 -1
  27. package/dist/core/Schema.d.ts +17 -7
  28. package/dist/core/Schema.d.ts.map +1 -1
  29. package/dist/core/adapters/dialog.d.ts.map +1 -1
  30. package/dist/core/adapters/dialog.js +126 -131
  31. package/dist/core/adapters/dialog.js.map +1 -1
  32. package/dist/core/adapters/local.d.ts.map +1 -1
  33. package/dist/core/adapters/local.js +92 -105
  34. package/dist/core/adapters/local.js.map +1 -1
  35. package/dist/core/adapters/privy.d.ts +164 -0
  36. package/dist/core/adapters/privy.d.ts.map +1 -0
  37. package/dist/core/adapters/privy.js +578 -0
  38. package/dist/core/adapters/privy.js.map +1 -0
  39. package/dist/core/adapters/turnkey.d.ts.map +1 -1
  40. package/dist/core/adapters/turnkey.js +136 -133
  41. package/dist/core/adapters/turnkey.js.map +1 -1
  42. package/dist/core/internal/AccessKeyTransaction.d.ts +59 -0
  43. package/dist/core/internal/AccessKeyTransaction.d.ts.map +1 -0
  44. package/dist/core/internal/AccessKeyTransaction.js +155 -0
  45. package/dist/core/internal/AccessKeyTransaction.js.map +1 -0
  46. package/dist/core/zod/rpc.d.ts +71 -4
  47. package/dist/core/zod/rpc.d.ts.map +1 -1
  48. package/dist/core/zod/rpc.js +33 -4
  49. package/dist/core/zod/rpc.js.map +1 -1
  50. package/dist/index.d.ts +1 -1
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +1 -1
  53. package/dist/index.js.map +1 -1
  54. package/dist/react-native/adapter.d.ts.map +1 -1
  55. package/dist/react-native/adapter.js +86 -75
  56. package/dist/react-native/adapter.js.map +1 -1
  57. package/dist/server/CliAuth.d.ts +55 -0
  58. package/dist/server/CliAuth.d.ts.map +1 -1
  59. package/dist/server/CliAuth.js +18 -1
  60. package/dist/server/CliAuth.js.map +1 -1
  61. package/dist/server/Kv.d.ts +19 -9
  62. package/dist/server/Kv.d.ts.map +1 -1
  63. package/dist/server/Kv.js +32 -9
  64. package/dist/server/Kv.js.map +1 -1
  65. package/dist/server/internal/handlers/auth.d.ts.map +1 -1
  66. package/dist/server/internal/handlers/auth.js +3 -1
  67. package/dist/server/internal/handlers/auth.js.map +1 -1
  68. package/dist/server/internal/handlers/codeAuth.js +1 -1
  69. package/dist/server/internal/handlers/codeAuth.js.map +1 -1
  70. package/dist/server/internal/handlers/exchange.js +1 -1
  71. package/dist/server/internal/handlers/exchange.js.map +1 -1
  72. package/dist/server/internal/handlers/relay.d.ts +18 -12
  73. package/dist/server/internal/handlers/relay.d.ts.map +1 -1
  74. package/dist/server/internal/handlers/relay.js +116 -3
  75. package/dist/server/internal/handlers/relay.js.map +1 -1
  76. package/dist/server/internal/handlers/webAuthn.d.ts +6 -1
  77. package/dist/server/internal/handlers/webAuthn.d.ts.map +1 -1
  78. package/dist/server/internal/handlers/webAuthn.js +16 -9
  79. package/dist/server/internal/handlers/webAuthn.js.map +1 -1
  80. package/package.json +3 -3
  81. package/src/cli/{Provider.test.ts → Provider.localnet.test.ts} +49 -1
  82. package/src/cli/adapter.ts +95 -76
  83. package/src/core/AccessKey.test.ts +563 -560
  84. package/src/core/AccessKey.ts +327 -327
  85. package/src/core/Account.test.ts +6 -51
  86. package/src/core/Account.ts +1 -19
  87. package/src/core/Adapter.ts +22 -4
  88. package/src/core/Client.test.ts +1 -1
  89. package/src/core/Client.ts +1 -1
  90. package/src/core/Provider.connect.browser.test.ts +1 -1
  91. package/src/core/{Provider.test.ts → Provider.localnet.test.ts} +98 -32
  92. package/src/core/Provider.ts +118 -112
  93. package/src/core/Remote.test.ts +28 -0
  94. package/src/core/Remote.ts +4 -1
  95. package/src/core/Schema.test-d.ts +41 -0
  96. package/src/core/adapters/dialog.test.ts +335 -15
  97. package/src/core/adapters/dialog.ts +120 -127
  98. package/src/core/adapters/local.test.ts +1 -1
  99. package/src/core/adapters/local.ts +95 -119
  100. package/src/core/adapters/privy.test-d.ts +128 -0
  101. package/src/core/adapters/privy.test.ts +648 -0
  102. package/src/core/adapters/privy.ts +811 -0
  103. package/src/core/adapters/turnkey.test.ts +117 -13
  104. package/src/core/adapters/turnkey.ts +132 -149
  105. package/src/core/internal/AccessKeyTransaction.ts +243 -0
  106. package/src/core/{mppx.test.ts → mppx.localnet.test.ts} +32 -2
  107. package/src/core/zod/request.test.ts +36 -0
  108. package/src/core/zod/rpc.test.ts +139 -0
  109. package/src/core/zod/rpc.ts +44 -10
  110. package/src/index.ts +1 -1
  111. package/src/react-native/{Provider.test.ts → Provider.localnet.test.ts} +65 -0
  112. package/src/react-native/adapter.ts +107 -87
  113. package/src/server/CliAuth.test-d.ts +23 -0
  114. package/src/server/CliAuth.test.ts +47 -0
  115. package/src/server/CliAuth.ts +20 -1
  116. package/src/server/Handler.test-d.ts +1 -1
  117. package/src/server/Kv.test.ts +51 -0
  118. package/src/server/Kv.ts +43 -9
  119. package/src/server/internal/handlers/auth.test.ts +10 -5
  120. package/src/server/internal/handlers/auth.ts +4 -1
  121. package/src/server/internal/handlers/codeAuth.ts +1 -1
  122. package/src/server/internal/handlers/exchange.ts +1 -1
  123. package/src/server/internal/handlers/{relay.test.ts → relay.localnet.test.ts} +4 -1
  124. package/src/server/internal/handlers/relay.ts +139 -14
  125. package/src/server/internal/handlers/session.test.ts +1 -1
  126. package/src/server/internal/handlers/webAuthn.test.ts +20 -0
  127. package/src/server/internal/handlers/webAuthn.ts +24 -12
  128. /package/src/server/internal/handlers/{exchange.test.ts → exchange.localnet.test.ts} +0 -0
@@ -1,12 +1,11 @@
1
- import { Address, Provider as ox_Provider, RpcRequest as ox_RpcRequest } from 'ox'
1
+ import { Address, Provider as ox_Provider, RpcRequest as ox_RpcRequest, RpcResponse } from 'ox'
2
2
  import { KeyAuthorization } from 'ox/tempo'
3
- import { prepareTransactionRequest } from 'viem/actions'
4
- import { Account as TempoAccount } from 'viem/tempo'
5
3
  import { z } from 'zod/mini'
6
4
 
7
5
  import * as AccessKey from '../AccessKey.js'
8
6
  import * as Adapter from '../Adapter.js'
9
7
  import * as Dialog from '../Dialog.js'
8
+ import * as AccessKeyTransaction from '../internal/AccessKeyTransaction.js'
10
9
  import * as Schema from '../Schema.js'
11
10
  import type * as Store from '../Store.js'
12
11
  import * as Rpc from '../zod/rpc.js'
@@ -115,11 +114,16 @@ export function dialog(options: dialog.Options = {}): Adapter.Adapter {
115
114
  async function generateAccessKey(options: Adapter.authorizeAccessKey.Parameters | undefined) {
116
115
  if (!options) return undefined
117
116
  if (options.publicKey || options.address) return undefined
117
+ if (options.keyType && options.keyType !== 'p256')
118
+ throw new RpcResponse.InvalidParamsError({
119
+ message: `\`keyType: "${options.keyType}"\` requires externally generated key material; provide \`publicKey\` or \`address\`.`,
120
+ })
118
121
 
119
- const { accessKey, keyPair } = await AccessKey.generate()
122
+ const generated = await AccessKey.generate()
123
+ const { accessKey } = generated
120
124
  return {
121
125
  accessKey,
122
- keyPair,
126
+ generated,
123
127
  request: {
124
128
  ...options,
125
129
  publicKey: accessKey.publicKey,
@@ -135,44 +139,15 @@ export function dialog(options: dialog.Options = {}): Adapter.Adapter {
135
139
  function saveAccessKey(
136
140
  address: Address.Address,
137
141
  keyAuth: KeyAuthorization.Rpc,
138
- keyPair: AccessKey.generate.ReturnType['keyPair'],
142
+ generated: Awaited<ReturnType<typeof AccessKey.generate>>,
139
143
  ) {
140
144
  const keyAuthorization = KeyAuthorization.fromRpc(keyAuth)
141
- AccessKey.save({ address, keyAuthorization, keyPair, store })
142
- }
143
-
144
- /**
145
- * Tries to execute `fn` with the local access key. Returns `undefined`
146
- * when no access key exists so the caller can fall through to the dialog.
147
- * On stale-key errors, removes the key and also returns `undefined`.
148
- * On recoverable transaction errors, keeps the key and falls through to
149
- * the dialog so the user can fund, approve, or retry.
150
- */
151
- async function withAccessKey<result>(
152
- options: Pick<Adapter.sendTransaction.Parameters, 'calls' | 'chainId' | 'from'>,
153
- fn: (
154
- account: TempoAccount.Account,
155
- keyAuthorization?: KeyAuthorization.Signed,
156
- ) => Promise<result>,
157
- ): Promise<{ account: TempoAccount.Account; result: result } | undefined> {
158
- if (!options.from || typeof options.chainId === 'undefined') return undefined
159
- const account = AccessKey.selectAccount({
160
- address: options.from,
161
- calls: options.calls,
162
- chainId: options.chainId,
145
+ AccessKey.add({
146
+ account: address,
147
+ authorization: keyAuthorization,
148
+ keyPair: generated.keyPair,
163
149
  store,
164
150
  })
165
- if (!account) return undefined
166
- const keyAuthorization = AccessKey.getPending(account, { store })
167
- try {
168
- const result = await fn(account, keyAuthorization ?? undefined)
169
- return { account, result }
170
- } catch (err) {
171
- if (AccessKey.invalidate(account, err, { store }))
172
- console.warn('[accounts] access key invalidated, falling through to dialog:', err)
173
- else console.warn('[accounts] access key sign failed, falling through to dialog:', err)
174
- return undefined
175
- }
176
151
  }
177
152
 
178
153
  const dialogInstance = dialog({ host, store, theme })
@@ -224,20 +199,18 @@ export function dialog(options: dialog.Options = {}): Adapter.Adapter {
224
199
  })
225
200
 
226
201
  const address = accounts[0]?.address
227
- const keyAuthorization = accounts[0]?.capabilities.keyAuthorization
202
+ const capabilities = accounts[0]?.capabilities
203
+ const keyAuthorization = capabilities?.keyAuthorization
228
204
 
229
205
  if (accessKey && address && keyAuthorization)
230
- saveAccessKey(address, keyAuthorization, accessKey.keyPair)
206
+ saveAccessKey(address, keyAuthorization, accessKey.generated)
231
207
 
232
208
  return {
233
209
  accounts: accounts.map((a) => ({ address: a.address })),
210
+ ...(capabilities?.auth ? { auth: capabilities.auth } : {}),
234
211
  ...(keyAuthorization ? { keyAuthorization } : {}),
235
- ...(accounts[0]?.capabilities.signature
236
- ? { signature: accounts[0].capabilities.signature }
237
- : {}),
238
- ...(accounts[0]?.capabilities.personalSign
239
- ? { personalSign: accounts[0].capabilities.personalSign }
240
- : {}),
212
+ ...(capabilities?.signature ? { signature: capabilities.signature } : {}),
213
+ ...(capabilities?.personalSign ? { personalSign: capabilities.personalSign } : {}),
241
214
  }
242
215
  },
243
216
 
@@ -265,20 +238,18 @@ export function dialog(options: dialog.Options = {}): Adapter.Adapter {
265
238
  })
266
239
 
267
240
  const address = accounts[0]?.address
268
- const keyAuthorization = accounts[0]?.capabilities.keyAuthorization
241
+ const capabilities = accounts[0]?.capabilities
242
+ const keyAuthorization = capabilities?.keyAuthorization
269
243
 
270
244
  if (accessKey && address && keyAuthorization)
271
- saveAccessKey(address, keyAuthorization, accessKey.keyPair)
245
+ saveAccessKey(address, keyAuthorization, accessKey.generated)
272
246
 
273
247
  return {
274
248
  accounts: accounts.map((a) => ({ address: a.address })),
249
+ ...(capabilities?.auth ? { auth: capabilities.auth } : {}),
275
250
  ...(keyAuthorization ? { keyAuthorization } : {}),
276
- ...(accounts[0]?.capabilities.signature
277
- ? { signature: accounts[0].capabilities.signature }
278
- : {}),
279
- ...(accounts[0]?.capabilities.personalSign
280
- ? { personalSign: accounts[0].capabilities.personalSign }
281
- : {}),
251
+ ...(capabilities?.signature ? { signature: capabilities.signature } : {}),
252
+ ...(capabilities?.personalSign ? { personalSign: capabilities.personalSign } : {}),
282
253
  }
283
254
  },
284
255
 
@@ -287,26 +258,36 @@ export function dialog(options: dialog.Options = {}): Adapter.Adapter {
287
258
  },
288
259
 
289
260
  async signTransaction(parameters, request) {
290
- const result = await withAccessKey(parameters, async (account, keyAuthorization) => {
291
- const { feePayer, ...rest } = parameters
292
- const client = getClient({
293
- chainId: parameters.chainId,
294
- feePayer: (() => {
295
- if (feePayer === false) return false
296
- if (typeof feePayer === 'string') return feePayer
297
- return undefined
298
- })(),
299
- })
300
- const prepared = await prepareTransactionRequest(client, {
301
- account,
302
- ...rest,
303
- ...(typeof feePayer !== 'undefined' ? { feePayer: !!feePayer as never } : {}),
304
- keyAuthorization,
305
- type: 'tempo',
306
- })
307
- return await account.signTransaction(prepared as never)
261
+ const { feePayer, ...rest } = parameters
262
+ const client = getClient({
263
+ chainId: parameters.chainId,
264
+ feePayer: (() => {
265
+ if (feePayer === false) return false
266
+ if (typeof feePayer === 'string') return feePayer
267
+ return undefined
268
+ })(),
308
269
  })
309
- if (result !== undefined) return result.result
270
+ const transaction =
271
+ parameters.from && typeof parameters.chainId !== 'undefined'
272
+ ? await AccessKeyTransaction.create({
273
+ address: parameters.from,
274
+ calls: parameters.calls,
275
+ chainId: parameters.chainId,
276
+ client,
277
+ store,
278
+ })
279
+ : undefined
280
+ if (transaction) {
281
+ try {
282
+ const prepared = await transaction.prepare({
283
+ ...rest,
284
+ ...(typeof feePayer !== 'undefined' ? { feePayer: !!feePayer as never } : {}),
285
+ })
286
+ return await prepared.sign()
287
+ } catch (error) {
288
+ console.warn('[accounts] access key sign failed, falling through to dialog:', error)
289
+ }
290
+ }
310
291
  return await provider.request({
311
292
  ...request,
312
293
  params: [z.encode(Rpc.transactionRequest, parameters)] as const,
@@ -318,32 +299,35 @@ export function dialog(options: dialog.Options = {}): Adapter.Adapter {
318
299
  },
319
300
 
320
301
  async sendTransaction(parameters, request) {
321
- const result = await withAccessKey(parameters, async (account, keyAuthorization) => {
322
- const { feePayer, ...rest } = parameters
323
- const client = getClient({
324
- chainId: parameters.chainId,
325
- feePayer: (() => {
326
- if (feePayer === false) return false
327
- if (typeof feePayer === 'string') return feePayer
328
- return undefined
329
- })(),
330
- })
331
- const prepared = await prepareTransactionRequest(client, {
332
- account,
333
- ...rest,
334
- ...(typeof feePayer !== 'undefined' ? { feePayer: !!feePayer as never } : {}),
335
- keyAuthorization,
336
- type: 'tempo',
337
- })
338
- const signed = await account.signTransaction(prepared as never)
339
- return await client.request({
340
- method: 'eth_sendRawTransaction' as never,
341
- params: [signed],
342
- })
302
+ const { feePayer, ...rest } = parameters
303
+ const client = getClient({
304
+ chainId: parameters.chainId,
305
+ feePayer: (() => {
306
+ if (feePayer === false) return false
307
+ if (typeof feePayer === 'string') return feePayer
308
+ return undefined
309
+ })(),
343
310
  })
344
- if (result !== undefined) {
345
- AccessKey.removePending(result.account, { store })
346
- return result.result
311
+ const transaction =
312
+ parameters.from && typeof parameters.chainId !== 'undefined'
313
+ ? await AccessKeyTransaction.create({
314
+ address: parameters.from,
315
+ calls: parameters.calls,
316
+ chainId: parameters.chainId,
317
+ client,
318
+ store,
319
+ })
320
+ : undefined
321
+ if (transaction) {
322
+ try {
323
+ const prepared = await transaction.prepare({
324
+ ...rest,
325
+ ...(typeof feePayer !== 'undefined' ? { feePayer: !!feePayer as never } : {}),
326
+ })
327
+ return await prepared.send()
328
+ } catch (error) {
329
+ console.warn('[accounts] access key sign failed, falling through to dialog:', error)
330
+ }
347
331
  }
348
332
  return await provider.request({
349
333
  ...request,
@@ -352,32 +336,35 @@ export function dialog(options: dialog.Options = {}): Adapter.Adapter {
352
336
  },
353
337
 
354
338
  async sendTransactionSync(parameters, request) {
355
- const result = await withAccessKey(parameters, async (account, keyAuthorization) => {
356
- const { feePayer, ...rest } = parameters
357
- const client = getClient({
358
- chainId: parameters.chainId,
359
- feePayer: (() => {
360
- if (feePayer === false) return false
361
- if (typeof feePayer === 'string') return feePayer
362
- return undefined
363
- })(),
364
- })
365
- const prepared = await prepareTransactionRequest(client, {
366
- account,
367
- ...rest,
368
- ...(typeof feePayer !== 'undefined' ? { feePayer: !!feePayer as never } : {}),
369
- keyAuthorization,
370
- type: 'tempo',
371
- })
372
- const signed = await account.signTransaction(prepared as never)
373
- return await client.request({
374
- method: 'eth_sendRawTransactionSync' as never,
375
- params: [signed],
376
- })
339
+ const { feePayer, ...rest } = parameters
340
+ const client = getClient({
341
+ chainId: parameters.chainId,
342
+ feePayer: (() => {
343
+ if (feePayer === false) return false
344
+ if (typeof feePayer === 'string') return feePayer
345
+ return undefined
346
+ })(),
377
347
  })
378
- if (result !== undefined) {
379
- AccessKey.removePending(result.account, { store })
380
- return result.result
348
+ const transaction =
349
+ parameters.from && typeof parameters.chainId !== 'undefined'
350
+ ? await AccessKeyTransaction.create({
351
+ address: parameters.from,
352
+ calls: parameters.calls,
353
+ chainId: parameters.chainId,
354
+ client,
355
+ store,
356
+ })
357
+ : undefined
358
+ if (transaction) {
359
+ try {
360
+ const prepared = await transaction.prepare({
361
+ ...rest,
362
+ ...(typeof feePayer !== 'undefined' ? { feePayer: !!feePayer as never } : {}),
363
+ })
364
+ return await prepared.sendSync()
365
+ } catch (error) {
366
+ console.warn('[accounts] access key sign failed, falling through to dialog:', error)
367
+ }
381
368
  }
382
369
  return await provider.request({
383
370
  ...request,
@@ -399,15 +386,21 @@ export function dialog(options: dialog.Options = {}): Adapter.Adapter {
399
386
  })
400
387
 
401
388
  if (accessKey) {
402
- const account = getAccount({ accessKey: false, signable: false })
403
- saveAccessKey(account.address, result.keyAuthorization, accessKey.keyPair)
389
+ const account = getAccount({ signable: false })
390
+ saveAccessKey(account.address, result.keyAuthorization, accessKey.generated)
404
391
  }
405
392
 
406
393
  return result
407
394
  },
408
395
 
409
- async revokeAccessKey(_params, request) {
396
+ async revokeAccessKey(params, request) {
410
397
  await provider.request(request)
398
+ AccessKey.remove({
399
+ accessKey: params.accessKeyAddress,
400
+ account: params.address,
401
+ chainId: store.getState().chainId,
402
+ store,
403
+ })
411
404
  },
412
405
 
413
406
  async deposit(_params, request) {
@@ -1,6 +1,6 @@
1
1
  import type { Hex } from 'viem'
2
2
  import { hashMessage, verifyMessage } from 'viem'
3
- import { tempoLocalnet } from 'viem/chains'
3
+ import { tempoLocalnet } from 'viem/tempo/chains'
4
4
  import { describe, expect, test } from 'vp/test'
5
5
 
6
6
  import {
@@ -1,12 +1,13 @@
1
1
  import { Provider as ox_Provider } from 'ox'
2
- import { KeyAuthorization } from 'ox/tempo'
3
- import { BaseError, hashMessage } from 'viem'
2
+ import { KeyAuthorization, SignatureEnvelope } from 'ox/tempo'
3
+ import { hashMessage } from 'viem'
4
4
  import { prepareTransactionRequest } from 'viem/actions'
5
- import { Account as TempoAccount, Actions } from 'viem/tempo'
5
+ import { Actions } from 'viem/tempo'
6
6
 
7
7
  import * as AccessKey from '../AccessKey.js'
8
8
  import * as Account from '../Account.js'
9
9
  import * as Adapter from '../Adapter.js'
10
+ import * as AccessKeyTransaction from '../internal/AccessKeyTransaction.js'
10
11
 
11
12
  /**
12
13
  * Creates a local adapter where the app manages keys and signing in-process.
@@ -28,22 +29,67 @@ export function local(options: local.Options): Adapter.Adapter {
28
29
  const { createAccount, icon, loadAccounts, name, rdns } = options
29
30
 
30
31
  return Adapter.define({ icon, name, rdns }, ({ getAccount, getClient, store }) => {
31
- async function withAccessKey<result>(
32
- options: Pick<Account.find.Options, 'address' | 'calls' | 'chainId'>,
33
- fn: (
34
- account: TempoAccount.Account,
35
- keyAuthorization?: KeyAuthorization.Signed,
36
- ) => Promise<result>,
37
- ): Promise<result> {
38
- const account = getAccount({ ...options, signable: true })
39
- const keyAuthorization = AccessKey.getPending(account, { store })
40
- try {
41
- return await fn(account, keyAuthorization ?? undefined)
42
- } catch (error) {
43
- if (account.source !== 'accessKey') throw error
44
- AccessKey.invalidate(account, error, { store })
45
- const root = getAccount({ accessKey: false, address: options.address, signable: true })
46
- return await fn(root, undefined)
32
+ async function prepareTransaction(parameters: Adapter.signTransaction.Parameters) {
33
+ const { feePayer, ...rest } = parameters
34
+ const client = getClient({
35
+ chainId: parameters.chainId,
36
+ feePayer: (() => {
37
+ if (feePayer === false) return false
38
+ if (typeof feePayer === 'string') return feePayer
39
+ return undefined
40
+ })(),
41
+ })
42
+ const request = {
43
+ ...rest,
44
+ ...(feePayer ? { feePayer: true as const } : {}),
45
+ }
46
+ const state = store.getState()
47
+ const address = parameters.from ?? state.accounts[state.activeAccount]?.address
48
+ const transaction = address
49
+ ? await AccessKeyTransaction.create({
50
+ address,
51
+ calls: parameters.calls,
52
+ chainId: parameters.chainId ?? state.chainId,
53
+ client,
54
+ store,
55
+ })
56
+ : undefined
57
+ if (transaction) {
58
+ try {
59
+ return await transaction.prepare(request)
60
+ } catch {}
61
+ }
62
+
63
+ const account = getAccount({
64
+ address: parameters.from,
65
+ signable: true,
66
+ })
67
+ const prepared = await prepareTransactionRequest(client, {
68
+ account,
69
+ ...request,
70
+ keyAuthorization: undefined,
71
+ type: 'tempo',
72
+ })
73
+ async function sign() {
74
+ return await account.signTransaction(prepared as never)
75
+ }
76
+ return {
77
+ request: prepared,
78
+ sign,
79
+ async send() {
80
+ const signed = await sign()
81
+ return (await client.request({
82
+ method: 'eth_sendRawTransaction' as never,
83
+ params: [signed],
84
+ })) as Adapter.sendTransaction.ReturnType
85
+ },
86
+ async sendSync() {
87
+ const signed = await sign()
88
+ return (await client.request({
89
+ method: 'eth_sendRawTransactionSync' as never,
90
+ params: [signed],
91
+ })) as Adapter.sendTransactionSync.ReturnType
92
+ },
47
93
  }
48
94
  }
49
95
 
@@ -102,7 +148,7 @@ export function local(options: local.Options): Adapter.Adapter {
102
148
  }
103
149
  },
104
150
  async authorizeAccessKey(parameters) {
105
- const account = getAccount({ accessKey: false, signable: true })
151
+ const account = getAccount({ signable: true })
106
152
  const keyAuthorization = await AccessKey.authorize({
107
153
  account,
108
154
  chainId: getClient().chain.id,
@@ -139,7 +185,7 @@ export function local(options: local.Options): Adapter.Adapter {
139
185
 
140
186
  // Slot allocation:
141
187
  // 1. `personalSign` digest, if present.
142
- // 2. Else key-auth digest (existing 1-prompt fold for `authorizeAccessKey`).
188
+ // 2. Else unsigned key-auth digest (existing 1-prompt fold for `authorizeAccessKey`).
143
189
  // 3. Else caller's `rest.digest`.
144
190
  // When BOTH `personalSign` and `authorizeAccessKey` are present,
145
191
  // `personalSign` wins the load-accounts ceremony and the key
@@ -169,12 +215,21 @@ export function local(options: local.Options): Adapter.Adapter {
169
215
  peronsalSign_digest || !signature_
170
216
  ? await account.sign({ hash: keyAuthorization_digest! })
171
217
  : signature_
172
- return AccessKey.saveAuthorization({
173
- address: account.address,
174
- prepared: keyAuthorization_unsigned,
175
- signature: signature_keyAuthorization,
218
+ const keyAuthorization = KeyAuthorization.from(
219
+ keyAuthorization_unsigned.keyAuthorization,
220
+ {
221
+ signature: SignatureEnvelope.from(signature_keyAuthorization),
222
+ },
223
+ )
224
+ AccessKey.add({
225
+ account: account.address,
226
+ authorization: keyAuthorization,
227
+ ...(keyAuthorization_unsigned.keyPair
228
+ ? { keyPair: keyAuthorization_unsigned.keyPair }
229
+ : {}),
176
230
  store,
177
231
  })
232
+ return KeyAuthorization.toRpc(keyAuthorization)
178
233
  })()
179
234
 
180
235
  return {
@@ -187,55 +242,30 @@ export function local(options: local.Options): Adapter.Adapter {
187
242
  }
188
243
  },
189
244
  async revokeAccessKey(parameters) {
190
- const account = getAccount({ accessKey: false, signable: true })
245
+ const account = getAccount({ signable: true })
191
246
  const client = getClient()
192
247
  try {
193
248
  await Actions.accessKey.revoke(client, {
194
249
  account,
195
250
  accessKey: parameters.accessKeyAddress,
196
- } as never)
251
+ })
197
252
  } catch (error) {
198
- const isKeyNotFound =
199
- error instanceof BaseError &&
200
- !!error.walk(
201
- (e) => (e as { data?: { errorName?: string } }).data?.errorName === 'KeyNotFound',
202
- )
203
- if (!isKeyNotFound) throw error
253
+ if (!AccessKey.isUnavailableError(error)) throw error
204
254
  }
205
- store.setState((state) => ({
206
- accessKeys: state.accessKeys.filter(
207
- (a) => a.address?.toLowerCase() !== parameters.accessKeyAddress.toLowerCase(),
208
- ),
209
- }))
255
+ AccessKey.remove({
256
+ accessKey: parameters.accessKeyAddress,
257
+ account: account.address,
258
+ chainId: client.chain.id,
259
+ store,
260
+ })
210
261
  },
211
262
  async signPersonalMessage({ data, address }) {
212
263
  const account = getAccount({ address, signable: true })
213
264
  return await account.signMessage({ message: { raw: data } })
214
265
  },
215
266
  async signTransaction(parameters) {
216
- const { feePayer, ...rest } = parameters
217
- const client = getClient({
218
- chainId: parameters.chainId,
219
- feePayer: (() => {
220
- if (feePayer === false) return false
221
- if (typeof feePayer === 'string') return feePayer
222
- return undefined
223
- })(),
224
- })
225
- const { account, prepared } = await withAccessKey(
226
- { address: parameters.from, calls: parameters.calls, chainId: parameters.chainId },
227
- async (account, keyAuthorization) => ({
228
- account,
229
- prepared: await prepareTransactionRequest(client, {
230
- account,
231
- ...rest,
232
- ...(feePayer ? { feePayer: true } : {}),
233
- keyAuthorization,
234
- type: 'tempo',
235
- }),
236
- }),
237
- )
238
- return await account.signTransaction(prepared as never)
267
+ const prepared = await prepareTransaction(parameters)
268
+ return await prepared.sign()
239
269
  },
240
270
  async signTypedData({ data, address }) {
241
271
  const account = getAccount({ address, signable: true })
@@ -248,66 +278,12 @@ export function local(options: local.Options): Adapter.Adapter {
248
278
  return await account.signTypedData(parsed)
249
279
  },
250
280
  async sendTransaction(parameters) {
251
- const { feePayer, ...rest } = parameters
252
- const client = getClient({
253
- chainId: parameters.chainId,
254
- feePayer: (() => {
255
- if (feePayer === false) return false
256
- if (typeof feePayer === 'string') return feePayer
257
- return undefined
258
- })(),
259
- })
260
- const { account, prepared } = await withAccessKey(
261
- { address: parameters.from, calls: parameters.calls, chainId: parameters.chainId },
262
- async (account, keyAuthorization) => ({
263
- account,
264
- prepared: await prepareTransactionRequest(client, {
265
- account,
266
- ...rest,
267
- ...(feePayer ? { feePayer: true } : {}),
268
- keyAuthorization,
269
- type: 'tempo',
270
- }),
271
- }),
272
- )
273
- const signed = await account.signTransaction(prepared as never)
274
- const result = await client.request({
275
- method: 'eth_sendRawTransaction' as never,
276
- params: [signed],
277
- })
278
- AccessKey.removePending(account, { store })
279
- return result
281
+ const prepared = await prepareTransaction(parameters)
282
+ return await prepared.send()
280
283
  },
281
284
  async sendTransactionSync(parameters) {
282
- const { feePayer, ...rest } = parameters
283
- const client = getClient({
284
- chainId: parameters.chainId,
285
- feePayer: (() => {
286
- if (feePayer === false) return false
287
- if (typeof feePayer === 'string') return feePayer
288
- return undefined
289
- })(),
290
- })
291
- const { account, prepared } = await withAccessKey(
292
- { address: parameters.from, calls: parameters.calls, chainId: parameters.chainId },
293
- async (account, keyAuthorization) => ({
294
- account,
295
- prepared: await prepareTransactionRequest(client, {
296
- account,
297
- ...rest,
298
- ...(feePayer ? { feePayer: true } : {}),
299
- keyAuthorization,
300
- type: 'tempo',
301
- }),
302
- }),
303
- )
304
- const signed = await account.signTransaction(prepared as never)
305
- const result = await client.request({
306
- method: 'eth_sendRawTransactionSync' as never,
307
- params: [signed],
308
- })
309
- AccessKey.removePending(account, { store })
310
- return result
285
+ const prepared = await prepareTransaction(parameters)
286
+ return await prepared.sendSync()
311
287
  },
312
288
  },
313
289
  }