accounts 0.3.0 → 0.4.1

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 (168) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +97 -0
  4. package/dist/core/AccessKey.d.ts +55 -0
  5. package/dist/core/AccessKey.d.ts.map +1 -0
  6. package/dist/core/AccessKey.js +69 -0
  7. package/dist/core/AccessKey.js.map +1 -0
  8. package/dist/core/Account.d.ts +91 -0
  9. package/dist/core/Account.d.ts.map +1 -0
  10. package/dist/core/Account.js +64 -0
  11. package/dist/core/Account.js.map +1 -0
  12. package/dist/core/Adapter.d.ts +187 -0
  13. package/dist/core/Adapter.d.ts.map +1 -0
  14. package/dist/core/Adapter.js +7 -0
  15. package/dist/core/Adapter.js.map +1 -0
  16. package/dist/core/Ceremony.d.ts +109 -0
  17. package/dist/core/Ceremony.d.ts.map +1 -0
  18. package/dist/core/Ceremony.js +104 -0
  19. package/dist/core/Ceremony.js.map +1 -0
  20. package/dist/core/Client.d.ts +16 -0
  21. package/dist/core/Client.d.ts.map +1 -0
  22. package/dist/core/Client.js +18 -0
  23. package/dist/core/Client.js.map +1 -0
  24. package/dist/core/Dialog.d.ts +52 -0
  25. package/dist/core/Dialog.d.ts.map +1 -0
  26. package/dist/core/Dialog.js +342 -0
  27. package/dist/core/Dialog.js.map +1 -0
  28. package/dist/core/Expiry.d.ts +15 -0
  29. package/dist/core/Expiry.d.ts.map +1 -0
  30. package/dist/core/Expiry.js +29 -0
  31. package/dist/core/Expiry.js.map +1 -0
  32. package/dist/core/Messenger.d.ts +86 -0
  33. package/dist/core/Messenger.d.ts.map +1 -0
  34. package/dist/core/Messenger.js +127 -0
  35. package/dist/core/Messenger.js.map +1 -0
  36. package/dist/core/Provider.d.ts +69 -0
  37. package/dist/core/Provider.d.ts.map +1 -0
  38. package/dist/core/Provider.js +401 -0
  39. package/dist/core/Provider.js.map +1 -0
  40. package/dist/core/Remote.d.ts +114 -0
  41. package/dist/core/Remote.d.ts.map +1 -0
  42. package/dist/core/Remote.js +116 -0
  43. package/dist/core/Remote.js.map +1 -0
  44. package/dist/core/Schema.d.ts +805 -0
  45. package/dist/core/Schema.d.ts.map +1 -0
  46. package/dist/core/Schema.js +43 -0
  47. package/dist/core/Schema.js.map +1 -0
  48. package/dist/core/Storage.d.ts +42 -0
  49. package/dist/core/Storage.d.ts.map +1 -0
  50. package/dist/core/Storage.js +173 -0
  51. package/dist/core/Storage.js.map +1 -0
  52. package/dist/core/Store.d.ts +58 -0
  53. package/dist/core/Store.d.ts.map +1 -0
  54. package/dist/core/Store.js +58 -0
  55. package/dist/core/Store.js.map +1 -0
  56. package/dist/core/adapters/dangerous_secp256k1.d.ts +30 -0
  57. package/dist/core/adapters/dangerous_secp256k1.d.ts.map +1 -0
  58. package/dist/core/adapters/dangerous_secp256k1.js +39 -0
  59. package/dist/core/adapters/dangerous_secp256k1.js.map +1 -0
  60. package/dist/core/adapters/dialog.d.ts +31 -0
  61. package/dist/core/adapters/dialog.d.ts.map +1 -0
  62. package/dist/core/adapters/dialog.js +306 -0
  63. package/dist/core/adapters/dialog.js.map +1 -0
  64. package/dist/core/adapters/local.d.ts +33 -0
  65. package/dist/core/adapters/local.d.ts.map +1 -0
  66. package/dist/core/adapters/local.js +227 -0
  67. package/dist/core/adapters/local.js.map +1 -0
  68. package/dist/core/adapters/webAuthn.d.ts +36 -0
  69. package/dist/core/adapters/webAuthn.d.ts.map +1 -0
  70. package/dist/core/adapters/webAuthn.js +93 -0
  71. package/dist/core/adapters/webAuthn.js.map +1 -0
  72. package/dist/core/internal/withDedupe.d.ts +12 -0
  73. package/dist/core/internal/withDedupe.d.ts.map +1 -0
  74. package/dist/core/internal/withDedupe.js +12 -0
  75. package/dist/core/internal/withDedupe.js.map +1 -0
  76. package/dist/core/zod/request.d.ts +31 -0
  77. package/dist/core/zod/request.d.ts.map +1 -0
  78. package/dist/core/zod/request.js +41 -0
  79. package/dist/core/zod/request.js.map +1 -0
  80. package/dist/core/zod/rpc.d.ts +603 -0
  81. package/dist/core/zod/rpc.d.ts.map +1 -0
  82. package/dist/core/zod/rpc.js +293 -0
  83. package/dist/core/zod/rpc.js.map +1 -0
  84. package/dist/core/zod/utils.d.ts +18 -0
  85. package/dist/core/zod/utils.d.ts.map +1 -0
  86. package/dist/core/zod/utils.js +21 -0
  87. package/dist/core/zod/utils.js.map +1 -0
  88. package/dist/index.d.ts +15 -0
  89. package/dist/index.d.ts.map +1 -0
  90. package/dist/index.js +15 -0
  91. package/dist/index.js.map +1 -0
  92. package/dist/internal/types.d.ts +284 -0
  93. package/dist/internal/types.d.ts.map +1 -0
  94. package/dist/internal/types.js +2 -0
  95. package/dist/internal/types.js.map +1 -0
  96. package/dist/server/Handler.d.ts +257 -0
  97. package/dist/server/Handler.d.ts.map +1 -0
  98. package/dist/server/Handler.js +433 -0
  99. package/dist/server/Handler.js.map +1 -0
  100. package/dist/server/Kv.d.ts +16 -0
  101. package/dist/server/Kv.d.ts.map +1 -0
  102. package/dist/server/Kv.js +30 -0
  103. package/dist/server/Kv.js.map +1 -0
  104. package/dist/server/index.d.ts +3 -0
  105. package/dist/server/index.d.ts.map +1 -0
  106. package/dist/server/index.js +3 -0
  107. package/dist/server/index.js.map +1 -0
  108. package/dist/server/internal/requestListener.d.ts +124 -0
  109. package/dist/server/internal/requestListener.d.ts.map +1 -0
  110. package/dist/server/internal/requestListener.js +173 -0
  111. package/dist/server/internal/requestListener.js.map +1 -0
  112. package/dist/wagmi/Connector.d.ts +93 -0
  113. package/dist/wagmi/Connector.d.ts.map +1 -0
  114. package/dist/wagmi/Connector.js +238 -0
  115. package/dist/wagmi/Connector.js.map +1 -0
  116. package/dist/wagmi/index.d.ts +3 -0
  117. package/dist/wagmi/index.d.ts.map +1 -0
  118. package/dist/wagmi/index.js +3 -0
  119. package/dist/wagmi/index.js.map +1 -0
  120. package/package.json +56 -2
  121. package/src/core/AccessKey.test.ts +257 -0
  122. package/src/core/AccessKey.ts +123 -0
  123. package/src/core/Account.test.ts +309 -0
  124. package/src/core/Account.ts +152 -0
  125. package/src/core/Adapter.ts +238 -0
  126. package/src/core/Ceremony.browser.test.ts +239 -0
  127. package/src/core/Ceremony.test.ts +151 -0
  128. package/src/core/Ceremony.ts +203 -0
  129. package/src/core/Client.ts +36 -0
  130. package/src/core/Dialog.browser.test.ts +309 -0
  131. package/src/core/Dialog.test-d.ts +19 -0
  132. package/src/core/Dialog.ts +442 -0
  133. package/src/core/Expiry.ts +34 -0
  134. package/src/core/Messenger.ts +206 -0
  135. package/src/core/Provider.browser.test.ts +774 -0
  136. package/src/core/Provider.connect.browser.test.ts +415 -0
  137. package/src/core/Provider.test-d.ts +53 -0
  138. package/src/core/Provider.test.ts +1566 -0
  139. package/src/core/Provider.ts +559 -0
  140. package/src/core/Remote.ts +262 -0
  141. package/src/core/Schema.test-d.ts +211 -0
  142. package/src/core/Schema.ts +143 -0
  143. package/src/core/Storage.ts +213 -0
  144. package/src/core/Store.test.ts +287 -0
  145. package/src/core/Store.ts +129 -0
  146. package/src/core/adapters/dangerous_secp256k1.ts +53 -0
  147. package/src/core/adapters/dialog.ts +379 -0
  148. package/src/core/adapters/local.test.ts +97 -0
  149. package/src/core/adapters/local.ts +277 -0
  150. package/src/core/adapters/webAuthn.ts +129 -0
  151. package/src/core/internal/withDedupe.test.ts +116 -0
  152. package/src/core/internal/withDedupe.ts +20 -0
  153. package/src/core/mppx.test.ts +83 -0
  154. package/src/core/zod/request.test.ts +121 -0
  155. package/src/core/zod/request.ts +70 -0
  156. package/src/core/zod/rpc.ts +374 -0
  157. package/src/core/zod/utils.test.ts +69 -0
  158. package/src/core/zod/utils.ts +40 -0
  159. package/src/index.ts +14 -0
  160. package/src/internal/types.ts +378 -0
  161. package/src/server/Handler.test.ts +1014 -0
  162. package/src/server/Handler.ts +605 -0
  163. package/src/server/Kv.ts +46 -0
  164. package/src/server/index.ts +2 -0
  165. package/src/server/internal/requestListener.ts +273 -0
  166. package/src/tsconfig.json +9 -0
  167. package/src/wagmi/Connector.ts +287 -0
  168. package/src/wagmi/index.ts +2 -0
