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.
- package/CHANGELOG.md +13 -0
- package/LICENSE +21 -0
- package/README.md +97 -0
- package/dist/core/AccessKey.d.ts +55 -0
- package/dist/core/AccessKey.d.ts.map +1 -0
- package/dist/core/AccessKey.js +69 -0
- package/dist/core/AccessKey.js.map +1 -0
- package/dist/core/Account.d.ts +91 -0
- package/dist/core/Account.d.ts.map +1 -0
- package/dist/core/Account.js +64 -0
- package/dist/core/Account.js.map +1 -0
- package/dist/core/Adapter.d.ts +187 -0
- package/dist/core/Adapter.d.ts.map +1 -0
- package/dist/core/Adapter.js +7 -0
- package/dist/core/Adapter.js.map +1 -0
- package/dist/core/Ceremony.d.ts +109 -0
- package/dist/core/Ceremony.d.ts.map +1 -0
- package/dist/core/Ceremony.js +104 -0
- package/dist/core/Ceremony.js.map +1 -0
- package/dist/core/Client.d.ts +16 -0
- package/dist/core/Client.d.ts.map +1 -0
- package/dist/core/Client.js +18 -0
- package/dist/core/Client.js.map +1 -0
- package/dist/core/Dialog.d.ts +52 -0
- package/dist/core/Dialog.d.ts.map +1 -0
- package/dist/core/Dialog.js +342 -0
- package/dist/core/Dialog.js.map +1 -0
- package/dist/core/Expiry.d.ts +15 -0
- package/dist/core/Expiry.d.ts.map +1 -0
- package/dist/core/Expiry.js +29 -0
- package/dist/core/Expiry.js.map +1 -0
- package/dist/core/Messenger.d.ts +86 -0
- package/dist/core/Messenger.d.ts.map +1 -0
- package/dist/core/Messenger.js +127 -0
- package/dist/core/Messenger.js.map +1 -0
- package/dist/core/Provider.d.ts +69 -0
- package/dist/core/Provider.d.ts.map +1 -0
- package/dist/core/Provider.js +401 -0
- package/dist/core/Provider.js.map +1 -0
- package/dist/core/Remote.d.ts +114 -0
- package/dist/core/Remote.d.ts.map +1 -0
- package/dist/core/Remote.js +116 -0
- package/dist/core/Remote.js.map +1 -0
- package/dist/core/Schema.d.ts +805 -0
- package/dist/core/Schema.d.ts.map +1 -0
- package/dist/core/Schema.js +43 -0
- package/dist/core/Schema.js.map +1 -0
- package/dist/core/Storage.d.ts +42 -0
- package/dist/core/Storage.d.ts.map +1 -0
- package/dist/core/Storage.js +173 -0
- package/dist/core/Storage.js.map +1 -0
- package/dist/core/Store.d.ts +58 -0
- package/dist/core/Store.d.ts.map +1 -0
- package/dist/core/Store.js +58 -0
- package/dist/core/Store.js.map +1 -0
- package/dist/core/adapters/dangerous_secp256k1.d.ts +30 -0
- package/dist/core/adapters/dangerous_secp256k1.d.ts.map +1 -0
- package/dist/core/adapters/dangerous_secp256k1.js +39 -0
- package/dist/core/adapters/dangerous_secp256k1.js.map +1 -0
- package/dist/core/adapters/dialog.d.ts +31 -0
- package/dist/core/adapters/dialog.d.ts.map +1 -0
- package/dist/core/adapters/dialog.js +306 -0
- package/dist/core/adapters/dialog.js.map +1 -0
- package/dist/core/adapters/local.d.ts +33 -0
- package/dist/core/adapters/local.d.ts.map +1 -0
- package/dist/core/adapters/local.js +227 -0
- package/dist/core/adapters/local.js.map +1 -0
- package/dist/core/adapters/webAuthn.d.ts +36 -0
- package/dist/core/adapters/webAuthn.d.ts.map +1 -0
- package/dist/core/adapters/webAuthn.js +93 -0
- package/dist/core/adapters/webAuthn.js.map +1 -0
- package/dist/core/internal/withDedupe.d.ts +12 -0
- package/dist/core/internal/withDedupe.d.ts.map +1 -0
- package/dist/core/internal/withDedupe.js +12 -0
- package/dist/core/internal/withDedupe.js.map +1 -0
- package/dist/core/zod/request.d.ts +31 -0
- package/dist/core/zod/request.d.ts.map +1 -0
- package/dist/core/zod/request.js +41 -0
- package/dist/core/zod/request.js.map +1 -0
- package/dist/core/zod/rpc.d.ts +603 -0
- package/dist/core/zod/rpc.d.ts.map +1 -0
- package/dist/core/zod/rpc.js +293 -0
- package/dist/core/zod/rpc.js.map +1 -0
- package/dist/core/zod/utils.d.ts +18 -0
- package/dist/core/zod/utils.d.ts.map +1 -0
- package/dist/core/zod/utils.js +21 -0
- package/dist/core/zod/utils.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/types.d.ts +284 -0
- package/dist/internal/types.d.ts.map +1 -0
- package/dist/internal/types.js +2 -0
- package/dist/internal/types.js.map +1 -0
- package/dist/server/Handler.d.ts +257 -0
- package/dist/server/Handler.d.ts.map +1 -0
- package/dist/server/Handler.js +433 -0
- package/dist/server/Handler.js.map +1 -0
- package/dist/server/Kv.d.ts +16 -0
- package/dist/server/Kv.d.ts.map +1 -0
- package/dist/server/Kv.js +30 -0
- package/dist/server/Kv.js.map +1 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +3 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/internal/requestListener.d.ts +124 -0
- package/dist/server/internal/requestListener.d.ts.map +1 -0
- package/dist/server/internal/requestListener.js +173 -0
- package/dist/server/internal/requestListener.js.map +1 -0
- package/dist/wagmi/Connector.d.ts +93 -0
- package/dist/wagmi/Connector.d.ts.map +1 -0
- package/dist/wagmi/Connector.js +238 -0
- package/dist/wagmi/Connector.js.map +1 -0
- package/dist/wagmi/index.d.ts +3 -0
- package/dist/wagmi/index.d.ts.map +1 -0
- package/dist/wagmi/index.js +3 -0
- package/dist/wagmi/index.js.map +1 -0
- package/package.json +56 -2
- package/src/core/AccessKey.test.ts +257 -0
- package/src/core/AccessKey.ts +123 -0
- package/src/core/Account.test.ts +309 -0
- package/src/core/Account.ts +152 -0
- package/src/core/Adapter.ts +238 -0
- package/src/core/Ceremony.browser.test.ts +239 -0
- package/src/core/Ceremony.test.ts +151 -0
- package/src/core/Ceremony.ts +203 -0
- package/src/core/Client.ts +36 -0
- package/src/core/Dialog.browser.test.ts +309 -0
- package/src/core/Dialog.test-d.ts +19 -0
- package/src/core/Dialog.ts +442 -0
- package/src/core/Expiry.ts +34 -0
- package/src/core/Messenger.ts +206 -0
- package/src/core/Provider.browser.test.ts +774 -0
- package/src/core/Provider.connect.browser.test.ts +415 -0
- package/src/core/Provider.test-d.ts +53 -0
- package/src/core/Provider.test.ts +1566 -0
- package/src/core/Provider.ts +559 -0
- package/src/core/Remote.ts +262 -0
- package/src/core/Schema.test-d.ts +211 -0
- package/src/core/Schema.ts +143 -0
- package/src/core/Storage.ts +213 -0
- package/src/core/Store.test.ts +287 -0
- package/src/core/Store.ts +129 -0
- package/src/core/adapters/dangerous_secp256k1.ts +53 -0
- package/src/core/adapters/dialog.ts +379 -0
- package/src/core/adapters/local.test.ts +97 -0
- package/src/core/adapters/local.ts +277 -0
- package/src/core/adapters/webAuthn.ts +129 -0
- package/src/core/internal/withDedupe.test.ts +116 -0
- package/src/core/internal/withDedupe.ts +20 -0
- package/src/core/mppx.test.ts +83 -0
- package/src/core/zod/request.test.ts +121 -0
- package/src/core/zod/request.ts +70 -0
- package/src/core/zod/rpc.ts +374 -0
- package/src/core/zod/utils.test.ts +69 -0
- package/src/core/zod/utils.ts +40 -0
- package/src/index.ts +14 -0
- package/src/internal/types.ts +378 -0
- package/src/server/Handler.test.ts +1014 -0
- package/src/server/Handler.ts +605 -0
- package/src/server/Kv.ts +46 -0
- package/src/server/index.ts +2 -0
- package/src/server/internal/requestListener.ts +273 -0
- package/src/tsconfig.json +9 -0
- package/src/wagmi/Connector.ts +287 -0
- package/src/wagmi/index.ts +2 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import { Address, Provider as ox_Provider, RpcRequest as ox_RpcRequest } from 'ox'
|
|
2
|
+
import { KeyAuthorization } from 'ox/tempo'
|
|
3
|
+
import { prepareTransactionRequest } from 'viem/actions'
|
|
4
|
+
import { Account as TempoAccount } from 'viem/tempo'
|
|
5
|
+
import { z } from 'zod/mini'
|
|
6
|
+
|
|
7
|
+
import * as AccessKey from '../AccessKey.js'
|
|
8
|
+
import * as Adapter from '../Adapter.js'
|
|
9
|
+
import * as Dialog from '../Dialog.js'
|
|
10
|
+
import * as Schema from '../Schema.js'
|
|
11
|
+
import type * as Store from '../Store.js'
|
|
12
|
+
import * as Rpc from '../zod/rpc.js'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Creates a dialog adapter that delegates signing to a remote embed app
|
|
16
|
+
* via an iframe or popup dialog.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { dialog, Provider } from 'accounts'
|
|
21
|
+
*
|
|
22
|
+
* const provider = Provider.create({
|
|
23
|
+
* adapter: dialog(),
|
|
24
|
+
* })
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function dialog(options: dialog.Options = {}): Adapter.Adapter {
|
|
28
|
+
const {
|
|
29
|
+
dialog = Dialog.isSafari() ? Dialog.popup() : Dialog.iframe(),
|
|
30
|
+
host = 'https://wallet.tempo.xyz/embed',
|
|
31
|
+
icon,
|
|
32
|
+
name = 'Tempo',
|
|
33
|
+
rdns = 'xyz.tempo',
|
|
34
|
+
} = options
|
|
35
|
+
|
|
36
|
+
return Adapter.define({ icon, name, rdns }, ({ getAccount, getClient, store }) => {
|
|
37
|
+
const listeners = new Set<(requestQueue: readonly Store.QueuedRequest[]) => void>()
|
|
38
|
+
const requestStore = ox_RpcRequest.createStore()
|
|
39
|
+
|
|
40
|
+
/** Wait for a queued request to be resolved via the store. */
|
|
41
|
+
function waitForQueuedRequest(requestId: number) {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
const listener = (requestQueue: readonly Store.QueuedRequest[]) => {
|
|
44
|
+
const queued = requestQueue.find((x) => x.request.id === requestId)
|
|
45
|
+
|
|
46
|
+
// Request removed and queue empty — cancelled or dialog closed.
|
|
47
|
+
if (!queued && requestQueue.length === 0) {
|
|
48
|
+
listeners.delete(listener)
|
|
49
|
+
reject(new ox_Provider.UserRejectedRequestError())
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Request not found but queue has other requests — wait.
|
|
54
|
+
if (!queued) return
|
|
55
|
+
|
|
56
|
+
// Request found but not yet resolved — wait.
|
|
57
|
+
if (queued.status !== 'success' && queued.status !== 'error') return
|
|
58
|
+
|
|
59
|
+
listeners.delete(listener)
|
|
60
|
+
|
|
61
|
+
if (queued.status === 'success') resolve(queued.result)
|
|
62
|
+
else reject(new ox_Provider.UserRejectedRequestError({ message: queued.error.message }))
|
|
63
|
+
|
|
64
|
+
// Remove the resolved request from the queue.
|
|
65
|
+
store.setState((x) => ({
|
|
66
|
+
...x,
|
|
67
|
+
requestQueue: x.requestQueue.filter((x) => x.request.id !== requestId),
|
|
68
|
+
}))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
listeners.add(listener)
|
|
72
|
+
|
|
73
|
+
// Notify immediately with current state so the store subscription
|
|
74
|
+
// picks up the request that was just added (setState fires
|
|
75
|
+
// synchronously before this listener is registered).
|
|
76
|
+
listener(store.getState().requestQueue)
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* An ox provider that queues RPC requests in the store. The store
|
|
82
|
+
* subscription syncs the pending queue to the dialog via `syncRequests`.
|
|
83
|
+
*/
|
|
84
|
+
const provider = ox_Provider.from(
|
|
85
|
+
{
|
|
86
|
+
async request(r) {
|
|
87
|
+
const request = requestStore.prepare(r as never)
|
|
88
|
+
|
|
89
|
+
store.setState((x) => ({
|
|
90
|
+
...x,
|
|
91
|
+
requestQueue: [...x.requestQueue, { request, status: 'pending' as const }],
|
|
92
|
+
}))
|
|
93
|
+
|
|
94
|
+
return waitForQueuedRequest(request.id)
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{ schema: Schema.ox },
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Prepares a local key pair when `authorizeAccessKey` is requested without
|
|
102
|
+
* an external publicKey/address, and returns the params to inject into the
|
|
103
|
+
* RPC request so the dialog signs the authorization.
|
|
104
|
+
*/
|
|
105
|
+
async function generateAccessKey(options: Adapter.authorizeAccessKey.Parameters | undefined) {
|
|
106
|
+
if (!options) return undefined
|
|
107
|
+
if (options.publicKey || options.address) return undefined
|
|
108
|
+
|
|
109
|
+
const { accessKey, keyPair } = await AccessKey.generate()
|
|
110
|
+
return {
|
|
111
|
+
accessKey,
|
|
112
|
+
keyPair,
|
|
113
|
+
request: {
|
|
114
|
+
...options,
|
|
115
|
+
publicKey: accessKey.publicKey,
|
|
116
|
+
keyType: 'p256' as const,
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* After the dialog returns a signed key authorization, saves the local
|
|
123
|
+
* key pair + key authorization into the store.
|
|
124
|
+
*/
|
|
125
|
+
function saveAccessKey(
|
|
126
|
+
address: Address.Address,
|
|
127
|
+
keyAuth: KeyAuthorization.Rpc,
|
|
128
|
+
keyPair: AccessKey.generate.ReturnType['keyPair'],
|
|
129
|
+
) {
|
|
130
|
+
const keyAuthorization = KeyAuthorization.fromRpc(keyAuth)
|
|
131
|
+
AccessKey.save({ address, keyAuthorization, keyPair, store })
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Tries to execute `fn` with the local access key. Returns `undefined`
|
|
136
|
+
* when no access key exists so the caller can fall through to the dialog.
|
|
137
|
+
* On access key errors, removes the stale key and also returns `undefined`.
|
|
138
|
+
*/
|
|
139
|
+
async function withAccessKey<result>(
|
|
140
|
+
fn: (
|
|
141
|
+
account: TempoAccount.Account,
|
|
142
|
+
keyAuthorization?: KeyAuthorization.Signed,
|
|
143
|
+
) => Promise<result>,
|
|
144
|
+
): Promise<result | undefined> {
|
|
145
|
+
const account = (() => {
|
|
146
|
+
try {
|
|
147
|
+
return getAccount({ signable: true })
|
|
148
|
+
} catch {
|
|
149
|
+
return undefined
|
|
150
|
+
}
|
|
151
|
+
})()
|
|
152
|
+
if (!account) return undefined
|
|
153
|
+
if (account.source !== 'accessKey') return undefined
|
|
154
|
+
const keyAuthorization = AccessKey.getPending(account, { store })
|
|
155
|
+
try {
|
|
156
|
+
const result = await fn(account, keyAuthorization ?? undefined)
|
|
157
|
+
AccessKey.removePending(account, { store })
|
|
158
|
+
return result
|
|
159
|
+
} catch {
|
|
160
|
+
AccessKey.remove(account, { store })
|
|
161
|
+
return undefined
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const dialogInstance = dialog({ host, store })
|
|
166
|
+
|
|
167
|
+
// Sync store → dialog: whenever the request queue changes, notify
|
|
168
|
+
// listeners and sync pending requests to the dialog.
|
|
169
|
+
const unsubscribe = store.subscribe(
|
|
170
|
+
(x) => x.requestQueue,
|
|
171
|
+
(requestQueue) => {
|
|
172
|
+
for (const listener of listeners) listener(requestQueue)
|
|
173
|
+
|
|
174
|
+
const pending = requestQueue.filter(
|
|
175
|
+
(x): x is Store.QueuedRequest & { status: 'pending' } => x.status === 'pending',
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
dialogInstance?.syncRequests(pending)
|
|
179
|
+
if (pending.length === 0) dialogInstance?.close()
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
cleanup() {
|
|
185
|
+
unsubscribe()
|
|
186
|
+
dialogInstance?.destroy()
|
|
187
|
+
},
|
|
188
|
+
actions: {
|
|
189
|
+
async createAccount(parameters, request) {
|
|
190
|
+
const accessKey = await generateAccessKey(parameters.authorizeAccessKey)
|
|
191
|
+
|
|
192
|
+
const { accounts } = await provider.request({
|
|
193
|
+
...request,
|
|
194
|
+
params: [
|
|
195
|
+
{
|
|
196
|
+
...request.params?.[0],
|
|
197
|
+
capabilities: {
|
|
198
|
+
...request.params?.[0]?.capabilities,
|
|
199
|
+
...(accessKey
|
|
200
|
+
? {
|
|
201
|
+
authorizeAccessKey: z.encode(
|
|
202
|
+
Rpc.wallet_connect.authorizeAccessKey,
|
|
203
|
+
accessKey.request,
|
|
204
|
+
),
|
|
205
|
+
}
|
|
206
|
+
: {}),
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
] as const,
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
const address = accounts[0]?.address
|
|
213
|
+
const keyAuthorization = accounts[0]?.capabilities.keyAuthorization
|
|
214
|
+
|
|
215
|
+
if (accessKey && address && keyAuthorization)
|
|
216
|
+
saveAccessKey(address, keyAuthorization, accessKey.keyPair)
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
accounts: accounts.map((a) => ({ address: a.address })),
|
|
220
|
+
...(keyAuthorization ? { keyAuthorization } : {}),
|
|
221
|
+
...(accounts[0]?.capabilities.signature
|
|
222
|
+
? { signature: accounts[0].capabilities.signature }
|
|
223
|
+
: {}),
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
async loadAccounts(parameters, request) {
|
|
228
|
+
const accessKey = await generateAccessKey(parameters?.authorizeAccessKey)
|
|
229
|
+
|
|
230
|
+
const { accounts } = await provider.request({
|
|
231
|
+
...request,
|
|
232
|
+
params: [
|
|
233
|
+
{
|
|
234
|
+
...request.params?.[0],
|
|
235
|
+
capabilities: {
|
|
236
|
+
...request.params?.[0]?.capabilities,
|
|
237
|
+
...(accessKey
|
|
238
|
+
? {
|
|
239
|
+
authorizeAccessKey: z.encode(
|
|
240
|
+
Rpc.wallet_connect.authorizeAccessKey,
|
|
241
|
+
accessKey.request,
|
|
242
|
+
),
|
|
243
|
+
}
|
|
244
|
+
: {}),
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
] as const,
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
const address = accounts[0]?.address
|
|
251
|
+
const keyAuthorization = accounts[0]?.capabilities.keyAuthorization
|
|
252
|
+
|
|
253
|
+
if (accessKey && address && keyAuthorization)
|
|
254
|
+
saveAccessKey(address, keyAuthorization, accessKey.keyPair)
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
accounts: accounts.map((a) => ({ address: a.address })),
|
|
258
|
+
...(keyAuthorization ? { keyAuthorization } : {}),
|
|
259
|
+
...(accounts[0]?.capabilities.signature
|
|
260
|
+
? { signature: accounts[0].capabilities.signature }
|
|
261
|
+
: {}),
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
|
|
265
|
+
async signPersonalMessage(_params, request) {
|
|
266
|
+
return await provider.request(request)
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
async signTransaction(parameters, request) {
|
|
270
|
+
const result = await withAccessKey(async (account, keyAuthorization) => {
|
|
271
|
+
const { feePayer, ...rest } = parameters
|
|
272
|
+
const client = getClient({
|
|
273
|
+
feePayer: typeof feePayer === 'string' ? feePayer : undefined,
|
|
274
|
+
})
|
|
275
|
+
const prepared = await prepareTransactionRequest(client, {
|
|
276
|
+
account,
|
|
277
|
+
...rest,
|
|
278
|
+
...(feePayer ? { feePayer: true } : {}),
|
|
279
|
+
keyAuthorization,
|
|
280
|
+
type: 'tempo',
|
|
281
|
+
})
|
|
282
|
+
return await account.signTransaction(prepared as never)
|
|
283
|
+
})
|
|
284
|
+
if (result !== undefined) return result
|
|
285
|
+
return await provider.request(request)
|
|
286
|
+
},
|
|
287
|
+
|
|
288
|
+
async signTypedData(_params, request) {
|
|
289
|
+
return await provider.request(request)
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
async sendTransaction(parameters, request) {
|
|
293
|
+
const result = await withAccessKey(async (account, keyAuthorization) => {
|
|
294
|
+
const { feePayer, ...rest } = parameters
|
|
295
|
+
const client = getClient({
|
|
296
|
+
feePayer: typeof feePayer === 'string' ? feePayer : undefined,
|
|
297
|
+
})
|
|
298
|
+
const prepared = await prepareTransactionRequest(client, {
|
|
299
|
+
account,
|
|
300
|
+
...rest,
|
|
301
|
+
...(feePayer ? { feePayer: true } : {}),
|
|
302
|
+
keyAuthorization,
|
|
303
|
+
type: 'tempo',
|
|
304
|
+
})
|
|
305
|
+
const signed = await account.signTransaction(prepared as never)
|
|
306
|
+
return await client.request({
|
|
307
|
+
method: 'eth_sendRawTransaction' as never,
|
|
308
|
+
params: [signed],
|
|
309
|
+
})
|
|
310
|
+
})
|
|
311
|
+
if (result !== undefined) return result
|
|
312
|
+
return await provider.request(request)
|
|
313
|
+
},
|
|
314
|
+
|
|
315
|
+
async sendTransactionSync(parameters, request) {
|
|
316
|
+
const result = await withAccessKey(async (account, keyAuthorization) => {
|
|
317
|
+
const { feePayer, ...rest } = parameters
|
|
318
|
+
const client = getClient({
|
|
319
|
+
feePayer: typeof feePayer === 'string' ? feePayer : undefined,
|
|
320
|
+
})
|
|
321
|
+
const prepared = await prepareTransactionRequest(client, {
|
|
322
|
+
account,
|
|
323
|
+
...rest,
|
|
324
|
+
...(feePayer ? { feePayer: true } : {}),
|
|
325
|
+
keyAuthorization,
|
|
326
|
+
type: 'tempo',
|
|
327
|
+
})
|
|
328
|
+
const signed = await account.signTransaction(prepared as never)
|
|
329
|
+
return await client.request({
|
|
330
|
+
method: 'eth_sendRawTransactionSync' as never,
|
|
331
|
+
params: [signed],
|
|
332
|
+
})
|
|
333
|
+
})
|
|
334
|
+
if (result !== undefined) return result
|
|
335
|
+
return await provider.request(request)
|
|
336
|
+
},
|
|
337
|
+
|
|
338
|
+
async authorizeAccessKey(parameters, request) {
|
|
339
|
+
const accessKey = await generateAccessKey(parameters)
|
|
340
|
+
|
|
341
|
+
const result = await provider.request({
|
|
342
|
+
...request,
|
|
343
|
+
params: [z.encode(Rpc.wallet_connect.authorizeAccessKey, accessKey!.request)!],
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
if (accessKey) {
|
|
347
|
+
const account = getAccount({ accessKey: false, signable: false })
|
|
348
|
+
saveAccessKey(account.address, result, accessKey.keyPair)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return result
|
|
352
|
+
},
|
|
353
|
+
|
|
354
|
+
async revokeAccessKey(_params, request) {
|
|
355
|
+
await provider.request(request)
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
async disconnect() {
|
|
359
|
+
store.setState({ accessKeys: [], accounts: [], activeAccount: 0 })
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
}
|
|
363
|
+
})
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export declare namespace dialog {
|
|
367
|
+
type Options = {
|
|
368
|
+
/** Dialog to use for the embed app. @default `Dialog.iframe()` (or `Dialog.popup()` in Safari) */
|
|
369
|
+
dialog?: Dialog.Dialog | undefined
|
|
370
|
+
/** URL of the embed app. @default `'https://wallet.tempo.xyz/embed'` */
|
|
371
|
+
host?: string | undefined
|
|
372
|
+
/** Data URI of the provider icon. */
|
|
373
|
+
icon?: `data:image/${string}` | undefined
|
|
374
|
+
/** Display name of the provider. @default `'Tempo'` */
|
|
375
|
+
name?: string | undefined
|
|
376
|
+
/** Reverse DNS identifier. @default `'xyz.tempo'` */
|
|
377
|
+
rdns?: string | undefined
|
|
378
|
+
}
|
|
379
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { tempoLocalnet } from 'viem/chains'
|
|
2
|
+
import { describe, expect, test } from 'vp/test'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
accounts as core_accounts,
|
|
6
|
+
getClient,
|
|
7
|
+
privateKeys,
|
|
8
|
+
webAuthnAccounts,
|
|
9
|
+
} from '../../../test/config.js'
|
|
10
|
+
import * as Account from '../Account.js'
|
|
11
|
+
import * as Storage from '../Storage.js'
|
|
12
|
+
import * as Store from '../Store.js'
|
|
13
|
+
import { local } from './local.js'
|
|
14
|
+
|
|
15
|
+
describe('local', () => {
|
|
16
|
+
describe('loadAccounts', () => {
|
|
17
|
+
test('default: loads accounts', async () => {
|
|
18
|
+
const { adapter } = setup()
|
|
19
|
+
|
|
20
|
+
const { accounts } = await adapter.actions.loadAccounts(undefined, {
|
|
21
|
+
method: 'wallet_connect',
|
|
22
|
+
params: undefined,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
expect(accounts.map((a) => a.address)).toMatchInlineSnapshot(`
|
|
26
|
+
[
|
|
27
|
+
"0x1ecBa262e4510F333FB5051743e2a53a765deBD0",
|
|
28
|
+
]
|
|
29
|
+
`)
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
describe('createAccount', () => {
|
|
34
|
+
test('default: creates account', async () => {
|
|
35
|
+
const { adapter } = setup({
|
|
36
|
+
createAccount: async () => ({
|
|
37
|
+
accounts: [
|
|
38
|
+
{
|
|
39
|
+
address: core_accounts[1].address,
|
|
40
|
+
keyType: 'secp256k1',
|
|
41
|
+
privateKey: privateKeys[1],
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
}),
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const { accounts } = await adapter.actions.createAccount(
|
|
48
|
+
{ name: 'test' },
|
|
49
|
+
{ method: 'wallet_connect', params: undefined },
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
expect(accounts.map((a) => a.address)).toMatchInlineSnapshot(`
|
|
53
|
+
[
|
|
54
|
+
"0x8C8d35429F74ec245F8Ef2f4Fd1e551cFF97d650",
|
|
55
|
+
]
|
|
56
|
+
`)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('error: throws when createAccount not configured', async () => {
|
|
60
|
+
const { adapter } = setup()
|
|
61
|
+
|
|
62
|
+
await expect(
|
|
63
|
+
adapter.actions.createAccount(
|
|
64
|
+
{ name: 'test' },
|
|
65
|
+
{ method: 'wallet_connect', params: undefined },
|
|
66
|
+
),
|
|
67
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
68
|
+
`[Provider.UnsupportedMethodError: \`createAccount\` not configured on adapter.]`,
|
|
69
|
+
)
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
function setup(overrides: Partial<local.Options> = {}) {
|
|
75
|
+
const storage = Storage.memory()
|
|
76
|
+
const store = Store.create({ chainId: tempoLocalnet.id, storage })
|
|
77
|
+
const adapter = local({
|
|
78
|
+
loadAccounts: async () => ({
|
|
79
|
+
accounts: [
|
|
80
|
+
{
|
|
81
|
+
address: webAuthnAccounts[0]!.address,
|
|
82
|
+
keyType: 'webAuthn_headless' as const,
|
|
83
|
+
privateKey: privateKeys[0]!,
|
|
84
|
+
rpId: 'example.com',
|
|
85
|
+
origin: 'https://example.com',
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
}),
|
|
89
|
+
...overrides,
|
|
90
|
+
})({
|
|
91
|
+
getAccount: (options) => Account.find({ ...options, signable: true, store }),
|
|
92
|
+
getClient: () => getClient({ chain: tempoLocalnet }) as never,
|
|
93
|
+
storage,
|
|
94
|
+
store,
|
|
95
|
+
})
|
|
96
|
+
return { adapter, store }
|
|
97
|
+
}
|