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.
- package/CHANGELOG.md +26 -0
- package/README.md +28 -34
- package/package.json +2 -28
- package/dist/wagmi/Actions/amm.d.ts +0 -418
- package/dist/wagmi/Actions/amm.d.ts.map +0 -1
- package/dist/wagmi/Actions/amm.js +0 -462
- package/dist/wagmi/Actions/amm.js.map +0 -1
- package/dist/wagmi/Actions/dex.d.ts +0 -864
- package/dist/wagmi/Actions/dex.d.ts.map +0 -1
- package/dist/wagmi/Actions/dex.js +0 -968
- package/dist/wagmi/Actions/dex.js.map +0 -1
- package/dist/wagmi/Actions/faucet.d.ts +0 -66
- package/dist/wagmi/Actions/faucet.d.ts.map +0 -1
- package/dist/wagmi/Actions/faucet.js +0 -64
- package/dist/wagmi/Actions/faucet.js.map +0 -1
- package/dist/wagmi/Actions/fee.d.ts +0 -111
- package/dist/wagmi/Actions/fee.d.ts.map +0 -1
- package/dist/wagmi/Actions/fee.js +0 -126
- package/dist/wagmi/Actions/fee.js.map +0 -1
- package/dist/wagmi/Actions/index.d.ts +0 -9
- package/dist/wagmi/Actions/index.d.ts.map +0 -1
- package/dist/wagmi/Actions/index.js +0 -9
- package/dist/wagmi/Actions/index.js.map +0 -1
- package/dist/wagmi/Actions/nonce.d.ts +0 -77
- package/dist/wagmi/Actions/nonce.d.ts.map +0 -1
- package/dist/wagmi/Actions/nonce.js +0 -87
- package/dist/wagmi/Actions/nonce.js.map +0 -1
- package/dist/wagmi/Actions/policy.d.ts +0 -480
- package/dist/wagmi/Actions/policy.d.ts.map +0 -1
- package/dist/wagmi/Actions/policy.js +0 -530
- package/dist/wagmi/Actions/policy.js.map +0 -1
- package/dist/wagmi/Actions/reward.d.ts +0 -346
- package/dist/wagmi/Actions/reward.d.ts.map +0 -1
- package/dist/wagmi/Actions/reward.js +0 -382
- package/dist/wagmi/Actions/reward.js.map +0 -1
- package/dist/wagmi/Actions/token.d.ts +0 -1546
- package/dist/wagmi/Actions/token.d.ts.map +0 -1
- package/dist/wagmi/Actions/token.js +0 -1712
- package/dist/wagmi/Actions/token.js.map +0 -1
- package/dist/wagmi/Connector.d.ts +0 -91
- package/dist/wagmi/Connector.d.ts.map +0 -1
- package/dist/wagmi/Connector.js +0 -473
- package/dist/wagmi/Connector.js.map +0 -1
- package/dist/wagmi/Hooks/amm.d.ts +0 -411
- package/dist/wagmi/Hooks/amm.d.ts.map +0 -1
- package/dist/wagmi/Hooks/amm.js +0 -494
- package/dist/wagmi/Hooks/amm.js.map +0 -1
- package/dist/wagmi/Hooks/dex.d.ts +0 -773
- package/dist/wagmi/Hooks/dex.d.ts.map +0 -1
- package/dist/wagmi/Hooks/dex.js +0 -921
- package/dist/wagmi/Hooks/dex.js.map +0 -1
- package/dist/wagmi/Hooks/faucet.d.ts +0 -71
- package/dist/wagmi/Hooks/faucet.d.ts.map +0 -1
- package/dist/wagmi/Hooks/faucet.js +0 -76
- package/dist/wagmi/Hooks/faucet.js.map +0 -1
- package/dist/wagmi/Hooks/fee.d.ts +0 -97
- package/dist/wagmi/Hooks/fee.d.ts.map +0 -1
- package/dist/wagmi/Hooks/fee.js +0 -109
- package/dist/wagmi/Hooks/fee.js.map +0 -1
- package/dist/wagmi/Hooks/index.d.ts +0 -9
- package/dist/wagmi/Hooks/index.d.ts.map +0 -1
- package/dist/wagmi/Hooks/index.js +0 -9
- package/dist/wagmi/Hooks/index.js.map +0 -1
- package/dist/wagmi/Hooks/nonce.d.ts +0 -59
- package/dist/wagmi/Hooks/nonce.d.ts.map +0 -1
- package/dist/wagmi/Hooks/nonce.js +0 -75
- package/dist/wagmi/Hooks/nonce.js.map +0 -1
- package/dist/wagmi/Hooks/policy.d.ts +0 -423
- package/dist/wagmi/Hooks/policy.d.ts.map +0 -1
- package/dist/wagmi/Hooks/policy.js +0 -510
- package/dist/wagmi/Hooks/policy.js.map +0 -1
- package/dist/wagmi/Hooks/reward.d.ts +0 -305
- package/dist/wagmi/Hooks/reward.d.ts.map +0 -1
- package/dist/wagmi/Hooks/reward.js +0 -368
- package/dist/wagmi/Hooks/reward.js.map +0 -1
- package/dist/wagmi/Hooks/token.d.ts +0 -1388
- package/dist/wagmi/Hooks/token.d.ts.map +0 -1
- package/dist/wagmi/Hooks/token.js +0 -1657
- package/dist/wagmi/Hooks/token.js.map +0 -1
- package/dist/wagmi/KeyManager.d.ts +0 -60
- package/dist/wagmi/KeyManager.d.ts.map +0 -1
- package/dist/wagmi/KeyManager.js +0 -106
- package/dist/wagmi/KeyManager.js.map +0 -1
- package/dist/wagmi/index.d.ts +0 -5
- package/dist/wagmi/index.d.ts.map +0 -1
- package/dist/wagmi/index.js +0 -5
- package/dist/wagmi/index.js.map +0 -1
- package/src/wagmi/Actions/amm.test.ts +0 -208
- package/src/wagmi/Actions/amm.ts +0 -690
- package/src/wagmi/Actions/dex.test.ts +0 -1482
- package/src/wagmi/Actions/dex.ts +0 -1540
- package/src/wagmi/Actions/faucet.ts +0 -89
- package/src/wagmi/Actions/fee.test.ts +0 -63
- package/src/wagmi/Actions/fee.ts +0 -211
- package/src/wagmi/Actions/index.ts +0 -8
- package/src/wagmi/Actions/nonce.test.ts +0 -82
- package/src/wagmi/Actions/nonce.ts +0 -139
- package/src/wagmi/Actions/policy.test.ts +0 -461
- package/src/wagmi/Actions/policy.ts +0 -817
- package/src/wagmi/Actions/reward.test.ts +0 -216
- package/src/wagmi/Actions/reward.ts +0 -613
- package/src/wagmi/Actions/token.test.ts +0 -1309
- package/src/wagmi/Actions/token.ts +0 -2644
- package/src/wagmi/Connector.test.ts +0 -56
- package/src/wagmi/Connector.ts +0 -670
- package/src/wagmi/Hooks/amm.test.ts +0 -564
- package/src/wagmi/Hooks/amm.ts +0 -796
- package/src/wagmi/Hooks/dex.test.ts +0 -992
- package/src/wagmi/Hooks/dex.ts +0 -1598
- package/src/wagmi/Hooks/faucet.ts +0 -144
- package/src/wagmi/Hooks/fee.test.ts +0 -166
- package/src/wagmi/Hooks/fee.ts +0 -206
- package/src/wagmi/Hooks/index.ts +0 -8
- package/src/wagmi/Hooks/nonce.test.ts +0 -142
- package/src/wagmi/Hooks/nonce.ts +0 -117
- package/src/wagmi/Hooks/policy.test.ts +0 -665
- package/src/wagmi/Hooks/policy.ts +0 -873
- package/src/wagmi/Hooks/reward.test.ts +0 -249
- package/src/wagmi/Hooks/reward.ts +0 -645
- package/src/wagmi/Hooks/token.test.ts +0 -1183
- package/src/wagmi/Hooks/token.ts +0 -2906
- package/src/wagmi/KeyManager.ts +0 -172
- package/src/wagmi/index.ts +0 -7
package/src/wagmi/Connector.ts
DELETED
|
@@ -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
|
-
}
|