@@ -0,0 +1,559 @@
1
+ import { announceProvider } from 'mipd'
2
+ import { Mppx, tempo as mppx_tempo } from 'mppx/client'
3
+ import { Hash, Hex, Json, Provider as ox_Provider, RpcResponse } from 'ox'
4
+ import type { Chain, Client as ViemClient, Transport } from 'viem'
5
+ import { tempo, tempoModerato } from 'viem/chains'
6
+ import { Actions } from 'viem/tempo'
7
+ import * as z from 'zod/mini'
8
+
9
+ import * as Account from './Account.js'
10
+ import type * as Adapter from './Adapter.js'
11
+ import { dialog } from './adapters/dialog.js'
12
+ import * as Client from './Client.js'
13
+ import { withDedupe } from './internal/withDedupe.js'
14
+ import * as Schema from './Schema.js'
15
+ import * as Storage from './Storage.js'
16
+ import * as Store from './Store.js'
17
+ import * as Request from './zod/request.js'
18
+ import * as Rpc from './zod/rpc.js'
19
+
20
+ export type Provider = ox_Provider.Provider<{ schema: Schema.Ox }> &
21
+ ox_Provider.Emitter & {
22
+ /** Configured chains. */
23
+ chains: readonly [Chain, ...Chain[]]
24
+ /** Returns a viem Account for the given address (or active account). */
25
+ getAccount: Account.Find
26
+ /** Returns a viem Client for the given (or current) chain ID. */
27
+ getClient(options?: {
28
+ chainId?: number | undefined
29
+ feePayer?: string | undefined
30
+ }): ViemClient<Transport, typeof tempo>
31
+ /** Reactive state store. */
32
+ store: Store.Store
33
+ }
34
+
35
+ /**
36
+ * Creates an EIP-1193 provider with a pluggable adapter.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import { Provider } from 'accounts'
41
+ *
42
+ * const provider = Provider.create()
43
+ * ```
44
+ */
45
+ export function create(options: create.Options = {}): create.ReturnType {
46
+ const {
47
+ adapter = dialog(),
48
+ chains = [tempo, tempoModerato],
49
+ feePayerUrl,
50
+ persistCredentials,
51
+ testnet,
52
+ storage = typeof window !== 'undefined' ? Storage.idb() : Storage.memory(),
53
+ } = options
54
+
55
+ const defaultChain = testnet
56
+ ? (chains.find((c) => c.testnet) ?? chains[chains.length - 1]!)
57
+ : chains[0]!
58
+
59
+ const store = Store.create({
60
+ chainId: defaultChain.id,
61
+ persistCredentials,
62
+ storage,
63
+ })
64
+
65
+ const getAccount: Account.Find = (options = {}) => Account.find({ ...options, store }) as never
66
+
67
+ function getClient(
68
+ options: { chainId?: number | undefined; feePayer?: string | undefined } = {},
69
+ ) {
70
+ const { chainId, feePayer } = options
71
+ return Client.fromChainId(chainId, { chains, feePayer, store })
72
+ }
73
+
74
+ const instance = adapter({ getAccount, getClient, storage, store })
75
+ const { actions } = instance
76
+
77
+ const emitter = ox_Provider.createEmitter()
78
+
79
+ // Emit EIP-1193 events on state changes.
80
+ store.subscribe(
81
+ (state) => state.accounts.map((a) => a.address).join(),
82
+ () =>
83
+ emitter.emit(
84
+ 'accountsChanged',
85
+ store.getState().accounts.map((a) => a.address),
86
+ ),
87
+ )
88
+ store.subscribe(
89
+ (state) => state.chainId,
90
+ (chainId) => emitter.emit('chainChanged', Hex.fromNumber(chainId)),
91
+ )
92
+ store.subscribe(
93
+ (state) => state.accounts.length > 0,
94
+ (connected) => {
95
+ if (connected) emitter.emit('connect', { chainId: Hex.fromNumber(store.getState().chainId) })
96
+ else emitter.emit('disconnect', new ox_Provider.DisconnectedError())
97
+ },
98
+ )
99
+
100
+ /** Throws `DisconnectedError` if no accounts are connected. */
101
+ function assertConnected() {
102
+ if (store.getState().accounts.length === 0)
103
+ throw new ox_Provider.DisconnectedError({ message: 'No accounts connected.' })
104
+ }
105
+
106
+ /** Returns accounts to persist. When `persistAccounts` is set, merges new accounts with existing ones. */
107
+ function resolveAccounts(accounts: readonly Account.Store[]) {
108
+ if (!instance.persistAccounts) return accounts
109
+ const merged = [...accounts]
110
+ for (const a of store.getState().accounts)
111
+ if (!merged.some((m) => m.address.toLowerCase() === a.address.toLowerCase())) merged.push(a)
112
+ return merged
113
+ }
114
+
115
+ /** Resolves the `feePayer` field from a transaction request into a URL string or `undefined`. */
116
+ function resolveFeePayer(feePayer: string | boolean | undefined): string | undefined {
117
+ if (typeof feePayer === 'string') return feePayer
118
+ if (feePayer === true) return feePayerUrl
119
+ return undefined
120
+ }
121
+
122
+ const provider = Object.assign(
123
+ ox_Provider.from(
124
+ {
125
+ ...(emitter as unknown as ox_Provider.Emitter),
126
+ async request({ method, params }: { method: string; params?: any }) {
127
+ await Store.waitForHydration(store)
128
+
129
+ const shouldDedupe = [
130
+ 'eth_accounts',
131
+ 'eth_chainId',
132
+ 'eth_requestAccounts',
133
+ 'wallet_connect',
134
+ 'wallet_getBalances',
135
+ 'wallet_getCapabilities',
136
+ ].includes(method)
137
+
138
+ return withDedupe(
139
+ async () => {
140
+ // Validate known methods. Unknown methods fall through to the RPC proxy.
141
+ let request: Request.WithDecoded<typeof Schema.Request>
142
+ try {
143
+ request = Request.validate(Schema.Request, { method, params })
144
+ } catch (e) {
145
+ if (!(e instanceof ox_Provider.UnsupportedMethodError)) throw e
146
+ // Proxy unknown methods to the RPC node.
147
+ return await Client.fromChainId(undefined, { chains, store }).request({
148
+ method: method as any,
149
+ params: params as any,
150
+ })
151
+ }
152
+
153
+ const result = await (async () => {
154
+ switch (request.method) {
155
+ case 'eth_accounts': {
156
+ const { accounts, activeAccount } = store.getState()
157
+ if (accounts.length === 0) return []
158
+ const activeAddr = accounts[activeAccount]?.address
159
+ const activeIdx = accounts.findIndex((a) => a.address === activeAddr)
160
+ const sorted = [...accounts]
161
+ if (activeIdx >= 0) {
162
+ const [active] = sorted.splice(activeIdx, 1)
163
+ return [active!.address, ...sorted.map((a) => a.address)]
164
+ }
165
+ return sorted.map(
166
+ (a) => a.address,
167
+ ) satisfies Rpc.eth_accounts.Encoded['returns']
168
+ }
169
+
170
+ case 'eth_chainId':
171
+ return Hex.fromNumber(
172
+ store.getState().chainId,
173
+ ) satisfies Rpc.eth_chainId.Encoded['returns']
174
+
175
+ case 'eth_requestAccounts': {
176
+ const { accounts } = await actions.loadAccounts(undefined, {
177
+ method: 'wallet_connect',
178
+ params: undefined,
179
+ })
180
+
181
+ store.setState({ accounts: resolveAccounts(accounts), activeAccount: 0 })
182
+
183
+ return accounts.map(
184
+ (a) => a.address,
185
+ ) satisfies Rpc.eth_requestAccounts.Encoded['returns']
186
+ }
187
+
188
+ case 'eth_sendTransaction': {
189
+ assertConnected()
190
+ const [decoded] = request._decoded.params
191
+ return (await actions.sendTransaction(
192
+ {
193
+ ...decoded,
194
+ feePayer: resolveFeePayer(decoded.feePayer),
195
+ },
196
+ request,
197
+ )) satisfies Rpc.eth_sendTransaction.Encoded['returns']
198
+ }
199
+
200
+ case 'eth_signTransaction': {
201
+ assertConnected()
202
+ const [decoded] = request._decoded.params
203
+ return (await actions.signTransaction(
204
+ {
205
+ ...decoded,
206
+ feePayer: resolveFeePayer(decoded.feePayer),
207
+ },
208
+ request,
209
+ )) satisfies Rpc.eth_signTransaction.Encoded['returns']
210
+ }
211
+
212
+ case 'eth_sendTransactionSync': {
213
+ assertConnected()
214
+ const [decoded] = request._decoded.params
215
+ return (await actions.sendTransactionSync(
216
+ {
217
+ ...decoded,
218
+ feePayer: resolveFeePayer(decoded.feePayer),
219
+ },
220
+ request,
221
+ )) satisfies Rpc.eth_sendTransactionSync.Encoded['returns']
222
+ }
223
+
224
+ case 'eth_signTypedData_v4': {
225
+ assertConnected()
226
+ const [address, data] = request._decoded.params
227
+ return (await actions.signTypedData(
228
+ {
229
+ address,
230
+ data,
231
+ },
232
+ request,
233
+ )) satisfies Rpc.eth_signTypedData_v4.Encoded['returns']
234
+ }
235
+
236
+ case 'personal_sign': {
237
+ assertConnected()
238
+ const [data, address] = request._decoded.params
239
+ return (await actions.signPersonalMessage(
240
+ {
241
+ address,
242
+ data,
243
+ },
244
+ request,
245
+ )) satisfies Rpc.personal_sign.Encoded['returns']
246
+ }
247
+
248
+ case 'wallet_sendCalls': {
249
+ assertConnected()
250
+ const decoded = request._decoded.params?.[0]
251
+ const { calls = [], capabilities, chainId, from } = decoded ?? {}
252
+ const sync = capabilities?.sync
253
+ const feePayer = resolveFeePayer(feePayerUrl ? true : undefined)
254
+ const txRequest = {
255
+ calls,
256
+ chainId,
257
+ from,
258
+ ...(feePayer ? { feePayer } : {}),
259
+ }
260
+ if (!sync) {
261
+ const hash = await actions.sendTransaction(txRequest, {
262
+ method: 'eth_sendTransaction',
263
+ params: [z.encode(Rpc.transactionRequest, txRequest)] as const,
264
+ })
265
+ const chainId = Hex.fromNumber(store.getState().chainId)
266
+ const id = Hex.concat(hash, Hex.padLeft(chainId, 32), sendCallsMagic)
267
+ return { capabilities: { sync }, id }
268
+ }
269
+ const receipt = await actions.sendTransactionSync(txRequest as never, {
270
+ method: 'eth_sendTransactionSync',
271
+ params: [z.encode(Rpc.transactionRequest, txRequest)] as const,
272
+ })
273
+ const hash = receipt.transactionHash
274
+ const chainIdHex = Hex.fromNumber(store.getState().chainId)
275
+ const id = Hex.concat(hash, Hex.padLeft(chainIdHex, 32), sendCallsMagic)
276
+ return {
277
+ atomic: true,
278
+ capabilities: { sync },
279
+ chainId: chainIdHex,
280
+ id,
281
+ receipts: [receipt],
282
+ status: (receipt as { status: string }).status === '0x1' ? 200 : 500,
283
+ version: '2.0.0',
284
+ } satisfies Rpc.wallet_sendCalls.Encoded['returns']
285
+ }
286
+
287
+ case 'wallet_getBalances': {
288
+ const decoded = request._decoded.params?.[0]
289
+ const { accounts, activeAccount } = store.getState()
290
+ const account = decoded?.account ?? accounts[activeAccount]?.address
291
+ if (!account)
292
+ throw new ox_Provider.DisconnectedError({
293
+ message: 'No accounts connected.',
294
+ })
295
+ const tokens = decoded?.tokens
296
+ // TODO: hook up to indexer
297
+ if (!tokens || tokens.length === 0)
298
+ throw new RpcResponse.InvalidParamsError({
299
+ message: '`tokens` is required.',
300
+ })
301
+ const client = Client.fromChainId(decoded?.chainId, { chains, store })
302
+ return (await Promise.all(
303
+ tokens.map(async (token) => {
304
+ const [balance, metadata] = await Promise.all([
305
+ Actions.token.getBalance(client, { account, token }),
306
+ Actions.token.getMetadata(client, { token }),
307
+ ])
308
+ const value = Number(balance) / 10 ** metadata.decimals
309
+ const display = new Intl.NumberFormat('en-US', {
310
+ style: 'currency',
311
+ currency: metadata.currency,
312
+ }).format(value)
313
+ return {
314
+ address: token,
315
+ balance: Hex.fromNumber(balance),
316
+ decimals: metadata.decimals,
317
+ display,
318
+ name: metadata.name,
319
+ symbol: metadata.symbol,
320
+ }
321
+ }),
322
+ )) satisfies Rpc.wallet_getBalances.Encoded['returns']
323
+ }
324
+
325
+ case 'wallet_getCallsStatus': {
326
+ const [id] = request._decoded.params ?? []
327
+ if (!id) throw new Error('`id` not found')
328
+ if (!id.endsWith(sendCallsMagic.slice(2))) throw new Error('`id` not supported')
329
+ Hex.assert(id)
330
+ const hash = Hex.slice(id, 0, 32)
331
+ const chainId = Hex.fromNumber(Number(Hex.slice(id, 32, 64)))
332
+ const client = Client.fromChainId(Number(chainId), { chains, store })
333
+ const receipt = await client.request({
334
+ method: 'eth_getTransactionReceipt',
335
+ params: [hash],
336
+ })
337
+ return {
338
+ atomic: true,
339
+ chainId,
340
+ id,
341
+ receipts: receipt ? [receipt as never] : [],
342
+ status: receipt?.status === '0x1' ? 200 : 500,
343
+ version: '2.0.0',
344
+ } satisfies Rpc.wallet_getCallsStatus.Encoded['returns']
345
+ }
346
+
347
+ case 'wallet_getCapabilities': {
348
+ const decoded = request._decoded.params
349
+ const address = decoded?.[0]
350
+ const chainIds = decoded?.[1]
351
+
352
+ if (address) {
353
+ const { accounts } = store.getState()
354
+ if (!accounts.some((a) => a.address.toLowerCase() === address.toLowerCase()))
355
+ throw new ox_Provider.UnauthorizedError({
356
+ message: `Address ${address} is not connected.`,
357
+ })
358
+ }
359
+
360
+ const filtered = chainIds
361
+ ? chains.filter((c) => chainIds.includes(Hex.fromNumber(c.id)))
362
+ : chains
363
+
364
+ const result: Record<
365
+ string,
366
+ {
367
+ accessKeys: { status: 'supported' }
368
+ atomic: { status: 'supported' }
369
+ }
370
+ > = {}
371
+ for (const chain of filtered)
372
+ result[Hex.fromNumber(chain.id)] = {
373
+ accessKeys: { status: 'supported' },
374
+ atomic: { status: 'supported' },
375
+ }
376
+ return result as Rpc.wallet_getCapabilities.Encoded['returns']
377
+ }
378
+
379
+ case 'wallet_connect': {
380
+ const capabilities = request._decoded.params?.[0]?.capabilities
381
+ const authorizeAccessKey =
382
+ capabilities?.authorizeAccessKey ?? options.authorizeAccessKey?.()
383
+
384
+ const { keyAuthorization, accounts, signature } = await (async () => {
385
+ if (capabilities?.method === 'register')
386
+ return await actions.createAccount(
387
+ {
388
+ digest: capabilities.digest,
389
+ authorizeAccessKey,
390
+ name: capabilities.name ?? 'default',
391
+ userId: capabilities.userId,
392
+ },
393
+ request,
394
+ )
395
+ return await actions.loadAccounts(
396
+ {
397
+ credentialId: capabilities?.credentialId,
398
+ digest: capabilities?.digest,
399
+ authorizeAccessKey,
400
+ selectAccount: capabilities?.selectAccount,
401
+ },
402
+ request,
403
+ )
404
+ })()
405
+
406
+ store.setState({ accounts: resolveAccounts(accounts), activeAccount: 0 })
407
+
408
+ const accountAddress = accounts[0]?.address
409
+ return {
410
+ accounts: accounts.map((a) => ({
411
+ address: a.address,
412
+ capabilities:
413
+ a.address === accountAddress
414
+ ? {
415
+ ...(keyAuthorization
416
+ ? {
417
+ keyAuthorization: {
418
+ ...keyAuthorization,
419
+ address: keyAuthorization.keyId,
420
+ },
421
+ }
422
+ : {}),
423
+ ...(signature && capabilities?.digest ? { signature } : {}),
424
+ }
425
+ : {},
426
+ })),
427
+ } satisfies Rpc.wallet_connect.Encoded['returns']
428
+ }
429
+
430
+ case 'wallet_disconnect':
431
+ await actions.disconnect?.()
432
+ store.setState({ accessKeys: [], accounts: [], activeAccount: 0 })
433
+ return
434
+
435
+ case 'wallet_authorizeAccessKey': {
436
+ assertConnected()
437
+ if (!actions.authorizeAccessKey)
438
+ throw new ox_Provider.UnsupportedMethodError({
439
+ message: '`authorizeAccessKey` not supported by adapter.',
440
+ })
441
+ const decoded = request._decoded.params[0]
442
+ const result = await actions.authorizeAccessKey(decoded, request)
443
+ return {
444
+ ...result,
445
+ address: result.keyId,
446
+ } satisfies Rpc.wallet_authorizeAccessKey.Encoded['returns']
447
+ }
448
+
449
+ case 'wallet_revokeAccessKey': {
450
+ assertConnected()
451
+ if (!actions.revokeAccessKey)
452
+ throw new ox_Provider.UnsupportedMethodError({
453
+ message: '`revokeAccessKey` not supported by adapter.',
454
+ })
455
+ const [decoded] = request._decoded.params
456
+ await actions.revokeAccessKey(
457
+ {
458
+ ...decoded,
459
+ },
460
+ request,
461
+ )
462
+ return
463
+ }
464
+
465
+ case 'wallet_switchEthereumChain': {
466
+ const { chainId } = request._decoded.params[0]
467
+ if (!chains.some((c) => c.id === chainId))
468
+ throw new ox_Provider.UnsupportedChainIdError({
469
+ message: `Chain ${chainId} not configured.`,
470
+ })
471
+ await actions.switchChain?.({ chainId })
472
+ store.setState({ chainId })
473
+ return
474
+ }
475
+ }
476
+ })()
477
+
478
+ return result
479
+ },
480
+ {
481
+ enabled: shouldDedupe,
482
+ id: Json.stringify({ method, params }),
483
+ },
484
+ )
485
+ },
486
+ },
487
+ { schema: Schema.ox },
488
+ ),
489
+ { chains, getAccount, getClient, store },
490
+ )
491
+
492
+ if (typeof window !== 'undefined') {
493
+ announceProvider({
494
+ info: {
495
+ icon: adapter.icon ?? defaultIcon,
496
+ name: adapter.name ?? 'Injected Wallet',
497
+ rdns:
498
+ adapter.rdns ??
499
+ `com.${(adapter.name ?? 'Injected Wallet').toLowerCase().replace(/\s+/g, '')}`,
500
+ uuid: crypto.randomUUID(),
501
+ },
502
+ provider,
503
+ } as never)
504
+ }
505
+
506
+ if (options.mpp)
507
+ Mppx.create({
508
+ methods: [
509
+ mppx_tempo({
510
+ getClient: ({ chainId }) => {
511
+ const client = Client.fromChainId(chainId, { chains, store })
512
+ const account = Account.find({ store, signable: true })
513
+ return Object.assign(client, { account })
514
+ },
515
+ }),
516
+ ],
517
+ })
518
+
519
+ return provider
520
+ }
521
+
522
+ const defaultIcon =
523
+ 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"><rect width="1" height="1"/></svg>' as const
524
+ const sendCallsMagic = Hash.keccak256(Hex.fromString('TEMPO_5792'))
525
+
526
+ export declare namespace create {
527
+ type Options = {
528
+ /** Adapter to use for account management. @default dialog() */
529
+ adapter?: Adapter.Adapter | undefined
530
+ /**
531
+ * Default access key parameters for `wallet_connect`.
532
+ *
533
+ * When set, `wallet_connect` will automatically authorize an access key.
534
+ */
535
+ authorizeAccessKey?: (() => Adapter.authorizeAccessKey.Parameters) | undefined
536
+ /**
537
+ * Supported chains. First chain is the default.
538
+ * @default [tempo, tempoModerato]
539
+ */
540
+ chains?: readonly [Chain, ...Chain[]] | undefined
541
+ /**
542
+ * Fee payer URL for interacting with a service running `Handler.feePayer`
543
+ * from `accounts/server`.
544
+ */
545
+ feePayerUrl?: string | undefined
546
+ /** Enable Machine Payment Protocol (mppx) support. @default false */
547
+ mpp?: boolean | undefined
548
+ /** Whether to persist credentials and access keys to storage. When `false`, only account addresses are persisted. @default true */
549
+ persistCredentials?: boolean | undefined
550
+ /** Storage adapter for persistence. @default Storage.idb() in browser, Storage.memory() otherwise. */
551
+ storage?: Storage.Storage | undefined
552
+ /**
553
+ * Use testnet.
554
+ * @default false
555
+ */
556
+ testnet?: boolean | undefined
557
+ }
558
+ type ReturnType = Provider
559
+ }