tempo.ts 0.9.0 → 0.10.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 +14 -0
- package/dist/ox/AuthorizationTempo.d.ts +450 -0
- package/dist/ox/AuthorizationTempo.d.ts.map +1 -0
- package/dist/ox/AuthorizationTempo.js +433 -0
- package/dist/ox/AuthorizationTempo.js.map +1 -0
- package/dist/ox/KeyAuthorization.d.ts +1 -1
- package/dist/ox/KeyAuthorization.d.ts.map +1 -1
- package/dist/ox/KeyAuthorization.js +3 -4
- package/dist/ox/KeyAuthorization.js.map +1 -1
- package/dist/ox/Transaction.d.ts +4 -3
- package/dist/ox/Transaction.d.ts.map +1 -1
- package/dist/ox/Transaction.js +7 -0
- package/dist/ox/Transaction.js.map +1 -1
- package/dist/ox/TransactionEnvelopeTempo.d.ts +3 -3
- package/dist/ox/TransactionEnvelopeTempo.d.ts.map +1 -1
- package/dist/ox/TransactionEnvelopeTempo.js +8 -4
- package/dist/ox/TransactionEnvelopeTempo.js.map +1 -1
- package/dist/ox/TransactionRequest.d.ts +6 -4
- package/dist/ox/TransactionRequest.d.ts.map +1 -1
- package/dist/ox/TransactionRequest.js +7 -1
- package/dist/ox/TransactionRequest.js.map +1 -1
- package/dist/ox/index.d.ts +1 -0
- package/dist/ox/index.d.ts.map +1 -1
- package/dist/ox/index.js +1 -0
- package/dist/ox/index.js.map +1 -1
- package/dist/viem/Chain.d.ts +8 -3
- package/dist/viem/Chain.d.ts.map +1 -1
- package/dist/viem/Chain.js +1 -0
- package/dist/viem/Chain.js.map +1 -1
- package/dist/viem/Formatters.d.ts.map +1 -1
- package/dist/viem/Formatters.js +0 -13
- package/dist/viem/Formatters.js.map +1 -1
- package/dist/viem/Transaction.d.ts +5 -5
- package/dist/viem/Transaction.d.ts.map +1 -1
- package/dist/viem/Transaction.js +2 -15
- package/dist/viem/Transaction.js.map +1 -1
- package/dist/wagmi/Connector.d.ts +6 -12
- package/dist/wagmi/Connector.d.ts.map +1 -1
- package/dist/wagmi/Connector.js +73 -18
- package/dist/wagmi/Connector.js.map +1 -1
- package/package.json +1 -1
- package/src/ox/AuthorizationTempo.test.ts +1256 -0
- package/src/ox/AuthorizationTempo.ts +648 -0
- package/src/ox/KeyAuthorization.ts +5 -7
- package/src/ox/Transaction.ts +14 -3
- package/src/ox/TransactionEnvelopeTempo.test.ts +172 -2
- package/src/ox/TransactionEnvelopeTempo.ts +15 -5
- package/src/ox/TransactionRequest.ts +19 -5
- package/src/ox/e2e.test.ts +80 -8
- package/src/ox/index.ts +1 -0
- package/src/viem/Chain.ts +1 -0
- package/src/viem/Formatters.ts +0 -13
- package/src/viem/Transaction.ts +8 -29
- package/src/viem/e2e.test.ts +14 -28
- package/src/wagmi/Connector.ts +104 -31
package/src/viem/Transaction.ts
CHANGED
|
@@ -7,10 +7,8 @@ import {
|
|
|
7
7
|
type AccessList,
|
|
8
8
|
type Account,
|
|
9
9
|
type Address,
|
|
10
|
-
type AuthorizationList,
|
|
11
10
|
type FeeValuesEIP1559,
|
|
12
11
|
type ParseTransactionReturnType,
|
|
13
|
-
type SignedAuthorizationList,
|
|
14
12
|
type TransactionBase,
|
|
15
13
|
type TransactionRequestBase,
|
|
16
14
|
type TransactionSerializableBase,
|
|
@@ -29,6 +27,7 @@ import {
|
|
|
29
27
|
type TransactionType as viem_TransactionType,
|
|
30
28
|
} from 'viem'
|
|
31
29
|
import type { ExactPartial, OneOf, PartialBy } from '../internal/types.js'
|
|
30
|
+
import type * as AuthorizationTempo from '../ox/AuthorizationTempo.js'
|
|
32
31
|
import type * as KeyAuthorization from '../ox/KeyAuthorization.js'
|
|
33
32
|
import * as SignatureEnvelope from '../ox/SignatureEnvelope.js'
|
|
34
33
|
import * as TxTempo from '../ox/TransactionEnvelopeTempo.js'
|
|
@@ -46,8 +45,9 @@ export type TransactionRpc<pending extends boolean = false> = OneOf<
|
|
|
46
45
|
| viem_RpcTransaction<pending>
|
|
47
46
|
| (Omit<
|
|
48
47
|
TransactionTempo<Hex.Hex, Hex.Hex, pending, '0x76'>,
|
|
49
|
-
'keyAuthorization' | 'signature'
|
|
48
|
+
'authorizationList' | 'keyAuthorization' | 'signature'
|
|
50
49
|
> & {
|
|
50
|
+
authorizationList?: AuthorizationTempo.ListRpc | undefined
|
|
51
51
|
keyAuthorization?: KeyAuthorization.Rpc | null | undefined
|
|
52
52
|
signature: SignatureEnvelope.SignatureEnvelopeRpc
|
|
53
53
|
})
|
|
@@ -66,7 +66,7 @@ export type TransactionTempo<
|
|
|
66
66
|
'r' | 's' | 'v' | 'yParity'
|
|
67
67
|
> & {
|
|
68
68
|
accessList: AccessList
|
|
69
|
-
authorizationList?:
|
|
69
|
+
authorizationList?: AuthorizationTempo.ListSigned<quantity, index> | undefined
|
|
70
70
|
calls: readonly TxTempo.Call<quantity>[]
|
|
71
71
|
chainId: index
|
|
72
72
|
feeToken?: Address | undefined
|
|
@@ -114,7 +114,6 @@ export type TransactionRequestTempo<
|
|
|
114
114
|
> = TransactionRequestBase<quantity, index, type> &
|
|
115
115
|
ExactPartial<FeeValuesEIP1559<quantity>> & {
|
|
116
116
|
accessList?: AccessList | undefined
|
|
117
|
-
authorizationList?: AuthorizationList<index, boolean> | undefined
|
|
118
117
|
keyAuthorization?: KeyAuthorization.Signed<quantity, index> | undefined
|
|
119
118
|
calls?: readonly TxTempo.Call<quantity>[] | undefined
|
|
120
119
|
feePayer?: Account | true | undefined
|
|
@@ -134,7 +133,6 @@ export type TransactionSerializableTempo<
|
|
|
134
133
|
> = TransactionSerializableBase<quantity, index> &
|
|
135
134
|
ExactPartial<FeeValuesEIP1559<quantity>> & {
|
|
136
135
|
accessList?: AccessList | undefined
|
|
137
|
-
authorizationList?: SignedAuthorizationList | undefined
|
|
138
136
|
calls: readonly TxTempo.Call<quantity>[]
|
|
139
137
|
chainId: number
|
|
140
138
|
feeToken?: Address | bigint | undefined
|
|
@@ -252,16 +250,11 @@ export async function serialize(
|
|
|
252
250
|
function deserializeTempo(
|
|
253
251
|
serializedTransaction: TransactionSerializedTempo,
|
|
254
252
|
): TransactionSerializableTempo {
|
|
255
|
-
const {
|
|
256
|
-
|
|
253
|
+
const { feePayerSignature, nonce, ...tx } = TxTempo.deserialize(
|
|
254
|
+
serializedTransaction,
|
|
255
|
+
)
|
|
257
256
|
return {
|
|
258
257
|
...tx,
|
|
259
|
-
authorizationList: authorizationList?.map((auth) => ({
|
|
260
|
-
...auth,
|
|
261
|
-
nonce: Number(auth.nonce ?? 0n),
|
|
262
|
-
r: Hex.fromNumber(auth.r, { size: 32 }),
|
|
263
|
-
s: Hex.fromNumber(auth.s, { size: 32 }),
|
|
264
|
-
})),
|
|
265
258
|
nonce: Number(nonce ?? 0n),
|
|
266
259
|
feePayerSignature: feePayerSignature
|
|
267
260
|
? {
|
|
@@ -293,24 +286,10 @@ async function serializeTempo(
|
|
|
293
286
|
return undefined
|
|
294
287
|
})()
|
|
295
288
|
|
|
296
|
-
const {
|
|
297
|
-
authorizationList,
|
|
298
|
-
chainId,
|
|
299
|
-
feePayer,
|
|
300
|
-
feePayerSignature,
|
|
301
|
-
nonce,
|
|
302
|
-
...rest
|
|
303
|
-
} = transaction
|
|
289
|
+
const { chainId, feePayer, feePayerSignature, nonce, ...rest } = transaction
|
|
304
290
|
|
|
305
291
|
const transaction_ox = {
|
|
306
292
|
...rest,
|
|
307
|
-
authorizationList: authorizationList?.map((auth) => ({
|
|
308
|
-
...auth,
|
|
309
|
-
nonce: BigInt(auth.nonce),
|
|
310
|
-
r: BigInt(auth.r!),
|
|
311
|
-
s: BigInt(auth.s!),
|
|
312
|
-
yParity: Number(auth.yParity),
|
|
313
|
-
})),
|
|
314
293
|
calls: rest.calls?.length
|
|
315
294
|
? rest.calls
|
|
316
295
|
: [
|
package/src/viem/e2e.test.ts
CHANGED
|
@@ -68,9 +68,8 @@ describe('sendTransaction', () => {
|
|
|
68
68
|
expect(transactionIndex).toBeDefined()
|
|
69
69
|
expect(transaction).toMatchInlineSnapshot(`
|
|
70
70
|
{
|
|
71
|
-
"aaAuthorizationList": [],
|
|
72
71
|
"accessList": [],
|
|
73
|
-
"authorizationList":
|
|
72
|
+
"authorizationList": [],
|
|
74
73
|
"calls": [
|
|
75
74
|
{
|
|
76
75
|
"data": "0xdeadbeef",
|
|
@@ -145,9 +144,8 @@ describe('sendTransaction', () => {
|
|
|
145
144
|
expect(transactionIndex).toBeDefined()
|
|
146
145
|
expect(transaction).toMatchInlineSnapshot(`
|
|
147
146
|
{
|
|
148
|
-
"aaAuthorizationList": [],
|
|
149
147
|
"accessList": [],
|
|
150
|
-
"authorizationList":
|
|
148
|
+
"authorizationList": [],
|
|
151
149
|
"data": undefined,
|
|
152
150
|
"feePayerSignature": undefined,
|
|
153
151
|
"feeToken": "0x20c0000000000000000000000000000000000001",
|
|
@@ -210,9 +208,8 @@ describe('sendTransaction', () => {
|
|
|
210
208
|
expect(transactionIndex).toBeDefined()
|
|
211
209
|
expect(transaction).toMatchInlineSnapshot(`
|
|
212
210
|
{
|
|
213
|
-
"aaAuthorizationList": [],
|
|
214
211
|
"accessList": [],
|
|
215
|
-
"authorizationList":
|
|
212
|
+
"authorizationList": [],
|
|
216
213
|
"calls": [
|
|
217
214
|
{
|
|
218
215
|
"data": "0x",
|
|
@@ -325,9 +322,8 @@ describe('sendTransaction', () => {
|
|
|
325
322
|
expect(transactionIndex).toBeDefined()
|
|
326
323
|
expect(transaction).toMatchInlineSnapshot(`
|
|
327
324
|
{
|
|
328
|
-
"aaAuthorizationList": [],
|
|
329
325
|
"accessList": [],
|
|
330
|
-
"authorizationList":
|
|
326
|
+
"authorizationList": [],
|
|
331
327
|
"calls": [
|
|
332
328
|
{
|
|
333
329
|
"data": "0xdeadbeef",
|
|
@@ -410,9 +406,8 @@ describe('sendTransaction', () => {
|
|
|
410
406
|
expect(transactionIndex).toBeDefined()
|
|
411
407
|
expect(transaction).toMatchInlineSnapshot(`
|
|
412
408
|
{
|
|
413
|
-
"aaAuthorizationList": [],
|
|
414
409
|
"accessList": [],
|
|
415
|
-
"authorizationList":
|
|
410
|
+
"authorizationList": [],
|
|
416
411
|
"data": undefined,
|
|
417
412
|
"feePayerSignature": undefined,
|
|
418
413
|
"feeToken": "0x20c0000000000000000000000000000000000001",
|
|
@@ -478,9 +473,8 @@ describe('sendTransaction', () => {
|
|
|
478
473
|
expect(transactionIndex).toBeDefined()
|
|
479
474
|
expect(transaction).toMatchInlineSnapshot(`
|
|
480
475
|
{
|
|
481
|
-
"aaAuthorizationList": [],
|
|
482
476
|
"accessList": [],
|
|
483
|
-
"authorizationList":
|
|
477
|
+
"authorizationList": [],
|
|
484
478
|
"calls": [
|
|
485
479
|
{
|
|
486
480
|
"data": "0x",
|
|
@@ -608,9 +602,8 @@ describe('sendTransaction', () => {
|
|
|
608
602
|
expect(transactionIndex).toBeDefined()
|
|
609
603
|
expect(transaction).toMatchInlineSnapshot(`
|
|
610
604
|
{
|
|
611
|
-
"aaAuthorizationList": [],
|
|
612
605
|
"accessList": [],
|
|
613
|
-
"authorizationList":
|
|
606
|
+
"authorizationList": [],
|
|
614
607
|
"calls": [
|
|
615
608
|
{
|
|
616
609
|
"data": "0xdeadbeef",
|
|
@@ -814,9 +807,8 @@ describe('sendTransaction', () => {
|
|
|
814
807
|
expect(transactionIndex).toBeDefined()
|
|
815
808
|
expect(transaction).toMatchInlineSnapshot(`
|
|
816
809
|
{
|
|
817
|
-
"aaAuthorizationList": [],
|
|
818
810
|
"accessList": [],
|
|
819
|
-
"authorizationList":
|
|
811
|
+
"authorizationList": [],
|
|
820
812
|
"calls": [
|
|
821
813
|
{
|
|
822
814
|
"data": "0xdeadbeef",
|
|
@@ -903,9 +895,8 @@ describe('sendTransaction', () => {
|
|
|
903
895
|
expect(transactionIndex).toBeDefined()
|
|
904
896
|
expect(transaction).toMatchInlineSnapshot(`
|
|
905
897
|
{
|
|
906
|
-
"aaAuthorizationList": [],
|
|
907
898
|
"accessList": [],
|
|
908
|
-
"authorizationList":
|
|
899
|
+
"authorizationList": [],
|
|
909
900
|
"data": undefined,
|
|
910
901
|
"feePayerSignature": undefined,
|
|
911
902
|
"feeToken": "0x20c0000000000000000000000000000000000001",
|
|
@@ -975,9 +966,8 @@ describe('sendTransaction', () => {
|
|
|
975
966
|
expect(transactionIndex).toBeDefined()
|
|
976
967
|
expect(transaction).toMatchInlineSnapshot(`
|
|
977
968
|
{
|
|
978
|
-
"aaAuthorizationList": [],
|
|
979
969
|
"accessList": [],
|
|
980
|
-
"authorizationList":
|
|
970
|
+
"authorizationList": [],
|
|
981
971
|
"calls": [
|
|
982
972
|
{
|
|
983
973
|
"data": "0x",
|
|
@@ -1153,9 +1143,8 @@ describe('signTransaction', () => {
|
|
|
1153
1143
|
expect(transactionIndex).toBeDefined()
|
|
1154
1144
|
expect(transaction2).toMatchInlineSnapshot(`
|
|
1155
1145
|
{
|
|
1156
|
-
"aaAuthorizationList": [],
|
|
1157
1146
|
"accessList": [],
|
|
1158
|
-
"authorizationList":
|
|
1147
|
+
"authorizationList": [],
|
|
1159
1148
|
"calls": [
|
|
1160
1149
|
{
|
|
1161
1150
|
"data": "0xdeadbeef",
|
|
@@ -1326,9 +1315,8 @@ describe('relay', () => {
|
|
|
1326
1315
|
expect(transactionIndex).toBeDefined()
|
|
1327
1316
|
expect(transaction).toMatchInlineSnapshot(`
|
|
1328
1317
|
{
|
|
1329
|
-
"aaAuthorizationList": [],
|
|
1330
1318
|
"accessList": [],
|
|
1331
|
-
"authorizationList":
|
|
1319
|
+
"authorizationList": [],
|
|
1332
1320
|
"calls": [
|
|
1333
1321
|
{
|
|
1334
1322
|
"data": "0xe789744400000000000000000000000020c0000000000000000000000000000000000001",
|
|
@@ -1457,9 +1445,8 @@ describe('relay', () => {
|
|
|
1457
1445
|
expect(transactionIndex).toBeDefined()
|
|
1458
1446
|
expect(transaction).toMatchInlineSnapshot(`
|
|
1459
1447
|
{
|
|
1460
|
-
"aaAuthorizationList": [],
|
|
1461
1448
|
"accessList": [],
|
|
1462
|
-
"authorizationList":
|
|
1449
|
+
"authorizationList": [],
|
|
1463
1450
|
"calls": [
|
|
1464
1451
|
{
|
|
1465
1452
|
"data": "0xe789744400000000000000000000000020c0000000000000000000000000000000000001",
|
|
@@ -1569,9 +1556,8 @@ describe('relay', () => {
|
|
|
1569
1556
|
expect(transactionIndex).toBeDefined()
|
|
1570
1557
|
expect(transaction).toMatchInlineSnapshot(`
|
|
1571
1558
|
{
|
|
1572
|
-
"aaAuthorizationList": [],
|
|
1573
1559
|
"accessList": [],
|
|
1574
|
-
"authorizationList":
|
|
1560
|
+
"authorizationList": [],
|
|
1575
1561
|
"calls": [
|
|
1576
1562
|
{
|
|
1577
1563
|
"data": "0xe789744400000000000000000000000020c0000000000000000000000000000000000001",
|
package/src/wagmi/Connector.ts
CHANGED
|
@@ -209,8 +209,23 @@ export function webAuthn(options: webAuthn.Parameters) {
|
|
|
209
209
|
let account: Account.RootAccount | undefined
|
|
210
210
|
let accessKey: Account.AccessKeyAccount | undefined
|
|
211
211
|
|
|
212
|
+
const defaultAccessKeyOptions = {
|
|
213
|
+
expiry: Math.floor(
|
|
214
|
+
(Date.now() + 24 * 60 * 60 * 1000) / 1000, // one day
|
|
215
|
+
),
|
|
216
|
+
strict: false,
|
|
217
|
+
}
|
|
218
|
+
const accessKeyOptions = (() => {
|
|
219
|
+
if (typeof options.grantAccessKey === 'object')
|
|
220
|
+
return { ...defaultAccessKeyOptions, ...options.grantAccessKey }
|
|
221
|
+
if (options.grantAccessKey === true) return defaultAccessKeyOptions
|
|
222
|
+
return undefined
|
|
223
|
+
})()
|
|
224
|
+
|
|
212
225
|
const idbStorage = Storage.idb<{
|
|
213
|
-
[key: `accessKey:${string}`]: WebCryptoP256.createKeyPair.ReturnType
|
|
226
|
+
[key: `accessKey:${string}`]: WebCryptoP256.createKeyPair.ReturnType & {
|
|
227
|
+
keyAuthorization: KeyAuthorization.KeyAuthorization
|
|
228
|
+
}
|
|
214
229
|
}>()
|
|
215
230
|
|
|
216
231
|
type Properties = {
|
|
@@ -251,10 +266,18 @@ export function webAuthn(options: webAuthn.Parameters) {
|
|
|
251
266
|
account = Account.fromWebAuthnP256(credential)
|
|
252
267
|
},
|
|
253
268
|
async connect(parameters = {}) {
|
|
254
|
-
const { grantAccessKey = false } = options
|
|
255
269
|
const capabilities =
|
|
256
270
|
'capabilities' in parameters ? (parameters.capabilities ?? {}) : {}
|
|
257
271
|
|
|
272
|
+
if (
|
|
273
|
+
accessKeyOptions?.strict &&
|
|
274
|
+
accessKeyOptions.expiry &&
|
|
275
|
+
accessKeyOptions.expiry < Date.now() / 1000
|
|
276
|
+
)
|
|
277
|
+
throw new Error(
|
|
278
|
+
`\`grantAccessKey.expiry = ${accessKeyOptions.expiry}\` is in the past (${new Date(accessKeyOptions.expiry * 1000).toLocaleString()}). Please provide a valid expiry.`,
|
|
279
|
+
)
|
|
280
|
+
|
|
258
281
|
// We are going to need to find:
|
|
259
282
|
// - a WebAuthn `credential` to instantiate an account
|
|
260
283
|
// - optionally, a `keyPair` to use as the access key for the account
|
|
@@ -286,7 +309,7 @@ export function webAuthn(options: webAuthn.Parameters) {
|
|
|
286
309
|
|
|
287
310
|
// Get key pair (access key) to use for the account.
|
|
288
311
|
const keyPair = await (async () => {
|
|
289
|
-
if (!
|
|
312
|
+
if (!accessKeyOptions) return undefined
|
|
290
313
|
return await WebCryptoP256.createKeyPair()
|
|
291
314
|
})()
|
|
292
315
|
|
|
@@ -303,15 +326,15 @@ export function webAuthn(options: webAuthn.Parameters) {
|
|
|
303
326
|
if (credential) {
|
|
304
327
|
// Get key pair (access key) to use for the account.
|
|
305
328
|
const keyPair = await (async () => {
|
|
306
|
-
if (!
|
|
329
|
+
if (!accessKeyOptions) return undefined
|
|
307
330
|
const address = Address.fromPublicKey(
|
|
308
331
|
PublicKey.fromHex(credential.publicKey),
|
|
309
332
|
)
|
|
310
333
|
return await idbStorage.getItem(`accessKey:${address}`)
|
|
311
334
|
})()
|
|
312
335
|
|
|
313
|
-
// If the access key
|
|
314
|
-
if (
|
|
336
|
+
// If the access key provisioning is not in strict mode, return the credential and key pair (if exists).
|
|
337
|
+
if (!accessKeyOptions?.strict) return { credential, keyPair }
|
|
315
338
|
|
|
316
339
|
// If a key pair is found, return the credential and key pair.
|
|
317
340
|
if (keyPair) return { credential, keyPair }
|
|
@@ -328,21 +351,25 @@ export function webAuthn(options: webAuthn.Parameters) {
|
|
|
328
351
|
{
|
|
329
352
|
// Get key pair (access key) to use for the account.
|
|
330
353
|
const keyPair = await (async () => {
|
|
331
|
-
if (!
|
|
354
|
+
if (!accessKeyOptions) return undefined
|
|
332
355
|
return await WebCryptoP256.createKeyPair()
|
|
333
356
|
})()
|
|
334
357
|
|
|
335
358
|
// If we are provisioning an access key, we will need to sign a key authorization.
|
|
336
359
|
// We will need the hash (digest) to sign, and the address of the access key to construct the key authorization.
|
|
337
|
-
const {
|
|
360
|
+
const { hash, keyAuthorization_unsigned } = await (async () => {
|
|
338
361
|
if (!keyPair)
|
|
339
362
|
return { accessKeyAddress: undefined, hash: undefined }
|
|
340
363
|
const accessKeyAddress = Address.fromPublicKey(keyPair.publicKey)
|
|
341
|
-
const
|
|
364
|
+
const keyAuthorization_unsigned = KeyAuthorization.from({
|
|
365
|
+
...accessKeyOptions,
|
|
342
366
|
address: accessKeyAddress,
|
|
343
367
|
type: 'p256',
|
|
344
368
|
})
|
|
345
|
-
|
|
369
|
+
const hash = KeyAuthorization.getSignPayload(
|
|
370
|
+
keyAuthorization_unsigned,
|
|
371
|
+
)
|
|
372
|
+
return { keyAuthorization_unsigned, hash }
|
|
346
373
|
})()
|
|
347
374
|
|
|
348
375
|
// If no active credential, we will attempt to load the last active credential from storage.
|
|
@@ -363,16 +390,15 @@ export function webAuthn(options: webAuthn.Parameters) {
|
|
|
363
390
|
rpId: options.getOptions?.rpId ?? options.rpId,
|
|
364
391
|
})
|
|
365
392
|
|
|
366
|
-
const keyAuthorization =
|
|
393
|
+
const keyAuthorization = keyAuthorization_unsigned
|
|
367
394
|
? KeyAuthorization.from({
|
|
368
|
-
|
|
395
|
+
...keyAuthorization_unsigned,
|
|
369
396
|
signature: SignatureEnvelope.from({
|
|
370
397
|
metadata: credential.metadata,
|
|
371
398
|
signature: credential.signature,
|
|
372
399
|
publicKey: PublicKey.fromHex(credential.publicKey),
|
|
373
400
|
type: 'webAuthn',
|
|
374
401
|
}),
|
|
375
|
-
type: 'p256',
|
|
376
402
|
})
|
|
377
403
|
: undefined
|
|
378
404
|
|
|
@@ -399,19 +425,42 @@ export function webAuthn(options: webAuthn.Parameters) {
|
|
|
399
425
|
storage: Storage.from(config.storage as never),
|
|
400
426
|
})
|
|
401
427
|
|
|
428
|
+
// If we are reconnecting, check if the access key is expired.
|
|
429
|
+
if (parameters.isReconnecting) {
|
|
430
|
+
if (
|
|
431
|
+
'keyAuthorization' in keyPair &&
|
|
432
|
+
keyPair.keyAuthorization.expiry &&
|
|
433
|
+
keyPair.keyAuthorization.expiry < Date.now() / 1000
|
|
434
|
+
) {
|
|
435
|
+
// remove any pending key authorizations from storage.
|
|
436
|
+
await account?.storage.removeItem('pendingKeyAuthorization')
|
|
437
|
+
|
|
438
|
+
const message = `Access key expired (on ${new Date(keyPair.keyAuthorization.expiry * 1000).toLocaleString()}).`
|
|
439
|
+
accessKey = undefined
|
|
440
|
+
|
|
441
|
+
// if in strict mode, disconnect and throw an error.
|
|
442
|
+
if (accessKeyOptions?.strict) {
|
|
443
|
+
await this.disconnect()
|
|
444
|
+
throw new Error(message)
|
|
445
|
+
}
|
|
446
|
+
// otherwise, fall back to the root account.
|
|
447
|
+
console.warn(`${message} Falling back to passkey.`)
|
|
448
|
+
}
|
|
449
|
+
}
|
|
402
450
|
// If we are not reconnecting, orchestrate the provisioning of the access key.
|
|
403
|
-
|
|
451
|
+
else {
|
|
404
452
|
const keyAuth =
|
|
405
|
-
keyAuthorization ??
|
|
453
|
+
keyAuthorization ??
|
|
454
|
+
(await account.signKeyAuthorization(accessKey, accessKeyOptions))
|
|
406
455
|
|
|
407
456
|
await account.storage.setItem('pendingKeyAuthorization', keyAuth)
|
|
408
457
|
await idbStorage.setItem(
|
|
409
458
|
`accessKey:${account.address.toLowerCase()}`,
|
|
410
|
-
keyPair,
|
|
459
|
+
{ ...keyPair, keyAuthorization: keyAuth },
|
|
411
460
|
)
|
|
412
461
|
}
|
|
413
|
-
// If we are granting an access key, throw an error if the access key is not provisioned.
|
|
414
|
-
} else if (
|
|
462
|
+
// If we are granting an access key and it is in strict mode, throw an error if the access key is not provisioned.
|
|
463
|
+
} else if (accessKeyOptions?.strict) {
|
|
415
464
|
await config.storage?.removeItem('webAuthn.activeCredential')
|
|
416
465
|
throw new Error('access key not found')
|
|
417
466
|
}
|
|
@@ -430,6 +479,7 @@ export function webAuthn(options: webAuthn.Parameters) {
|
|
|
430
479
|
},
|
|
431
480
|
async disconnect() {
|
|
432
481
|
await config.storage?.removeItem('webAuthn.activeCredential')
|
|
482
|
+
config.emitter.emit('disconnect')
|
|
433
483
|
account = undefined
|
|
434
484
|
},
|
|
435
485
|
async getAccounts() {
|
|
@@ -476,8 +526,36 @@ export function webAuthn(options: webAuthn.Parameters) {
|
|
|
476
526
|
const transport = transports[chain.id]
|
|
477
527
|
if (!transport) throw new ChainNotConfiguredError()
|
|
478
528
|
|
|
529
|
+
const targetAccount = await (async () => {
|
|
530
|
+
if (!accessKey) return account
|
|
531
|
+
|
|
532
|
+
const item = await idbStorage.getItem(
|
|
533
|
+
`accessKey:${accessKey.address.toLowerCase()}`,
|
|
534
|
+
)
|
|
535
|
+
if (
|
|
536
|
+
item?.keyAuthorization.expiry &&
|
|
537
|
+
item.keyAuthorization.expiry < Date.now() / 1000
|
|
538
|
+
) {
|
|
539
|
+
// remove any pending key authorizations from storage.
|
|
540
|
+
await account?.storage.removeItem('pendingKeyAuthorization')
|
|
541
|
+
|
|
542
|
+
const message = `Access key expired (on ${new Date(item.keyAuthorization.expiry * 1000).toLocaleString()}).`
|
|
543
|
+
|
|
544
|
+
// if in strict mode, disconnect and throw an error.
|
|
545
|
+
if (accessKeyOptions?.strict) {
|
|
546
|
+
await this.disconnect()
|
|
547
|
+
throw new Error(message)
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// otherwise, fall back to the root account.
|
|
551
|
+
console.warn(`${message} Falling back to passkey.`)
|
|
552
|
+
return account
|
|
553
|
+
}
|
|
554
|
+
return accessKey
|
|
555
|
+
})()
|
|
556
|
+
|
|
479
557
|
return createClient({
|
|
480
|
-
account:
|
|
558
|
+
account: targetAccount,
|
|
481
559
|
chain: chain as Chain,
|
|
482
560
|
transport: walletNamespaceCompat(transport),
|
|
483
561
|
})
|
|
@@ -503,19 +581,14 @@ export namespace webAuthn {
|
|
|
503
581
|
| Pick<WebAuthnP256.getCredential.Parameters, 'getFn' | 'rpId'>
|
|
504
582
|
| undefined
|
|
505
583
|
/**
|
|
506
|
-
* Whether or not to grant an access key upon connection.
|
|
507
|
-
*
|
|
508
|
-
* - `true`: The account MUST have an access key provisioned.
|
|
509
|
-
* On failure, the connection will fail.
|
|
510
|
-
* - `"lax"`: The account MAY have an access key provisioned.
|
|
511
|
-
* On failure, the connection will succeed, but the access key will not be provisioned
|
|
512
|
-
* and must be provisioned manually if the user wants to enforce access keys.
|
|
513
|
-
* - `false`: The account WILL NOT have an access key provisioned. The access key must be
|
|
514
|
-
* provisioned manually if the user wants to enforce access keys.
|
|
515
|
-
*
|
|
516
|
-
* @default false
|
|
584
|
+
* Whether or not to grant an access key upon connection, and optionally, expiry + limits to assign to the key.
|
|
517
585
|
*/
|
|
518
|
-
grantAccessKey?:
|
|
586
|
+
grantAccessKey?:
|
|
587
|
+
| boolean
|
|
588
|
+
| (Pick<KeyAuthorization.KeyAuthorization, 'expiry' | 'limits'> & {
|
|
589
|
+
/** Whether or not to throw an error and disconnect if the access key is not provisioned or is expired. */
|
|
590
|
+
strict?: boolean | undefined
|
|
591
|
+
})
|
|
519
592
|
/** Public key manager. */
|
|
520
593
|
keyManager: KeyManager.KeyManager
|
|
521
594
|
/** The RP ID to use for WebAuthn. */
|