tempo.ts 0.12.0 → 0.13.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 (123) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +28 -34
  3. package/package.json +2 -28
  4. package/dist/wagmi/Actions/amm.d.ts +0 -418
  5. package/dist/wagmi/Actions/amm.d.ts.map +0 -1
  6. package/dist/wagmi/Actions/amm.js +0 -462
  7. package/dist/wagmi/Actions/amm.js.map +0 -1
  8. package/dist/wagmi/Actions/dex.d.ts +0 -864
  9. package/dist/wagmi/Actions/dex.d.ts.map +0 -1
  10. package/dist/wagmi/Actions/dex.js +0 -968
  11. package/dist/wagmi/Actions/dex.js.map +0 -1
  12. package/dist/wagmi/Actions/faucet.d.ts +0 -66
  13. package/dist/wagmi/Actions/faucet.d.ts.map +0 -1
  14. package/dist/wagmi/Actions/faucet.js +0 -64
  15. package/dist/wagmi/Actions/faucet.js.map +0 -1
  16. package/dist/wagmi/Actions/fee.d.ts +0 -111
  17. package/dist/wagmi/Actions/fee.d.ts.map +0 -1
  18. package/dist/wagmi/Actions/fee.js +0 -126
  19. package/dist/wagmi/Actions/fee.js.map +0 -1
  20. package/dist/wagmi/Actions/index.d.ts +0 -9
  21. package/dist/wagmi/Actions/index.d.ts.map +0 -1
  22. package/dist/wagmi/Actions/index.js +0 -9
  23. package/dist/wagmi/Actions/index.js.map +0 -1
  24. package/dist/wagmi/Actions/nonce.d.ts +0 -77
  25. package/dist/wagmi/Actions/nonce.d.ts.map +0 -1
  26. package/dist/wagmi/Actions/nonce.js +0 -87
  27. package/dist/wagmi/Actions/nonce.js.map +0 -1
  28. package/dist/wagmi/Actions/policy.d.ts +0 -480
  29. package/dist/wagmi/Actions/policy.d.ts.map +0 -1
  30. package/dist/wagmi/Actions/policy.js +0 -530
  31. package/dist/wagmi/Actions/policy.js.map +0 -1
  32. package/dist/wagmi/Actions/reward.d.ts +0 -346
  33. package/dist/wagmi/Actions/reward.d.ts.map +0 -1
  34. package/dist/wagmi/Actions/reward.js +0 -382
  35. package/dist/wagmi/Actions/reward.js.map +0 -1
  36. package/dist/wagmi/Actions/token.d.ts +0 -1546
  37. package/dist/wagmi/Actions/token.d.ts.map +0 -1
  38. package/dist/wagmi/Actions/token.js +0 -1712
  39. package/dist/wagmi/Actions/token.js.map +0 -1
  40. package/dist/wagmi/Connector.d.ts +0 -91
  41. package/dist/wagmi/Connector.d.ts.map +0 -1
  42. package/dist/wagmi/Connector.js +0 -473
  43. package/dist/wagmi/Connector.js.map +0 -1
  44. package/dist/wagmi/Hooks/amm.d.ts +0 -411
  45. package/dist/wagmi/Hooks/amm.d.ts.map +0 -1
  46. package/dist/wagmi/Hooks/amm.js +0 -494
  47. package/dist/wagmi/Hooks/amm.js.map +0 -1
  48. package/dist/wagmi/Hooks/dex.d.ts +0 -773
  49. package/dist/wagmi/Hooks/dex.d.ts.map +0 -1
  50. package/dist/wagmi/Hooks/dex.js +0 -921
  51. package/dist/wagmi/Hooks/dex.js.map +0 -1
  52. package/dist/wagmi/Hooks/faucet.d.ts +0 -71
  53. package/dist/wagmi/Hooks/faucet.d.ts.map +0 -1
  54. package/dist/wagmi/Hooks/faucet.js +0 -76
  55. package/dist/wagmi/Hooks/faucet.js.map +0 -1
  56. package/dist/wagmi/Hooks/fee.d.ts +0 -97
  57. package/dist/wagmi/Hooks/fee.d.ts.map +0 -1
  58. package/dist/wagmi/Hooks/fee.js +0 -109
  59. package/dist/wagmi/Hooks/fee.js.map +0 -1
  60. package/dist/wagmi/Hooks/index.d.ts +0 -9
  61. package/dist/wagmi/Hooks/index.d.ts.map +0 -1
  62. package/dist/wagmi/Hooks/index.js +0 -9
  63. package/dist/wagmi/Hooks/index.js.map +0 -1
  64. package/dist/wagmi/Hooks/nonce.d.ts +0 -59
  65. package/dist/wagmi/Hooks/nonce.d.ts.map +0 -1
  66. package/dist/wagmi/Hooks/nonce.js +0 -75
  67. package/dist/wagmi/Hooks/nonce.js.map +0 -1
  68. package/dist/wagmi/Hooks/policy.d.ts +0 -423
  69. package/dist/wagmi/Hooks/policy.d.ts.map +0 -1
  70. package/dist/wagmi/Hooks/policy.js +0 -510
  71. package/dist/wagmi/Hooks/policy.js.map +0 -1
  72. package/dist/wagmi/Hooks/reward.d.ts +0 -305
  73. package/dist/wagmi/Hooks/reward.d.ts.map +0 -1
  74. package/dist/wagmi/Hooks/reward.js +0 -368
  75. package/dist/wagmi/Hooks/reward.js.map +0 -1
  76. package/dist/wagmi/Hooks/token.d.ts +0 -1388
  77. package/dist/wagmi/Hooks/token.d.ts.map +0 -1
  78. package/dist/wagmi/Hooks/token.js +0 -1657
  79. package/dist/wagmi/Hooks/token.js.map +0 -1
  80. package/dist/wagmi/KeyManager.d.ts +0 -60
  81. package/dist/wagmi/KeyManager.d.ts.map +0 -1
  82. package/dist/wagmi/KeyManager.js +0 -106
  83. package/dist/wagmi/KeyManager.js.map +0 -1
  84. package/dist/wagmi/index.d.ts +0 -5
  85. package/dist/wagmi/index.d.ts.map +0 -1
  86. package/dist/wagmi/index.js +0 -5
  87. package/dist/wagmi/index.js.map +0 -1
  88. package/src/wagmi/Actions/amm.test.ts +0 -208
  89. package/src/wagmi/Actions/amm.ts +0 -690
  90. package/src/wagmi/Actions/dex.test.ts +0 -1482
  91. package/src/wagmi/Actions/dex.ts +0 -1540
  92. package/src/wagmi/Actions/faucet.ts +0 -89
  93. package/src/wagmi/Actions/fee.test.ts +0 -63
  94. package/src/wagmi/Actions/fee.ts +0 -211
  95. package/src/wagmi/Actions/index.ts +0 -8
  96. package/src/wagmi/Actions/nonce.test.ts +0 -82
  97. package/src/wagmi/Actions/nonce.ts +0 -139
  98. package/src/wagmi/Actions/policy.test.ts +0 -461
  99. package/src/wagmi/Actions/policy.ts +0 -817
  100. package/src/wagmi/Actions/reward.test.ts +0 -216
  101. package/src/wagmi/Actions/reward.ts +0 -613
  102. package/src/wagmi/Actions/token.test.ts +0 -1309
  103. package/src/wagmi/Actions/token.ts +0 -2644
  104. package/src/wagmi/Connector.test.ts +0 -56
  105. package/src/wagmi/Connector.ts +0 -670
  106. package/src/wagmi/Hooks/amm.test.ts +0 -564
  107. package/src/wagmi/Hooks/amm.ts +0 -796
  108. package/src/wagmi/Hooks/dex.test.ts +0 -992
  109. package/src/wagmi/Hooks/dex.ts +0 -1598
  110. package/src/wagmi/Hooks/faucet.ts +0 -144
  111. package/src/wagmi/Hooks/fee.test.ts +0 -166
  112. package/src/wagmi/Hooks/fee.ts +0 -206
  113. package/src/wagmi/Hooks/index.ts +0 -8
  114. package/src/wagmi/Hooks/nonce.test.ts +0 -142
  115. package/src/wagmi/Hooks/nonce.ts +0 -117
  116. package/src/wagmi/Hooks/policy.test.ts +0 -665
  117. package/src/wagmi/Hooks/policy.ts +0 -873
  118. package/src/wagmi/Hooks/reward.test.ts +0 -249
  119. package/src/wagmi/Hooks/reward.ts +0 -645
  120. package/src/wagmi/Hooks/token.test.ts +0 -1183
  121. package/src/wagmi/Hooks/token.ts +0 -2906
  122. package/src/wagmi/KeyManager.ts +0 -172
  123. package/src/wagmi/index.ts +0 -7
@@ -1,670 +0,0 @@
1
- import * as idb from 'idb-keyval'
2
- import * as Address from 'ox/Address'
3
- import type * as Hex from 'ox/Hex'
4
- import * as PublicKey from 'ox/PublicKey'
5
- import { KeyAuthorization, SignatureEnvelope } from 'ox/tempo'
6
- import {
7
- createClient,
8
- defineChain,
9
- type EIP1193Provider,
10
- getAddress,
11
- type LocalAccount,
12
- SwitchChainError,
13
- } from 'viem'
14
- import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
15
- import {
16
- Account,
17
- WebAuthnP256,
18
- WebCryptoP256,
19
- walletNamespaceCompat,
20
- } from 'viem/tempo'
21
- import { ChainNotConfiguredError, createConnector } from 'wagmi'
22
- import type { OneOf } from '../internal/types.js'
23
- import type * as KeyManager from './KeyManager.js'
24
-
25
- /**
26
- * Connector for a Secp256k1 EOA.
27
- *
28
- * WARNING: NOT RECOMMENDED FOR PRODUCTION USAGE.
29
- * This connector stores private keys in clear text, and are bound to the session
30
- * length of the storage used.
31
- *
32
- * @returns Connector.
33
- */
34
- export function dangerous_secp256k1(
35
- options: dangerous_secp256k1.Parameters = {},
36
- ) {
37
- let account: LocalAccount | undefined
38
-
39
- type Properties = {
40
- connect<withCapabilities extends boolean = false>(parameters: {
41
- capabilities?:
42
- | OneOf<
43
- | {
44
- type: 'sign-up'
45
- }
46
- | {}
47
- >
48
- | undefined
49
- chainId?: number | undefined
50
- isReconnecting?: boolean | undefined
51
- withCapabilities?: withCapabilities | boolean | undefined
52
- }): Promise<{
53
- accounts: readonly Address.Address[]
54
- chainId: number
55
- }>
56
- }
57
- type Provider = Pick<EIP1193Provider, 'request'>
58
- type StorageItem = {
59
- 'secp256k1.activeAddress': Address.Address
60
- 'secp256k1.lastActiveAddress': Address.Address
61
- [key: `secp256k1.${string}.privateKey`]: Hex.Hex
62
- }
63
-
64
- return createConnector<Provider, Properties, StorageItem>((config) => ({
65
- id: 'secp256k1',
66
- name: 'EOA (Secp256k1)',
67
- type: 'secp256k1',
68
- async setup() {
69
- const address = await config.storage?.getItem('secp256k1.activeAddress')
70
- const privateKey = await config.storage?.getItem(
71
- `secp256k1.${address}.privateKey`,
72
- )
73
- if (privateKey) account = privateKeyToAccount(privateKey)
74
- else if (
75
- address &&
76
- options.account &&
77
- Address.isEqual(address, options.account.address)
78
- )
79
- account = options.account
80
- },
81
- async connect(parameters = {}) {
82
- const address = await (async () => {
83
- if (
84
- 'capabilities' in parameters &&
85
- parameters.capabilities?.type === 'sign-up'
86
- ) {
87
- const privateKey = generatePrivateKey()
88
- const account = privateKeyToAccount(privateKey)
89
- const address = account.address
90
- await config.storage?.setItem(
91
- `secp256k1.${address}.privateKey`,
92
- privateKey,
93
- )
94
- await config.storage?.setItem('secp256k1.activeAddress', address)
95
- await config.storage?.setItem('secp256k1.lastActiveAddress', address)
96
- return address
97
- }
98
-
99
- const address = await config.storage?.getItem(
100
- 'secp256k1.lastActiveAddress',
101
- )
102
- const privateKey = await config.storage?.getItem(
103
- `secp256k1.${address}.privateKey`,
104
- )
105
-
106
- if (privateKey) account = privateKeyToAccount(privateKey)
107
- else if (options.account) {
108
- account = options.account
109
- await config.storage?.setItem(
110
- 'secp256k1.lastActiveAddress',
111
- account.address,
112
- )
113
- }
114
-
115
- if (!account) throw new Error('account not found.')
116
-
117
- await config.storage?.setItem(
118
- 'secp256k1.activeAddress',
119
- account.address,
120
- )
121
- return account.address
122
- })()
123
-
124
- const chainId = parameters.chainId ?? config.chains[0]?.id
125
- if (!chainId) throw new ChainNotConfiguredError()
126
-
127
- return {
128
- accounts: (parameters.withCapabilities
129
- ? [{ address }]
130
- : [address]) as never,
131
- chainId,
132
- }
133
- },
134
- async disconnect() {
135
- await config.storage?.removeItem('secp256k1.activeAddress')
136
- account = undefined
137
- },
138
- async getAccounts() {
139
- if (!account) return []
140
- return [getAddress(account.address)]
141
- },
142
- async getChainId() {
143
- return config.chains[0]?.id!
144
- },
145
- async isAuthorized() {
146
- try {
147
- const accounts = await this.getAccounts()
148
- return !!accounts.length
149
- } catch (error) {
150
- console.error(
151
- 'Connector.secp256k1: Failed to check authorization',
152
- error,
153
- )
154
- return false
155
- }
156
- },
157
- async switchChain({ chainId }) {
158
- const chain = config.chains.find((chain) => chain.id === chainId)
159
- if (!chain) throw new SwitchChainError(new ChainNotConfiguredError())
160
- return chain
161
- },
162
- onAccountsChanged() {},
163
- onChainChanged(chain) {
164
- const chainId = Number(chain)
165
- config.emitter.emit('change', { chainId })
166
- },
167
- async onDisconnect() {
168
- config.emitter.emit('disconnect')
169
- account = undefined
170
- },
171
- async getClient({ chainId } = {}) {
172
- const chain =
173
- config.chains.find((x) => x.id === chainId) ?? config.chains[0]
174
- if (!chain) throw new ChainNotConfiguredError()
175
-
176
- const transports = config.transports
177
- if (!transports) throw new ChainNotConfiguredError()
178
-
179
- const transport = transports[chain.id]
180
- if (!transport) throw new ChainNotConfiguredError()
181
-
182
- if (!account) throw new Error('account not found.')
183
-
184
- return createClient({
185
- account,
186
- chain,
187
- transport: walletNamespaceCompat(transport, {
188
- account,
189
- }),
190
- })
191
- },
192
- async getProvider({ chainId } = {}) {
193
- const { request } = await this.getClient!({ chainId })
194
- return { request }
195
- },
196
- }))
197
- }
198
-
199
- export declare namespace dangerous_secp256k1 {
200
- export type Parameters = {
201
- account?: LocalAccount | undefined
202
- }
203
- }
204
-
205
- /**
206
- * Connector for a WebAuthn EOA.
207
- *
208
- * @returns Connector.
209
- */
210
- export function webAuthn(options: webAuthn.Parameters) {
211
- let account: Account.RootAccount | undefined
212
- let accessKey: Account.AccessKeyAccount | undefined
213
-
214
- const defaultAccessKeyOptions = {
215
- expiry: Math.floor(
216
- (Date.now() + 24 * 60 * 60 * 1000) / 1000, // one day
217
- ),
218
- strict: false,
219
- }
220
- const accessKeyOptions = (() => {
221
- if (typeof options.grantAccessKey === 'object')
222
- return { ...defaultAccessKeyOptions, ...options.grantAccessKey }
223
- if (options.grantAccessKey === true) return defaultAccessKeyOptions
224
- return undefined
225
- })()
226
-
227
- type Properties = {
228
- connect<withCapabilities extends boolean = false>(parameters: {
229
- chainId?: number | undefined
230
- capabilities?:
231
- | OneOf<
232
- | {
233
- label?: string | undefined
234
- type: 'sign-up'
235
- }
236
- | {
237
- selectAccount?: boolean | undefined
238
- type: 'sign-in'
239
- }
240
- | {}
241
- >
242
- | undefined
243
- isReconnecting?: boolean | undefined
244
- withCapabilities?: withCapabilities | boolean | undefined
245
- }): Promise<{ accounts: readonly Address.Address[]; chainId: number }>
246
- }
247
- type Provider = Pick<EIP1193Provider, 'request'>
248
- type StorageItem = {
249
- [
250
- key: `pendingKeyAuthorization:${string}`
251
- ]: KeyAuthorization.KeyAuthorization
252
- 'webAuthn.activeCredential': WebAuthnP256.P256Credential
253
- 'webAuthn.lastActiveCredential': WebAuthnP256.P256Credential
254
- }
255
-
256
- return createConnector<Provider, Properties, StorageItem>((config) => ({
257
- id: 'webAuthn',
258
- name: 'EOA (WebAuthn)',
259
- type: 'webAuthn',
260
- async setup() {
261
- const credential = await config.storage?.getItem(
262
- 'webAuthn.activeCredential',
263
- )
264
- if (!credential) return
265
- account = Account.fromWebAuthnP256(credential)
266
- },
267
- async connect(parameters = {}) {
268
- const capabilities =
269
- 'capabilities' in parameters ? (parameters.capabilities ?? {}) : {}
270
-
271
- if (
272
- accessKeyOptions?.strict &&
273
- accessKeyOptions.expiry &&
274
- accessKeyOptions.expiry < Date.now() / 1000
275
- )
276
- throw new Error(
277
- `\`grantAccessKey.expiry = ${accessKeyOptions.expiry}\` is in the past (${new Date(accessKeyOptions.expiry * 1000).toLocaleString()}). Please provide a valid expiry.`,
278
- )
279
-
280
- // We are going to need to find:
281
- // - a WebAuthn `credential` to instantiate an account
282
- // - optionally, a `keyPair` to use as the access key for the account
283
- // - optionally, a signed `keyAuthorization` to provision the access key
284
- const { credential, keyAuthorization, keyPair } = await (async () => {
285
- // If the connection type is of "sign-up", we are going to create a new credential
286
- // and provision an access key (if needed).
287
- if (capabilities.type === 'sign-up') {
288
- // Create credential (sign up)
289
- const createOptions_remote = await options.keyManager.getChallenge?.()
290
- const label =
291
- capabilities.label ??
292
- options.createOptions?.label ??
293
- new Date().toISOString()
294
- const rpId =
295
- createOptions_remote?.rp?.id ??
296
- options.createOptions?.rpId ??
297
- options.rpId
298
- const credential = await WebAuthnP256.createCredential({
299
- ...(options.createOptions ?? {}),
300
- label,
301
- rpId,
302
- ...(createOptions_remote ?? {}),
303
- })
304
- await options.keyManager.setPublicKey({
305
- credential: credential.raw,
306
- publicKey: credential.publicKey,
307
- })
308
-
309
- // Get key pair (access key) to use for the account.
310
- const keyPair = await (async () => {
311
- if (!accessKeyOptions) return undefined
312
- return await WebCryptoP256.createKeyPair()
313
- })()
314
-
315
- return { credential, keyPair }
316
- }
317
-
318
- // If we are not selecting an account, we will check if an active credential is present in
319
- // storage and if so, we will use it to instantiate an account.
320
- if (!capabilities.selectAccount) {
321
- const credential = (await config.storage?.getItem(
322
- 'webAuthn.activeCredential',
323
- )) as WebAuthnP256.getCredential.ReturnValue | undefined
324
-
325
- if (credential) {
326
- // Get key pair (access key) to use for the account.
327
- const keyPair = await (async () => {
328
- if (!accessKeyOptions) return undefined
329
- const address = Address.fromPublicKey(
330
- PublicKey.fromHex(credential.publicKey),
331
- )
332
- return await idb.get(`accessKey:${address}`)
333
- })()
334
-
335
- // If the access key provisioning is not in strict mode, return the credential and key pair (if exists).
336
- if (!accessKeyOptions?.strict) return { credential, keyPair }
337
-
338
- // If a key pair is found, return the credential and key pair.
339
- if (keyPair) return { credential, keyPair }
340
-
341
- // If we are reconnecting, throw an error if not found.
342
- if (parameters.isReconnecting)
343
- throw new Error('credential not found.')
344
-
345
- // Otherwise, we want to continue to sign up or register against new key pair.
346
- }
347
- }
348
-
349
- // Discover credential
350
- {
351
- // Get key pair (access key) to use for the account.
352
- const keyPair = await (async () => {
353
- if (!accessKeyOptions) return undefined
354
- return await WebCryptoP256.createKeyPair()
355
- })()
356
-
357
- // If we are provisioning an access key, we will need to sign a key authorization.
358
- // We will need the hash (digest) to sign, and the address of the access key to construct the key authorization.
359
- const { hash, keyAuthorization_unsigned } = await (async () => {
360
- if (!keyPair)
361
- return { accessKeyAddress: undefined, hash: undefined }
362
- const accessKeyAddress = Address.fromPublicKey(keyPair.publicKey)
363
- const keyAuthorization_unsigned = KeyAuthorization.from({
364
- ...accessKeyOptions,
365
- address: accessKeyAddress,
366
- type: 'p256',
367
- })
368
- const hash = KeyAuthorization.getSignPayload(
369
- keyAuthorization_unsigned,
370
- )
371
- return { keyAuthorization_unsigned, hash }
372
- })()
373
-
374
- // If no active credential, we will attempt to load the last active credential from storage.
375
- const lastActiveCredential = !capabilities.selectAccount
376
- ? await config.storage?.getItem('webAuthn.lastActiveCredential')
377
- : undefined
378
- const credential = await WebAuthnP256.getCredential({
379
- ...(options.getOptions ?? {}),
380
- credentialId: lastActiveCredential?.id,
381
- async getPublicKey(credential) {
382
- const publicKey = await options.keyManager.getPublicKey({
383
- credential,
384
- })
385
- if (!publicKey) throw new Error('publicKey not found.')
386
- return publicKey
387
- },
388
- hash,
389
- rpId: options.getOptions?.rpId ?? options.rpId,
390
- })
391
-
392
- const keyAuthorization = keyAuthorization_unsigned
393
- ? KeyAuthorization.from({
394
- ...keyAuthorization_unsigned,
395
- signature: SignatureEnvelope.from({
396
- metadata: credential.metadata,
397
- signature: credential.signature,
398
- publicKey: PublicKey.fromHex(credential.publicKey),
399
- type: 'webAuthn',
400
- }),
401
- })
402
- : undefined
403
-
404
- return { credential, keyAuthorization, keyPair }
405
- }
406
- })()
407
-
408
- config.storage?.setItem(
409
- 'webAuthn.lastActiveCredential',
410
- normalizeValue(credential),
411
- )
412
- config.storage?.setItem(
413
- 'webAuthn.activeCredential',
414
- normalizeValue(credential),
415
- )
416
-
417
- account = Account.fromWebAuthnP256(credential)
418
-
419
- if (keyPair) {
420
- accessKey = Account.fromWebCryptoP256(keyPair, {
421
- access: account,
422
- })
423
-
424
- // If we are reconnecting, check if the access key is expired.
425
- if (parameters.isReconnecting) {
426
- if (
427
- 'keyAuthorization' in keyPair &&
428
- keyPair.keyAuthorization.expiry &&
429
- keyPair.keyAuthorization.expiry < Date.now() / 1000
430
- ) {
431
- // remove any pending key authorizations from storage.
432
- await config?.storage?.removeItem(
433
- `pendingKeyAuthorization:${account.address.toLowerCase()}`,
434
- )
435
-
436
- const message = `Access key expired (on ${new Date(keyPair.keyAuthorization.expiry * 1000).toLocaleString()}).`
437
- accessKey = undefined
438
-
439
- // if in strict mode, disconnect and throw an error.
440
- if (accessKeyOptions?.strict) {
441
- await this.disconnect()
442
- throw new Error(message)
443
- }
444
- // otherwise, fall back to the root account.
445
- console.warn(`${message} Falling back to passkey.`)
446
- }
447
- }
448
- // If we are not reconnecting, orchestrate the provisioning of the access key.
449
- else {
450
- const keyAuth =
451
- keyAuthorization ??
452
- (await account.signKeyAuthorization(accessKey, accessKeyOptions))
453
-
454
- await config?.storage?.setItem(
455
- `pendingKeyAuthorization:${account.address.toLowerCase()}`,
456
- keyAuth,
457
- )
458
- await idb.set(`accessKey:${account.address.toLowerCase()}`, {
459
- ...keyPair,
460
- keyAuthorization: keyAuth,
461
- })
462
- }
463
- // If we are granting an access key and it is in strict mode, throw an error if the access key is not provisioned.
464
- } else if (accessKeyOptions?.strict) {
465
- await config.storage?.removeItem('webAuthn.activeCredential')
466
- throw new Error('access key not found')
467
- }
468
-
469
- const address = getAddress(account.address)
470
-
471
- const chainId = parameters.chainId ?? config.chains[0]?.id
472
- if (!chainId) throw new ChainNotConfiguredError()
473
-
474
- return {
475
- accounts: (parameters.withCapabilities
476
- ? [{ address }]
477
- : [address]) as never,
478
- chainId,
479
- }
480
- },
481
- async disconnect() {
482
- await config.storage?.removeItem('webAuthn.activeCredential')
483
- config.emitter.emit('disconnect')
484
- account = undefined
485
- },
486
- async getAccounts() {
487
- if (!account) return []
488
- return [getAddress(account.address)]
489
- },
490
- async getChainId() {
491
- return config.chains[0]?.id!
492
- },
493
- async isAuthorized() {
494
- try {
495
- const accounts = await this.getAccounts()
496
- return !!accounts.length
497
- } catch (error) {
498
- console.error(
499
- 'Connector.webAuthn: Failed to check authorization',
500
- error,
501
- )
502
- return false
503
- }
504
- },
505
- async switchChain({ chainId }) {
506
- const chain = config.chains.find((chain) => chain.id === chainId)
507
- if (!chain) throw new SwitchChainError(new ChainNotConfiguredError())
508
- return chain
509
- },
510
- onAccountsChanged() {},
511
- onChainChanged(chain) {
512
- const chainId = Number(chain)
513
- config.emitter.emit('change', { chainId })
514
- },
515
- async onDisconnect() {
516
- config.emitter.emit('disconnect')
517
- account = undefined
518
- },
519
- async getClient({ chainId } = {}) {
520
- const chain =
521
- config.chains.find((x) => x.id === chainId) ?? config.chains[0]
522
- if (!chain) throw new ChainNotConfiguredError()
523
-
524
- const transports = config.transports
525
- if (!transports) throw new ChainNotConfiguredError()
526
-
527
- const transport = transports[chain.id]
528
- if (!transport) throw new ChainNotConfiguredError()
529
-
530
- const targetAccount = await (async () => {
531
- if (!accessKey) return account
532
-
533
- const item = await idb.get(
534
- `accessKey:${accessKey.address.toLowerCase()}`,
535
- )
536
- if (
537
- item?.keyAuthorization.expiry &&
538
- item.keyAuthorization.expiry < Date.now() / 1000
539
- ) {
540
- // remove any pending key authorizations from storage.
541
- await config?.storage?.removeItem(
542
- `pendingKeyAuthorization:${accessKey.address.toLowerCase()}`,
543
- )
544
-
545
- const message = `Access key expired (on ${new Date(item.keyAuthorization.expiry * 1000).toLocaleString()}).`
546
-
547
- // if in strict mode, disconnect and throw an error.
548
- if (accessKeyOptions?.strict) {
549
- await this.disconnect()
550
- throw new Error(message)
551
- }
552
-
553
- // otherwise, fall back to the root account.
554
- console.warn(`${message} Falling back to passkey.`)
555
- return account
556
- }
557
- return accessKey
558
- })()
559
- if (!targetAccount) throw new Error('account not found.')
560
-
561
- const targetChain = defineChain({
562
- ...chain,
563
- async prepareTransactionRequest(args, { phase }) {
564
- const keyAuthorization = await (async () => {
565
- {
566
- const keyAuthorization = (
567
- args as {
568
- keyAuthorization?:
569
- | KeyAuthorization.KeyAuthorization
570
- | undefined
571
- }
572
- ).keyAuthorization
573
- if (keyAuthorization) return keyAuthorization
574
- }
575
-
576
- const keyAuthorization = await config.storage?.getItem(
577
- `pendingKeyAuthorization:${targetAccount?.address.toLowerCase()}`,
578
- )
579
- await config.storage?.removeItem(
580
- `pendingKeyAuthorization:${targetAccount?.address.toLowerCase()}`,
581
- )
582
- return keyAuthorization
583
- })()
584
-
585
- const [prepareTransactionRequestFn, options] = (() => {
586
- if (!chain.prepareTransactionRequest) return [undefined, undefined]
587
- if (typeof chain.prepareTransactionRequest === 'function')
588
- return [chain.prepareTransactionRequest, undefined]
589
- return chain.prepareTransactionRequest
590
- })()
591
-
592
- const request = await (async () => {
593
- if (!prepareTransactionRequestFn) return {}
594
- if (!options || options?.runAt?.includes(phase))
595
- return await prepareTransactionRequestFn(args, { phase })
596
- return {}
597
- })()
598
-
599
- return {
600
- ...args,
601
- ...request,
602
- keyAuthorization,
603
- }
604
- },
605
- })
606
-
607
- return createClient({
608
- account: targetAccount,
609
- chain: targetChain,
610
- transport: walletNamespaceCompat(transport, {
611
- account: targetAccount,
612
- }),
613
- })
614
- },
615
- async getProvider({ chainId } = {}) {
616
- const { request } = await this.getClient!({ chainId })
617
- return { request }
618
- },
619
- }))
620
- }
621
-
622
- export namespace webAuthn {
623
- export type Parameters = {
624
- /** Options for WebAuthn registration. */
625
- createOptions?:
626
- | Pick<
627
- WebAuthnP256.createCredential.Parameters,
628
- 'createFn' | 'label' | 'rpId' | 'userId' | 'timeout'
629
- >
630
- | undefined
631
- /** Options for WebAuthn authentication. */
632
- getOptions?:
633
- | Pick<WebAuthnP256.getCredential.Parameters, 'getFn' | 'rpId'>
634
- | undefined
635
- /**
636
- * Whether or not to grant an access key upon connection, and optionally, expiry + limits to assign to the key.
637
- */
638
- grantAccessKey?:
639
- | boolean
640
- | (Pick<KeyAuthorization.KeyAuthorization, 'expiry' | 'limits'> & {
641
- /** Whether or not to throw an error and disconnect if the access key is not provisioned or is expired. */
642
- strict?: boolean | undefined
643
- })
644
- /** Public key manager. */
645
- keyManager: KeyManager.KeyManager
646
- /** The RP ID to use for WebAuthn. */
647
- rpId?: string | undefined
648
- }
649
- }
650
-
651
- /**
652
- * Normalizes a value into a structured-clone compatible format.
653
- *
654
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone
655
- */
656
- function normalizeValue<type>(value: type): type {
657
- if (Array.isArray(value)) return value.map(normalizeValue) as never
658
- if (typeof value === 'function') return undefined as never
659
- if (typeof value !== 'object' || value === null) return value
660
- if (Object.getPrototypeOf(value) !== Object.prototype)
661
- try {
662
- return structuredClone(value)
663
- } catch {
664
- return undefined as never
665
- }
666
-
667
- const normalized: Record<string, unknown> = {}
668
- for (const [k, v] of Object.entries(value)) normalized[k] = normalizeValue(v)
669
- return normalized as never
670
- }