accounts 0.12.2 → 0.14.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 +35 -0
- package/README.md +6 -5
- package/dist/cli/adapter.d.ts.map +1 -1
- package/dist/cli/adapter.js +76 -69
- package/dist/cli/adapter.js.map +1 -1
- package/dist/core/AccessKey.d.ts +89 -106
- package/dist/core/AccessKey.d.ts.map +1 -1
- package/dist/core/AccessKey.js +230 -210
- package/dist/core/AccessKey.js.map +1 -1
- package/dist/core/Account.d.ts +0 -9
- package/dist/core/Account.d.ts.map +1 -1
- package/dist/core/Account.js +1 -13
- package/dist/core/Account.js.map +1 -1
- package/dist/core/Adapter.d.ts +24 -4
- package/dist/core/Adapter.d.ts.map +1 -1
- package/dist/core/Client.d.ts +1 -1
- package/dist/core/Client.d.ts.map +1 -1
- package/dist/core/Client.js.map +1 -1
- package/dist/core/Provider.d.ts +6 -7
- package/dist/core/Provider.d.ts.map +1 -1
- package/dist/core/Provider.js +73 -77
- package/dist/core/Provider.js.map +1 -1
- package/dist/core/Remote.d.ts +2 -0
- package/dist/core/Remote.d.ts.map +1 -1
- package/dist/core/Remote.js +3 -1
- package/dist/core/Remote.js.map +1 -1
- package/dist/core/Schema.d.ts +17 -7
- package/dist/core/Schema.d.ts.map +1 -1
- package/dist/core/adapters/dialog.d.ts.map +1 -1
- package/dist/core/adapters/dialog.js +126 -131
- package/dist/core/adapters/dialog.js.map +1 -1
- package/dist/core/adapters/local.d.ts.map +1 -1
- package/dist/core/adapters/local.js +92 -105
- package/dist/core/adapters/local.js.map +1 -1
- package/dist/core/adapters/privy.d.ts +164 -0
- package/dist/core/adapters/privy.d.ts.map +1 -0
- package/dist/core/adapters/privy.js +578 -0
- package/dist/core/adapters/privy.js.map +1 -0
- package/dist/core/adapters/turnkey.d.ts.map +1 -1
- package/dist/core/adapters/turnkey.js +136 -133
- package/dist/core/adapters/turnkey.js.map +1 -1
- package/dist/core/internal/AccessKeyTransaction.d.ts +59 -0
- package/dist/core/internal/AccessKeyTransaction.d.ts.map +1 -0
- package/dist/core/internal/AccessKeyTransaction.js +155 -0
- package/dist/core/internal/AccessKeyTransaction.js.map +1 -0
- package/dist/core/zod/rpc.d.ts +71 -4
- package/dist/core/zod/rpc.d.ts.map +1 -1
- package/dist/core/zod/rpc.js +33 -4
- package/dist/core/zod/rpc.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react-native/adapter.d.ts.map +1 -1
- package/dist/react-native/adapter.js +86 -75
- package/dist/react-native/adapter.js.map +1 -1
- package/dist/server/CliAuth.d.ts +55 -0
- package/dist/server/CliAuth.d.ts.map +1 -1
- package/dist/server/CliAuth.js +18 -1
- package/dist/server/CliAuth.js.map +1 -1
- package/dist/server/Kv.d.ts +19 -9
- package/dist/server/Kv.d.ts.map +1 -1
- package/dist/server/Kv.js +32 -9
- package/dist/server/Kv.js.map +1 -1
- package/dist/server/internal/handlers/auth.d.ts.map +1 -1
- package/dist/server/internal/handlers/auth.js +3 -1
- package/dist/server/internal/handlers/auth.js.map +1 -1
- package/dist/server/internal/handlers/codeAuth.js +1 -1
- package/dist/server/internal/handlers/codeAuth.js.map +1 -1
- package/dist/server/internal/handlers/exchange.js +1 -1
- package/dist/server/internal/handlers/exchange.js.map +1 -1
- package/dist/server/internal/handlers/relay.d.ts +18 -12
- package/dist/server/internal/handlers/relay.d.ts.map +1 -1
- package/dist/server/internal/handlers/relay.js +116 -3
- package/dist/server/internal/handlers/relay.js.map +1 -1
- package/dist/server/internal/handlers/webAuthn.d.ts +6 -1
- package/dist/server/internal/handlers/webAuthn.d.ts.map +1 -1
- package/dist/server/internal/handlers/webAuthn.js +16 -9
- package/dist/server/internal/handlers/webAuthn.js.map +1 -1
- package/package.json +3 -3
- package/src/cli/{Provider.test.ts → Provider.localnet.test.ts} +49 -1
- package/src/cli/adapter.ts +95 -76
- package/src/core/AccessKey.test.ts +563 -560
- package/src/core/AccessKey.ts +327 -327
- package/src/core/Account.test.ts +6 -51
- package/src/core/Account.ts +1 -19
- package/src/core/Adapter.ts +22 -4
- package/src/core/Client.test.ts +1 -1
- package/src/core/Client.ts +1 -1
- package/src/core/Provider.connect.browser.test.ts +1 -1
- package/src/core/{Provider.test.ts → Provider.localnet.test.ts} +98 -32
- package/src/core/Provider.ts +118 -112
- package/src/core/Remote.test.ts +28 -0
- package/src/core/Remote.ts +4 -1
- package/src/core/Schema.test-d.ts +41 -0
- package/src/core/adapters/dialog.test.ts +335 -15
- package/src/core/adapters/dialog.ts +120 -127
- package/src/core/adapters/local.test.ts +1 -1
- package/src/core/adapters/local.ts +95 -119
- package/src/core/adapters/privy.test-d.ts +128 -0
- package/src/core/adapters/privy.test.ts +648 -0
- package/src/core/adapters/privy.ts +811 -0
- package/src/core/adapters/turnkey.test.ts +117 -13
- package/src/core/adapters/turnkey.ts +132 -149
- package/src/core/internal/AccessKeyTransaction.ts +243 -0
- package/src/core/{mppx.test.ts → mppx.localnet.test.ts} +32 -2
- package/src/core/zod/request.test.ts +36 -0
- package/src/core/zod/rpc.test.ts +139 -0
- package/src/core/zod/rpc.ts +44 -10
- package/src/index.ts +1 -1
- package/src/react-native/{Provider.test.ts → Provider.localnet.test.ts} +65 -0
- package/src/react-native/adapter.ts +107 -87
- package/src/server/CliAuth.test-d.ts +23 -0
- package/src/server/CliAuth.test.ts +47 -0
- package/src/server/CliAuth.ts +20 -1
- package/src/server/Handler.test-d.ts +1 -1
- package/src/server/Kv.test.ts +51 -0
- package/src/server/Kv.ts +43 -9
- package/src/server/internal/handlers/auth.test.ts +10 -5
- package/src/server/internal/handlers/auth.ts +4 -1
- package/src/server/internal/handlers/codeAuth.ts +1 -1
- package/src/server/internal/handlers/exchange.ts +1 -1
- package/src/server/internal/handlers/{relay.test.ts → relay.localnet.test.ts} +4 -1
- package/src/server/internal/handlers/relay.ts +139 -14
- package/src/server/internal/handlers/session.test.ts +1 -1
- package/src/server/internal/handlers/webAuthn.test.ts +20 -0
- package/src/server/internal/handlers/webAuthn.ts +24 -12
- /package/src/server/internal/handlers/{exchange.test.ts → exchange.localnet.test.ts} +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Hex, WebCryptoP256 } from 'ox'
|
|
2
2
|
import { KeyAuthorization, SignatureEnvelope } from 'ox/tempo'
|
|
3
|
-
import { encodeErrorResult } from 'viem'
|
|
4
|
-
import { Abis, Account as TempoAccount
|
|
3
|
+
import { BaseError, encodeErrorResult, encodeFunctionResult } from 'viem'
|
|
4
|
+
import { Abis, Account as TempoAccount } from 'viem/tempo'
|
|
5
5
|
import { describe, expect, test } from 'vp/test'
|
|
6
6
|
|
|
7
|
-
import { accounts
|
|
7
|
+
import { accounts } from '../../test/config.js'
|
|
8
8
|
import * as AccessKey from './AccessKey.js'
|
|
9
|
+
import * as AccessKeyTransaction from './internal/AccessKeyTransaction.js'
|
|
9
10
|
import * as Store from './Store.js'
|
|
10
11
|
|
|
11
12
|
function createStore() {
|
|
@@ -17,6 +18,7 @@ const rootAddress = accounts[0]!.address
|
|
|
17
18
|
function createKeyAuthorization(
|
|
18
19
|
address: `0x${string}`,
|
|
19
20
|
options: {
|
|
21
|
+
chainId?: bigint | undefined
|
|
20
22
|
expiry?: number | undefined
|
|
21
23
|
limits?: { token: `0x${string}`; limit: bigint }[] | undefined
|
|
22
24
|
scopes?: KeyAuthorization.Scope[] | undefined
|
|
@@ -25,7 +27,7 @@ function createKeyAuthorization(
|
|
|
25
27
|
return KeyAuthorization.from(
|
|
26
28
|
{
|
|
27
29
|
address,
|
|
28
|
-
chainId: 1n,
|
|
30
|
+
chainId: options.chainId ?? 1n,
|
|
29
31
|
expiry: options.expiry,
|
|
30
32
|
limits: options.limits,
|
|
31
33
|
scopes: options.scopes,
|
|
@@ -41,88 +43,129 @@ function createRevert(errorName: string) {
|
|
|
41
43
|
})
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const keyPair = await WebCryptoP256.createKeyPair()
|
|
65
|
-
const accessKey = TempoAccount.fromWebCryptoP256(keyPair)
|
|
66
|
-
const keyAuthorization = createKeyAuthorization(accessKey.address)
|
|
67
|
-
|
|
68
|
-
AccessKey.save({ address: rootAddress, keyAuthorization, store })
|
|
69
|
-
|
|
70
|
-
expect(store.getState().accessKeys[0]!.keyPair).toBeUndefined()
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
test('behavior: saves with keyPair', async () => {
|
|
74
|
-
const store = createStore()
|
|
75
|
-
const keyPair = await WebCryptoP256.createKeyPair()
|
|
76
|
-
const accessKey = TempoAccount.fromWebCryptoP256(keyPair)
|
|
77
|
-
const keyAuthorization = createKeyAuthorization(accessKey.address)
|
|
46
|
+
function createMetadataClient(
|
|
47
|
+
accessKey: Hex.Hex,
|
|
48
|
+
options: { isRevoked?: boolean | undefined; keyId?: Hex.Hex | undefined } = {},
|
|
49
|
+
) {
|
|
50
|
+
return {
|
|
51
|
+
call: async () => ({
|
|
52
|
+
data: encodeFunctionResult({
|
|
53
|
+
abi: Abis.accountKeychain,
|
|
54
|
+
functionName: 'getKey',
|
|
55
|
+
result: {
|
|
56
|
+
enforceLimits: false,
|
|
57
|
+
expiry: 0n,
|
|
58
|
+
isRevoked: options.isRevoked ?? false,
|
|
59
|
+
keyId: options.keyId ?? accessKey,
|
|
60
|
+
signatureType: 1,
|
|
61
|
+
},
|
|
62
|
+
} as never),
|
|
63
|
+
}),
|
|
64
|
+
}
|
|
65
|
+
}
|
|
78
66
|
|
|
79
|
-
|
|
67
|
+
function createMissingClient() {
|
|
68
|
+
return {
|
|
69
|
+
call: async () => {
|
|
70
|
+
throw createRevert('KeyNotFound')
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
}
|
|
80
74
|
|
|
81
|
-
|
|
82
|
-
|
|
75
|
+
function createFillClient(
|
|
76
|
+
accessKey: Hex.Hex,
|
|
77
|
+
options: { isRevoked?: boolean | undefined; keyId?: Hex.Hex | undefined } = {},
|
|
78
|
+
) {
|
|
79
|
+
const requests: unknown[] = []
|
|
80
|
+
return {
|
|
81
|
+
client: {
|
|
82
|
+
...createMetadataClient(accessKey, options),
|
|
83
|
+
request: async (request: unknown) => {
|
|
84
|
+
requests.push(request)
|
|
85
|
+
return { capabilities: { sponsored: false }, tx: {} }
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
requests,
|
|
89
|
+
}
|
|
90
|
+
}
|
|
83
91
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const ak2 = TempoAccount.fromWebCryptoP256(keyPair2)
|
|
92
|
+
function getStored(account: TempoAccount.AccessKeyAccount, store: Store.Store) {
|
|
93
|
+
return store
|
|
94
|
+
.getState()
|
|
95
|
+
.accessKeys.find((key) => key.address.toLowerCase() === account.accessKeyAddress.toLowerCase())
|
|
96
|
+
}
|
|
90
97
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
98
|
+
function addAuthorization(options: {
|
|
99
|
+
address: `0x${string}`
|
|
100
|
+
keyAuthorization: KeyAuthorization.Signed
|
|
101
|
+
keyPair?: Awaited<ReturnType<typeof WebCryptoP256.createKeyPair>> | undefined
|
|
102
|
+
privateKey?: Hex.Hex | undefined
|
|
103
|
+
state: 'signed' | 'pending' | 'authorized'
|
|
104
|
+
store: Store.Store
|
|
105
|
+
}) {
|
|
106
|
+
const { address, keyAuthorization, keyPair, privateKey, state, store } = options
|
|
107
|
+
AccessKey.add({
|
|
108
|
+
account: address,
|
|
109
|
+
authorization: keyAuthorization,
|
|
110
|
+
...(keyPair ? { keyPair } : {}),
|
|
111
|
+
...(privateKey ? { privateKey } : {}),
|
|
112
|
+
store,
|
|
113
|
+
})
|
|
114
|
+
if (state === 'pending')
|
|
115
|
+
AccessKey.markPending({
|
|
116
|
+
account: address,
|
|
117
|
+
accessKey: keyAuthorization.address,
|
|
118
|
+
chainId: Number(keyAuthorization.chainId),
|
|
94
119
|
store,
|
|
95
120
|
})
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
121
|
+
if (state === 'authorized')
|
|
122
|
+
AccessKey.markPublished({
|
|
123
|
+
account: address,
|
|
124
|
+
accessKey: keyAuthorization.address,
|
|
125
|
+
chainId: Number(keyAuthorization.chainId),
|
|
99
126
|
store,
|
|
100
127
|
})
|
|
128
|
+
}
|
|
101
129
|
|
|
102
|
-
|
|
130
|
+
function markPublished(options: {
|
|
131
|
+
accessKey: `0x${string}`
|
|
132
|
+
address?: `0x${string}` | undefined
|
|
133
|
+
chainId?: number | undefined
|
|
134
|
+
store: Store.Store
|
|
135
|
+
}) {
|
|
136
|
+
const { accessKey, store } = options
|
|
137
|
+
AccessKey.markPublished({
|
|
138
|
+
account: options.address ?? rootAddress,
|
|
139
|
+
accessKey,
|
|
140
|
+
chainId: options.chainId ?? 1,
|
|
141
|
+
store,
|
|
103
142
|
})
|
|
143
|
+
}
|
|
104
144
|
|
|
105
|
-
|
|
145
|
+
describe('add', () => {
|
|
146
|
+
test('default: saves a signed authorization', async () => {
|
|
106
147
|
const store = createStore()
|
|
107
148
|
const keyPair = await WebCryptoP256.createKeyPair()
|
|
108
149
|
const accessKey = TempoAccount.fromWebCryptoP256(keyPair)
|
|
109
150
|
const expiry = Math.floor(Date.now() / 1000) + 3600
|
|
110
|
-
const keyAuthorization = createKeyAuthorization(accessKey.address, { expiry })
|
|
111
|
-
|
|
112
|
-
AccessKey.save({ address: rootAddress, keyAuthorization, store })
|
|
113
|
-
|
|
114
|
-
expect(store.getState().accessKeys[0]!.expiry).toBe(expiry)
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
test('behavior: stores limits from key authorization', async () => {
|
|
118
|
-
const store = createStore()
|
|
119
|
-
const keyPair = await WebCryptoP256.createKeyPair()
|
|
120
|
-
const accessKey = TempoAccount.fromWebCryptoP256(keyPair)
|
|
121
151
|
const limits = [{ token: '0x20c0000000000000000000000000000000000001' as const, limit: 1000n }]
|
|
122
|
-
const keyAuthorization = createKeyAuthorization(accessKey.address, { limits })
|
|
152
|
+
const keyAuthorization = createKeyAuthorization(accessKey.address, { expiry, limits })
|
|
123
153
|
|
|
124
|
-
|
|
154
|
+
addAuthorization({
|
|
155
|
+
address: rootAddress,
|
|
156
|
+
keyAuthorization,
|
|
157
|
+
state: 'signed',
|
|
158
|
+
store,
|
|
159
|
+
})
|
|
125
160
|
|
|
161
|
+
const { accessKeys } = store.getState()
|
|
162
|
+
expect(accessKeys.length).toMatchInlineSnapshot(`1`)
|
|
163
|
+
expect(accessKeys[0]!.address).toBe(accessKey.address)
|
|
164
|
+
expect(accessKeys[0]!.access).toBe(rootAddress)
|
|
165
|
+
expect(accessKeys[0]!.chainId).toMatchInlineSnapshot(`1`)
|
|
166
|
+
expect(accessKeys[0]!.expiry).toBe(expiry)
|
|
167
|
+
expect(accessKeys[0]!.keyType).toMatchInlineSnapshot(`"p256"`)
|
|
168
|
+
expect(accessKeys[0]!.keyAuthorization).toBe(keyAuthorization)
|
|
126
169
|
expect(store.getState().accessKeys[0]!.limits).toMatchInlineSnapshot(`
|
|
127
170
|
[
|
|
128
171
|
{
|
|
@@ -134,110 +177,129 @@ describe('save', () => {
|
|
|
134
177
|
})
|
|
135
178
|
})
|
|
136
179
|
|
|
137
|
-
describe('
|
|
138
|
-
test('default: returns key authorization for access key account', async () => {
|
|
139
|
-
const store = createStore()
|
|
140
|
-
const keyPair = await WebCryptoP256.createKeyPair()
|
|
141
|
-
const accessKey = TempoAccount.fromWebCryptoP256(keyPair, { access: rootAddress })
|
|
142
|
-
const keyAuthorization = createKeyAuthorization(accessKey.accessKeyAddress)
|
|
143
|
-
|
|
144
|
-
AccessKey.save({ address: rootAddress, keyAuthorization, store })
|
|
145
|
-
|
|
146
|
-
const result = AccessKey.getPending(accessKey, { store })
|
|
147
|
-
expect(result).toBe(keyAuthorization)
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
test('behavior: returns undefined for root account', () => {
|
|
151
|
-
const store = createStore()
|
|
152
|
-
const result = AccessKey.getPending(accounts[0]!, { store })
|
|
153
|
-
expect(result).toBeUndefined()
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
test('behavior: returns undefined when no matching access key', async () => {
|
|
157
|
-
const store = createStore()
|
|
158
|
-
const keyPair = await WebCryptoP256.createKeyPair()
|
|
159
|
-
const accessKey = TempoAccount.fromWebCryptoP256(keyPair, { access: rootAddress })
|
|
160
|
-
|
|
161
|
-
const result = AccessKey.getPending(accessKey, { store })
|
|
162
|
-
expect(result).toBeUndefined()
|
|
163
|
-
})
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
describe('removePending', () => {
|
|
180
|
+
describe('markPublished', () => {
|
|
167
181
|
test('default: clears key authorization from access key', async () => {
|
|
168
182
|
const store = createStore()
|
|
169
183
|
const keyPair = await WebCryptoP256.createKeyPair()
|
|
170
184
|
const accessKey = TempoAccount.fromWebCryptoP256(keyPair, { access: rootAddress })
|
|
171
185
|
const keyAuthorization = createKeyAuthorization(accessKey.accessKeyAddress)
|
|
172
186
|
|
|
173
|
-
|
|
174
|
-
|
|
187
|
+
addAuthorization({
|
|
188
|
+
address: rootAddress,
|
|
189
|
+
keyAuthorization,
|
|
190
|
+
state: 'signed',
|
|
191
|
+
store,
|
|
192
|
+
})
|
|
193
|
+
expect(getStored(accessKey, store)?.keyAuthorization).toBeDefined()
|
|
175
194
|
|
|
176
|
-
|
|
195
|
+
markPublished({ accessKey: accessKey.accessKeyAddress, store })
|
|
177
196
|
|
|
178
|
-
expect(
|
|
197
|
+
expect(getStored(accessKey, store)?.keyAuthorization).toBeUndefined()
|
|
198
|
+
expect(getStored(accessKey, store)?.keyAuthorizationPending).toBeUndefined()
|
|
179
199
|
})
|
|
180
200
|
|
|
181
|
-
test('behavior:
|
|
201
|
+
test('behavior: only clears the matching account and chain', async () => {
|
|
182
202
|
const store = createStore()
|
|
183
203
|
const keyPair = await WebCryptoP256.createKeyPair()
|
|
184
204
|
const accessKey = TempoAccount.fromWebCryptoP256(keyPair, { access: rootAddress })
|
|
185
205
|
const keyAuthorization = createKeyAuthorization(accessKey.accessKeyAddress)
|
|
206
|
+
const keyAuthorization_chain = createKeyAuthorization(accessKey.accessKeyAddress, {
|
|
207
|
+
chainId: 2n,
|
|
208
|
+
})
|
|
209
|
+
const keyAuthorization_account = createKeyAuthorization(accessKey.accessKeyAddress)
|
|
186
210
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
211
|
+
addAuthorization({
|
|
212
|
+
address: rootAddress,
|
|
213
|
+
keyAuthorization,
|
|
214
|
+
state: 'signed',
|
|
215
|
+
store,
|
|
216
|
+
})
|
|
217
|
+
addAuthorization({
|
|
218
|
+
address: rootAddress,
|
|
219
|
+
keyAuthorization: keyAuthorization_chain,
|
|
220
|
+
state: 'signed',
|
|
221
|
+
store,
|
|
222
|
+
})
|
|
223
|
+
addAuthorization({
|
|
224
|
+
address: accounts[1]!.address,
|
|
225
|
+
keyAuthorization: keyAuthorization_account,
|
|
226
|
+
state: 'signed',
|
|
227
|
+
store,
|
|
228
|
+
})
|
|
205
229
|
|
|
206
|
-
|
|
230
|
+
markPublished({ accessKey: accessKey.accessKeyAddress, store })
|
|
207
231
|
|
|
208
|
-
expect(
|
|
209
|
-
|
|
232
|
+
expect(
|
|
233
|
+
store.getState().accessKeys.map((key) => ({
|
|
234
|
+
access: key.access,
|
|
235
|
+
chainId: key.chainId,
|
|
236
|
+
keyAuthorization: !!key.keyAuthorization,
|
|
237
|
+
})),
|
|
238
|
+
).toMatchInlineSnapshot(`
|
|
239
|
+
[
|
|
240
|
+
{
|
|
241
|
+
"access": "${accounts[1]!.address}",
|
|
242
|
+
"chainId": 1,
|
|
243
|
+
"keyAuthorization": true,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
"access": "${rootAddress}",
|
|
247
|
+
"chainId": 2,
|
|
248
|
+
"keyAuthorization": true,
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"access": "${rootAddress}",
|
|
252
|
+
"chainId": 1,
|
|
253
|
+
"keyAuthorization": false,
|
|
254
|
+
},
|
|
255
|
+
]
|
|
256
|
+
`)
|
|
210
257
|
})
|
|
211
258
|
})
|
|
212
259
|
|
|
213
|
-
describe('
|
|
214
|
-
async function setup() {
|
|
260
|
+
describe('create invalidation', () => {
|
|
261
|
+
async function setup(options: { other?: boolean | undefined } = {}) {
|
|
215
262
|
const store = createStore()
|
|
263
|
+
const keyPair_other = await WebCryptoP256.createKeyPair()
|
|
264
|
+
const account_other = TempoAccount.fromWebCryptoP256(keyPair_other, { access: rootAddress })
|
|
265
|
+
if (options.other)
|
|
266
|
+
addAuthorization({
|
|
267
|
+
address: rootAddress,
|
|
268
|
+
keyAuthorization: createKeyAuthorization(account_other.accessKeyAddress),
|
|
269
|
+
keyPair: keyPair_other,
|
|
270
|
+
state: 'signed',
|
|
271
|
+
store,
|
|
272
|
+
})
|
|
273
|
+
|
|
216
274
|
const keyPair = await WebCryptoP256.createKeyPair()
|
|
217
275
|
const account = TempoAccount.fromWebCryptoP256(keyPair, { access: rootAddress })
|
|
218
|
-
|
|
276
|
+
addAuthorization({
|
|
219
277
|
address: rootAddress,
|
|
220
278
|
keyAuthorization: createKeyAuthorization(account.accessKeyAddress),
|
|
221
279
|
keyPair,
|
|
280
|
+
state: 'signed',
|
|
222
281
|
store,
|
|
223
282
|
})
|
|
224
|
-
return {
|
|
283
|
+
return { account_other, store }
|
|
225
284
|
}
|
|
226
285
|
|
|
227
|
-
test('
|
|
228
|
-
const {
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
AccessKey.save({
|
|
286
|
+
test('behavior: removes matching access key for stale-key errors', async () => {
|
|
287
|
+
const { account_other, store } = await setup({ other: true })
|
|
288
|
+
|
|
289
|
+
const transaction = await AccessKeyTransaction.create({
|
|
232
290
|
address: rootAddress,
|
|
233
|
-
|
|
234
|
-
|
|
291
|
+
chainId: 1,
|
|
292
|
+
client: {
|
|
293
|
+
request: async () => {
|
|
294
|
+
throw createRevert('KeyNotFound')
|
|
295
|
+
},
|
|
296
|
+
} as never,
|
|
235
297
|
store,
|
|
236
298
|
})
|
|
237
299
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
300
|
+
await expect(
|
|
301
|
+
transaction?.fill({ chainId: 1, from: rootAddress }),
|
|
302
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: reverted]`)
|
|
241
303
|
expect(store.getState().accessKeys.map((key) => key.address)).toMatchInlineSnapshot(`
|
|
242
304
|
[
|
|
243
305
|
"${account_other.accessKeyAddress}",
|
|
@@ -246,36 +308,70 @@ describe('invalidate', () => {
|
|
|
246
308
|
})
|
|
247
309
|
|
|
248
310
|
test('behavior: preserves access key for recoverable execution errors', async () => {
|
|
249
|
-
const {
|
|
311
|
+
const { store } = await setup()
|
|
250
312
|
|
|
251
|
-
const
|
|
313
|
+
const transaction = await AccessKeyTransaction.create({
|
|
314
|
+
address: rootAddress,
|
|
315
|
+
chainId: 1,
|
|
316
|
+
client: {
|
|
317
|
+
request: async () => {
|
|
318
|
+
throw createRevert('SpendingLimitExceeded')
|
|
319
|
+
},
|
|
320
|
+
} as never,
|
|
252
321
|
store,
|
|
253
322
|
})
|
|
254
323
|
|
|
255
|
-
expect(
|
|
324
|
+
await expect(
|
|
325
|
+
transaction?.fill({ chainId: 1, from: rootAddress }),
|
|
326
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: reverted]`)
|
|
256
327
|
expect(store.getState().accessKeys.length).toMatchInlineSnapshot(`1`)
|
|
257
328
|
})
|
|
258
329
|
|
|
259
330
|
test('behavior: preserves access key for unknown errors', async () => {
|
|
260
|
-
const {
|
|
331
|
+
const { store } = await setup()
|
|
261
332
|
|
|
262
|
-
const
|
|
333
|
+
const transaction = await AccessKeyTransaction.create({
|
|
334
|
+
address: rootAddress,
|
|
335
|
+
chainId: 1,
|
|
336
|
+
client: {
|
|
337
|
+
request: async () => {
|
|
338
|
+
throw new Error('network failed')
|
|
339
|
+
},
|
|
340
|
+
} as never,
|
|
341
|
+
store,
|
|
342
|
+
})
|
|
263
343
|
|
|
264
|
-
expect(
|
|
344
|
+
await expect(
|
|
345
|
+
transaction?.fill({ chainId: 1, from: rootAddress }),
|
|
346
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: network failed]`)
|
|
265
347
|
expect(store.getState().accessKeys.length).toMatchInlineSnapshot(`1`)
|
|
266
348
|
})
|
|
349
|
+
})
|
|
267
350
|
|
|
268
|
-
|
|
269
|
-
|
|
351
|
+
describe('isUnavailableError', () => {
|
|
352
|
+
test('default: recognizes unavailable key revert errors', () => {
|
|
353
|
+
expect(AccessKey.isUnavailableError(createRevert('KeyNotFound'))).toMatchInlineSnapshot(`true`)
|
|
354
|
+
expect(AccessKey.isUnavailableError(createRevert('KeyAlreadyRevoked'))).toMatchInlineSnapshot(
|
|
355
|
+
`true`,
|
|
356
|
+
)
|
|
357
|
+
expect(
|
|
358
|
+
AccessKey.isUnavailableError(createRevert('SpendingLimitExceeded')),
|
|
359
|
+
).toMatchInlineSnapshot(`false`)
|
|
360
|
+
})
|
|
270
361
|
|
|
271
|
-
|
|
362
|
+
test('behavior: recognizes nested viem error data', () => {
|
|
363
|
+
const error = new BaseError('revoke failed', {
|
|
364
|
+
cause: Object.assign(new Error('execution reverted'), {
|
|
365
|
+
data: { errorName: 'KeyAlreadyRevoked' },
|
|
366
|
+
}),
|
|
367
|
+
})
|
|
272
368
|
|
|
273
|
-
expect(
|
|
369
|
+
expect(AccessKey.isUnavailableError(error)).toMatchInlineSnapshot(`true`)
|
|
274
370
|
})
|
|
275
371
|
})
|
|
276
372
|
|
|
277
373
|
describe('generate', () => {
|
|
278
|
-
test('default: returns access key and key pair', async () => {
|
|
374
|
+
test('default: returns p256 access key and key pair', async () => {
|
|
279
375
|
const result = await AccessKey.generate()
|
|
280
376
|
|
|
281
377
|
expect(result.accessKey.address).toMatch(/^0x[0-9a-f]{40}$/i)
|
|
@@ -301,6 +397,22 @@ describe('prepareAuthorization', () => {
|
|
|
301
397
|
expect(result.keyPair).toBeDefined()
|
|
302
398
|
})
|
|
303
399
|
|
|
400
|
+
test('error: rejects secp256k1 authorization without external key material', async () => {
|
|
401
|
+
await expect(
|
|
402
|
+
AccessKey.prepareAuthorization({ chainId: 1, expiry: 123, keyType: 'secp256k1' }),
|
|
403
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
404
|
+
`[RpcResponse.InvalidParamsError: \`keyType: "secp256k1"\` requires externally generated key material; provide \`publicKey\` or \`address\`.]`,
|
|
405
|
+
)
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
test('error: rejects webAuthn authorization without external key material', async () => {
|
|
409
|
+
await expect(
|
|
410
|
+
AccessKey.prepareAuthorization({ chainId: 1, expiry: 123, keyType: 'webAuthn' }),
|
|
411
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
412
|
+
`[RpcResponse.InvalidParamsError: \`keyType: "webAuthn"\` requires externally generated key material; provide \`publicKey\` or \`address\`.]`,
|
|
413
|
+
)
|
|
414
|
+
})
|
|
415
|
+
|
|
304
416
|
test('behavior: prepares external key authorization from address', async () => {
|
|
305
417
|
const result = await AccessKey.prepareAuthorization({
|
|
306
418
|
address: accounts[1]!.address,
|
|
@@ -374,6 +486,27 @@ describe('prepareAuthorization', () => {
|
|
|
374
486
|
`)
|
|
375
487
|
})
|
|
376
488
|
|
|
489
|
+
test('behavior: prepares external secp256k1 authorization from public key', async () => {
|
|
490
|
+
const result = await AccessKey.prepareAuthorization({
|
|
491
|
+
chainId: 123n,
|
|
492
|
+
expiry: 456,
|
|
493
|
+
keyType: 'secp256k1',
|
|
494
|
+
publicKey: accounts[1]!.publicKey,
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
expect(result.keyPair).toBeUndefined()
|
|
498
|
+
expect(result.keyAuthorization).toMatchInlineSnapshot(`
|
|
499
|
+
{
|
|
500
|
+
"address": "${accounts[1]!.address.toLowerCase()}",
|
|
501
|
+
"chainId": 123n,
|
|
502
|
+
"expiry": 456,
|
|
503
|
+
"limits": undefined,
|
|
504
|
+
"scopes": undefined,
|
|
505
|
+
"type": "secp256k1",
|
|
506
|
+
}
|
|
507
|
+
`)
|
|
508
|
+
})
|
|
509
|
+
|
|
377
510
|
test('behavior: defaults external key type to secp256k1', async () => {
|
|
378
511
|
const result = await AccessKey.prepareAuthorization({
|
|
379
512
|
address: accounts[1]!.address,
|
|
@@ -385,23 +518,34 @@ describe('prepareAuthorization', () => {
|
|
|
385
518
|
})
|
|
386
519
|
})
|
|
387
520
|
|
|
388
|
-
describe('
|
|
389
|
-
test('default:
|
|
521
|
+
describe('authorize', () => {
|
|
522
|
+
test('default: prepares, signs, and saves authorization', async () => {
|
|
390
523
|
const store = createStore()
|
|
391
|
-
const
|
|
392
|
-
address: accounts[1]!.address,
|
|
393
|
-
chainId: 1,
|
|
394
|
-
expiry: 123,
|
|
395
|
-
})
|
|
524
|
+
const digests: Hex.Hex[] = []
|
|
396
525
|
const signature = `0x${'11'.repeat(32)}${'22'.repeat(32)}1b` as const
|
|
526
|
+
const account = {
|
|
527
|
+
...accounts[0]!,
|
|
528
|
+
sign: async (parameters: { hash: Hex.Hex }) => {
|
|
529
|
+
digests.push(parameters.hash)
|
|
530
|
+
return signature
|
|
531
|
+
},
|
|
532
|
+
} as TempoAccount.Account
|
|
397
533
|
|
|
398
|
-
const result = AccessKey.
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
534
|
+
const result = await AccessKey.authorize({
|
|
535
|
+
account,
|
|
536
|
+
chainId: 1,
|
|
537
|
+
parameters: {
|
|
538
|
+
address: accounts[1]!.address,
|
|
539
|
+
expiry: 123,
|
|
540
|
+
},
|
|
402
541
|
store,
|
|
403
542
|
})
|
|
404
543
|
|
|
544
|
+
expect(digests).toMatchInlineSnapshot(`
|
|
545
|
+
[
|
|
546
|
+
"0xea47721547363fc82a5dca62b4544e4718d861b3df10bfac65d30102594b5c26",
|
|
547
|
+
]
|
|
548
|
+
`)
|
|
405
549
|
expect(result).toMatchInlineSnapshot(`
|
|
406
550
|
{
|
|
407
551
|
"chainId": "0x1",
|
|
@@ -434,448 +578,298 @@ describe('saveAuthorization', () => {
|
|
|
434
578
|
})
|
|
435
579
|
})
|
|
436
580
|
|
|
437
|
-
describe('
|
|
438
|
-
|
|
581
|
+
describe('select', () => {
|
|
582
|
+
async function setup(options: { pending?: boolean | undefined } = {}) {
|
|
439
583
|
const store = createStore()
|
|
440
|
-
const
|
|
441
|
-
const
|
|
442
|
-
const
|
|
443
|
-
...accounts[0]!,
|
|
444
|
-
sign: async (parameters: { hash: Hex.Hex }) => {
|
|
445
|
-
digests.push(parameters.hash)
|
|
446
|
-
return signature
|
|
447
|
-
},
|
|
448
|
-
} as TempoAccount.Account
|
|
584
|
+
const keyPair = await WebCryptoP256.createKeyPair()
|
|
585
|
+
const accessKey = TempoAccount.fromWebCryptoP256(keyPair, { access: rootAddress })
|
|
586
|
+
const keyAuthorization = createKeyAuthorization(accessKey.accessKeyAddress)
|
|
449
587
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
expiry: 123,
|
|
456
|
-
},
|
|
588
|
+
addAuthorization({
|
|
589
|
+
address: rootAddress,
|
|
590
|
+
keyAuthorization,
|
|
591
|
+
keyPair,
|
|
592
|
+
state: options.pending ? 'pending' : 'signed',
|
|
457
593
|
store,
|
|
458
594
|
})
|
|
459
595
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
"0xea47721547363fc82a5dca62b4544e4718d861b3df10bfac65d30102594b5c26",
|
|
463
|
-
]
|
|
464
|
-
`)
|
|
465
|
-
expect(store.getState().accessKeys.length).toMatchInlineSnapshot(`1`)
|
|
466
|
-
})
|
|
467
|
-
})
|
|
596
|
+
return { accessKey, keyAuthorization, store }
|
|
597
|
+
}
|
|
468
598
|
|
|
469
|
-
|
|
470
|
-
|
|
599
|
+
test('behavior: skips access keys for another root address', async () => {
|
|
600
|
+
const store = createStore()
|
|
471
601
|
const keyPair = await WebCryptoP256.createKeyPair()
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
address:
|
|
475
|
-
|
|
602
|
+
const accessKey = TempoAccount.fromWebCryptoP256(keyPair, { access: accounts[1]!.address })
|
|
603
|
+
addAuthorization({
|
|
604
|
+
address: accounts[1]!.address,
|
|
605
|
+
keyAuthorization: createKeyAuthorization(accessKey.accessKeyAddress),
|
|
476
606
|
keyPair,
|
|
477
|
-
|
|
607
|
+
state: 'signed',
|
|
608
|
+
store,
|
|
478
609
|
})
|
|
479
610
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
expect(result.source).toMatchInlineSnapshot(`"accessKey"`)
|
|
483
|
-
})
|
|
484
|
-
|
|
485
|
-
test('behavior: hydrates private-key access key to signable account', () => {
|
|
486
|
-
const result = AccessKey.hydrate({
|
|
487
|
-
access: rootAddress,
|
|
488
|
-
address: accounts[1]!.address,
|
|
611
|
+
const result = await AccessKey.select({
|
|
612
|
+
account: rootAddress,
|
|
489
613
|
chainId: 1,
|
|
490
|
-
|
|
491
|
-
|
|
614
|
+
client: createMetadataClient(accessKey.accessKeyAddress) as never,
|
|
615
|
+
store,
|
|
492
616
|
})
|
|
493
617
|
|
|
494
|
-
expect(result.type).toMatchInlineSnapshot(`"local"`)
|
|
495
|
-
expect(typeof result.sign).toMatchInlineSnapshot(`"function"`)
|
|
496
|
-
expect(result.source).toMatchInlineSnapshot(`"accessKey"`)
|
|
497
|
-
})
|
|
498
|
-
|
|
499
|
-
test('error: throws for external access key without signer material', () => {
|
|
500
|
-
expect(() =>
|
|
501
|
-
AccessKey.hydrate({
|
|
502
|
-
access: rootAddress,
|
|
503
|
-
address: '0x0000000000000000000000000000000000000099',
|
|
504
|
-
chainId: 1,
|
|
505
|
-
keyType: 'p256',
|
|
506
|
-
}),
|
|
507
|
-
).toThrowErrorMatchingInlineSnapshot(
|
|
508
|
-
`[Provider.UnauthorizedError: External access key cannot be hydrated for signing.]`,
|
|
509
|
-
)
|
|
510
|
-
})
|
|
511
|
-
})
|
|
512
|
-
|
|
513
|
-
describe('selectAccount', () => {
|
|
514
|
-
function setup(accessKeys: readonly Store.AccessKey[] = []) {
|
|
515
|
-
const store = createStore()
|
|
516
|
-
store.setState({ accessKeys })
|
|
517
|
-
return store
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
test('default: selects locally-signable access key for root address', async () => {
|
|
521
|
-
const keyPair = await WebCryptoP256.createKeyPair()
|
|
522
|
-
const store = setup([
|
|
523
|
-
{
|
|
524
|
-
access: rootAddress,
|
|
525
|
-
address: '0x0000000000000000000000000000000000000099',
|
|
526
|
-
chainId: 1,
|
|
527
|
-
keyPair,
|
|
528
|
-
keyType: 'webCrypto',
|
|
529
|
-
},
|
|
530
|
-
])
|
|
531
|
-
|
|
532
|
-
const result = AccessKey.selectAccount({ address: rootAddress, chainId: 1, store })
|
|
533
|
-
|
|
534
|
-
expect(result?.source).toMatchInlineSnapshot(`"accessKey"`)
|
|
535
|
-
})
|
|
536
|
-
|
|
537
|
-
test('behavior: skips access keys for another root address', async () => {
|
|
538
|
-
const keyPair = await WebCryptoP256.createKeyPair()
|
|
539
|
-
const store = setup([
|
|
540
|
-
{
|
|
541
|
-
access: accounts[1]!.address,
|
|
542
|
-
address: '0x0000000000000000000000000000000000000099',
|
|
543
|
-
chainId: 1,
|
|
544
|
-
keyPair,
|
|
545
|
-
keyType: 'webCrypto',
|
|
546
|
-
},
|
|
547
|
-
])
|
|
548
|
-
|
|
549
|
-
const result = AccessKey.selectAccount({ address: rootAddress, chainId: 1, store })
|
|
550
|
-
|
|
551
618
|
expect(result).toMatchInlineSnapshot(`undefined`)
|
|
552
619
|
})
|
|
553
620
|
|
|
554
621
|
test('behavior: skips access keys for another chain', async () => {
|
|
555
|
-
const
|
|
556
|
-
const store = setup([
|
|
557
|
-
{
|
|
558
|
-
access: rootAddress,
|
|
559
|
-
address: '0x0000000000000000000000000000000000000099',
|
|
560
|
-
chainId: 1,
|
|
561
|
-
keyPair,
|
|
562
|
-
keyType: 'webCrypto',
|
|
563
|
-
},
|
|
564
|
-
])
|
|
565
|
-
|
|
566
|
-
const result = AccessKey.selectAccount({ address: rootAddress, chainId: 42_431, store })
|
|
567
|
-
|
|
568
|
-
expect(result).toMatchInlineSnapshot(`undefined`)
|
|
569
|
-
})
|
|
622
|
+
const { accessKey, store } = await setup()
|
|
570
623
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
keyType: 'p256',
|
|
578
|
-
},
|
|
579
|
-
])
|
|
580
|
-
|
|
581
|
-
const result = AccessKey.selectAccount({ address: rootAddress, chainId: 1, store })
|
|
624
|
+
const result = await AccessKey.select({
|
|
625
|
+
account: rootAddress,
|
|
626
|
+
chainId: 42_431,
|
|
627
|
+
client: createMetadataClient(accessKey.accessKeyAddress) as never,
|
|
628
|
+
store,
|
|
629
|
+
})
|
|
582
630
|
|
|
583
631
|
expect(result).toMatchInlineSnapshot(`undefined`)
|
|
584
632
|
})
|
|
585
633
|
|
|
586
|
-
test('behavior:
|
|
587
|
-
const
|
|
588
|
-
const
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
keyType: 'webCrypto',
|
|
596
|
-
},
|
|
597
|
-
])
|
|
634
|
+
test('behavior: skips external access keys without signer material', async () => {
|
|
635
|
+
const store = createStore()
|
|
636
|
+
const keyAuthorization = createKeyAuthorization('0x0000000000000000000000000000000000000099')
|
|
637
|
+
addAuthorization({
|
|
638
|
+
address: rootAddress,
|
|
639
|
+
keyAuthorization,
|
|
640
|
+
state: 'signed',
|
|
641
|
+
store,
|
|
642
|
+
})
|
|
598
643
|
|
|
599
|
-
const result = AccessKey.
|
|
644
|
+
const result = await AccessKey.select({
|
|
645
|
+
account: rootAddress,
|
|
646
|
+
chainId: 1,
|
|
647
|
+
client: createMetadataClient(keyAuthorization.address) as never,
|
|
648
|
+
store,
|
|
649
|
+
})
|
|
600
650
|
|
|
601
651
|
expect(result).toMatchInlineSnapshot(`undefined`)
|
|
602
|
-
expect(store.getState().accessKeys).toMatchInlineSnapshot(`[]`)
|
|
603
652
|
})
|
|
604
653
|
|
|
605
|
-
test('behavior:
|
|
654
|
+
test('behavior: matches access key scopes against transaction calls', async () => {
|
|
655
|
+
const store = createStore()
|
|
606
656
|
const keyPair = await WebCryptoP256.createKeyPair()
|
|
607
|
-
const
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
const result = AccessKey.selectAccount({ address: rootAddress, chainId: 1, store })
|
|
657
|
+
const accessKey = TempoAccount.fromWebCryptoP256(keyPair, { access: rootAddress })
|
|
658
|
+
const token = '0x0000000000000000000000000000000000000abc' as const
|
|
659
|
+
addAuthorization({
|
|
660
|
+
address: rootAddress,
|
|
661
|
+
keyAuthorization: createKeyAuthorization(accessKey.accessKeyAddress, {
|
|
662
|
+
scopes: [{ address: token, selector: 'transfer(address,uint256)' }],
|
|
663
|
+
}),
|
|
664
|
+
keyPair,
|
|
665
|
+
state: 'signed',
|
|
666
|
+
store,
|
|
667
|
+
})
|
|
619
668
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
669
|
+
const match = await AccessKey.select({
|
|
670
|
+
account: rootAddress,
|
|
671
|
+
calls: [{ to: token, data: '0xa9059cbb0000000000000000000000000000000000000001' }],
|
|
672
|
+
chainId: 1,
|
|
673
|
+
client: createMetadataClient(accessKey.accessKeyAddress) as never,
|
|
674
|
+
store,
|
|
675
|
+
})
|
|
676
|
+
const miss = await AccessKey.select({
|
|
677
|
+
account: rootAddress,
|
|
678
|
+
calls: [{ to: '0x0000000000000000000000000000000000000def', data: '0xdeadbeef' }],
|
|
679
|
+
chainId: 1,
|
|
680
|
+
client: createMetadataClient(accessKey.accessKeyAddress) as never,
|
|
681
|
+
store,
|
|
682
|
+
})
|
|
623
683
|
|
|
624
|
-
|
|
625
|
-
const keyPair = await WebCryptoP256.createKeyPair()
|
|
626
|
-
const store = setup([
|
|
684
|
+
expect({ match: !!match, miss: !!miss }).toMatchInlineSnapshot(`
|
|
627
685
|
{
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
keyPair,
|
|
632
|
-
keyType: 'webCrypto',
|
|
633
|
-
limits: [
|
|
634
|
-
{
|
|
635
|
-
token: '0x0000000000000000000000000000000000000abc',
|
|
636
|
-
limit: 1000n,
|
|
637
|
-
},
|
|
638
|
-
],
|
|
639
|
-
},
|
|
640
|
-
])
|
|
641
|
-
|
|
642
|
-
const result = AccessKey.selectAccount({ address: rootAddress, chainId: 1, store })
|
|
643
|
-
|
|
644
|
-
expect(result?.source).toMatchInlineSnapshot(`"accessKey"`)
|
|
645
|
-
expect(store.getState().accessKeys[0]?.limits).toMatchInlineSnapshot(`
|
|
646
|
-
[
|
|
647
|
-
{
|
|
648
|
-
"limit": 1000n,
|
|
649
|
-
"token": "0x0000000000000000000000000000000000000abc",
|
|
650
|
-
},
|
|
651
|
-
]
|
|
686
|
+
"match": true,
|
|
687
|
+
"miss": false,
|
|
688
|
+
}
|
|
652
689
|
`)
|
|
653
690
|
})
|
|
691
|
+
})
|
|
654
692
|
|
|
655
|
-
|
|
693
|
+
describe('create', () => {
|
|
694
|
+
async function setup(options: { pending?: boolean | undefined } = {}) {
|
|
695
|
+
const store = createStore()
|
|
656
696
|
const keyPair = await WebCryptoP256.createKeyPair()
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
access: rootAddress,
|
|
660
|
-
address: '0x0000000000000000000000000000000000000099',
|
|
661
|
-
chainId: 1,
|
|
662
|
-
keyPair,
|
|
663
|
-
keyType: 'webCrypto',
|
|
664
|
-
},
|
|
665
|
-
])
|
|
697
|
+
const accessKey = TempoAccount.fromWebCryptoP256(keyPair, { access: rootAddress })
|
|
698
|
+
const keyAuthorization = createKeyAuthorization(accessKey.accessKeyAddress)
|
|
666
699
|
|
|
667
|
-
|
|
700
|
+
addAuthorization({
|
|
668
701
|
address: rootAddress,
|
|
669
|
-
|
|
702
|
+
keyAuthorization,
|
|
703
|
+
keyPair,
|
|
704
|
+
state: options.pending ? 'pending' : 'signed',
|
|
670
705
|
store,
|
|
671
|
-
calls: [{ to: '0x0000000000000000000000000000000000000abc', data: '0xa9059cbb' }],
|
|
672
706
|
})
|
|
673
707
|
|
|
674
|
-
|
|
675
|
-
}
|
|
708
|
+
return { accessKey, keyAuthorization, store }
|
|
709
|
+
}
|
|
676
710
|
|
|
677
|
-
test('behavior:
|
|
678
|
-
const
|
|
679
|
-
const
|
|
680
|
-
const store = setup([
|
|
681
|
-
{
|
|
682
|
-
access: rootAddress,
|
|
683
|
-
address: '0x0000000000000000000000000000000000000099',
|
|
684
|
-
chainId: 1,
|
|
685
|
-
keyPair,
|
|
686
|
-
keyType: 'webCrypto',
|
|
687
|
-
scopes: [{ address: token, selector: '0xa9059cbb' }],
|
|
688
|
-
},
|
|
689
|
-
])
|
|
711
|
+
test('behavior: returns undefined when no matching access key exists', async () => {
|
|
712
|
+
const store = createStore()
|
|
713
|
+
const { client } = createFillClient(accounts[1]!.address)
|
|
690
714
|
|
|
691
|
-
const result =
|
|
715
|
+
const result = await AccessKeyTransaction.create({
|
|
692
716
|
address: rootAddress,
|
|
693
717
|
chainId: 1,
|
|
718
|
+
client: client as never,
|
|
694
719
|
store,
|
|
695
|
-
calls: [{ to: token, data: '0xa9059cbb0000000000000000000000000000000000000001' }],
|
|
696
720
|
})
|
|
697
721
|
|
|
698
|
-
expect(result
|
|
722
|
+
expect(result).toMatchInlineSnapshot(`undefined`)
|
|
699
723
|
})
|
|
700
724
|
|
|
701
|
-
test('
|
|
702
|
-
const
|
|
703
|
-
const
|
|
704
|
-
const store = setup([
|
|
705
|
-
{
|
|
706
|
-
access: rootAddress,
|
|
707
|
-
address: '0x0000000000000000000000000000000000000099',
|
|
708
|
-
chainId: 1,
|
|
709
|
-
keyPair,
|
|
710
|
-
keyType: 'webCrypto',
|
|
711
|
-
scopes: [{ address: token, selector: '0xa9059cbb' }],
|
|
712
|
-
},
|
|
713
|
-
])
|
|
725
|
+
test('default: returns selected account with pending key authorization', async () => {
|
|
726
|
+
const { keyAuthorization, store } = await setup()
|
|
727
|
+
const { client, requests } = createFillClient(keyAuthorization.address)
|
|
714
728
|
|
|
715
|
-
const result =
|
|
729
|
+
const result = await AccessKeyTransaction.create({
|
|
716
730
|
address: rootAddress,
|
|
717
731
|
chainId: 1,
|
|
732
|
+
client: client as never,
|
|
718
733
|
store,
|
|
719
|
-
calls: [{ to: '0x0000000000000000000000000000000000000def', data: '0xdeadbeef' }],
|
|
720
734
|
})
|
|
735
|
+
await result?.fill({ chainId: 1, from: rootAddress })
|
|
721
736
|
|
|
722
|
-
expect(result).toMatchInlineSnapshot(`
|
|
737
|
+
expect(!!result).toMatchInlineSnapshot(`true`)
|
|
738
|
+
const request = requests[0] as {
|
|
739
|
+
params: readonly [{ keyAuthorization?: { keyId?: string | undefined } | undefined }]
|
|
740
|
+
}
|
|
741
|
+
expect(request.params[0].keyAuthorization?.keyId).toBe(keyAuthorization.address)
|
|
723
742
|
})
|
|
724
743
|
|
|
725
|
-
test('behavior:
|
|
726
|
-
const
|
|
727
|
-
const
|
|
728
|
-
const store = setup([
|
|
729
|
-
{
|
|
730
|
-
access: rootAddress,
|
|
731
|
-
address: '0x0000000000000000000000000000000000000099',
|
|
732
|
-
chainId: 1,
|
|
733
|
-
keyPair,
|
|
734
|
-
keyType: 'webCrypto',
|
|
735
|
-
scopes: [{ address: token, selector: 'transfer(address,uint256)' }],
|
|
736
|
-
},
|
|
737
|
-
])
|
|
738
|
-
|
|
739
|
-
const result = AccessKey.selectAccount({
|
|
744
|
+
test('behavior: signing prepared transaction marks key authorization pending', async () => {
|
|
745
|
+
const { accessKey, keyAuthorization, store } = await setup()
|
|
746
|
+
const result = await AccessKeyTransaction.create({
|
|
740
747
|
address: rootAddress,
|
|
741
748
|
chainId: 1,
|
|
749
|
+
client: { chain: { id: 1 } } as never,
|
|
742
750
|
store,
|
|
743
|
-
calls: [{ to: token, data: '0xa9059cbb0000000000000000000000000000000000000001' }],
|
|
744
751
|
})
|
|
745
752
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
{
|
|
755
|
-
access: rootAddress,
|
|
756
|
-
address: '0x0000000000000000000000000000000000000099',
|
|
757
|
-
chainId: 1,
|
|
758
|
-
keyPair,
|
|
759
|
-
keyType: 'webCrypto',
|
|
760
|
-
scopes: [
|
|
761
|
-
{ address: token, selector: 'transfer(address,uint256)', recipients: [recipient] },
|
|
762
|
-
],
|
|
763
|
-
},
|
|
764
|
-
])
|
|
765
|
-
const call = Actions.token.transfer.call({
|
|
766
|
-
amount: 1n,
|
|
767
|
-
to: recipient,
|
|
768
|
-
token,
|
|
753
|
+
const prepared = await result?.prepare({
|
|
754
|
+
from: rootAddress,
|
|
755
|
+
gas: 21_000n,
|
|
756
|
+
maxFeePerGas: 1n,
|
|
757
|
+
maxPriorityFeePerGas: 1n,
|
|
758
|
+
nonce: 0,
|
|
759
|
+
to: accounts[1]!.address,
|
|
760
|
+
value: 1n,
|
|
769
761
|
})
|
|
762
|
+
await prepared?.sign()
|
|
763
|
+
|
|
764
|
+
const stored = getStored(accessKey, store)
|
|
765
|
+
expect(stored?.keyAuthorization).toBe(keyAuthorization)
|
|
766
|
+
expect(stored?.keyAuthorizationPending).toMatchInlineSnapshot(`true`)
|
|
767
|
+
})
|
|
770
768
|
|
|
771
|
-
|
|
769
|
+
test('behavior: sending asynchronously keeps key authorization pending', async () => {
|
|
770
|
+
const { accessKey, keyAuthorization, store } = await setup()
|
|
771
|
+
const requests: unknown[] = []
|
|
772
|
+
const result = await AccessKeyTransaction.create({
|
|
772
773
|
address: rootAddress,
|
|
773
774
|
chainId: 1,
|
|
775
|
+
client: {
|
|
776
|
+
chain: { id: 1 },
|
|
777
|
+
request: async (request: unknown) => {
|
|
778
|
+
requests.push(request)
|
|
779
|
+
return '0xtransaction'
|
|
780
|
+
},
|
|
781
|
+
} as never,
|
|
774
782
|
store,
|
|
775
|
-
calls: [call],
|
|
776
783
|
})
|
|
777
784
|
|
|
778
|
-
|
|
785
|
+
const prepared = await result?.prepare({
|
|
786
|
+
from: rootAddress,
|
|
787
|
+
gas: 21_000n,
|
|
788
|
+
maxFeePerGas: 1n,
|
|
789
|
+
maxPriorityFeePerGas: 1n,
|
|
790
|
+
nonce: 0,
|
|
791
|
+
to: accounts[1]!.address,
|
|
792
|
+
value: 1n,
|
|
793
|
+
})
|
|
794
|
+
await prepared?.send()
|
|
795
|
+
|
|
796
|
+
const stored = getStored(accessKey, store)
|
|
797
|
+
expect(stored?.keyAuthorization).toBe(keyAuthorization)
|
|
798
|
+
expect(stored?.keyAuthorizationPending).toMatchInlineSnapshot(`true`)
|
|
799
|
+
expect(requests.map((request) => (request as { method: string }).method))
|
|
800
|
+
.toMatchInlineSnapshot(`
|
|
801
|
+
[
|
|
802
|
+
"eth_fillTransaction",
|
|
803
|
+
"eth_sendRawTransaction",
|
|
804
|
+
]
|
|
805
|
+
`)
|
|
779
806
|
})
|
|
780
807
|
|
|
781
|
-
test('behavior:
|
|
782
|
-
const
|
|
783
|
-
const token = '0x0000000000000000000000000000000000000abc' as const
|
|
784
|
-
const store = setup([
|
|
785
|
-
{
|
|
786
|
-
access: rootAddress,
|
|
787
|
-
address: '0x0000000000000000000000000000000000000099',
|
|
788
|
-
chainId: 1,
|
|
789
|
-
keyPair,
|
|
790
|
-
keyType: 'webCrypto',
|
|
791
|
-
scopes: [
|
|
792
|
-
{
|
|
793
|
-
address: token,
|
|
794
|
-
selector: 'transfer(address,uint256)',
|
|
795
|
-
recipients: ['0x0000000000000000000000000000000000000def'],
|
|
796
|
-
},
|
|
797
|
-
],
|
|
798
|
-
},
|
|
799
|
-
])
|
|
800
|
-
const call = Actions.token.transfer.call({
|
|
801
|
-
amount: 1n,
|
|
802
|
-
to: '0x0000000000000000000000000000000000000fed',
|
|
803
|
-
token,
|
|
804
|
-
})
|
|
808
|
+
test('behavior: clears pending authorization when pending key is published on-chain', async () => {
|
|
809
|
+
const { accessKey, store } = await setup({ pending: true })
|
|
805
810
|
|
|
806
|
-
const result =
|
|
811
|
+
const result = await AccessKeyTransaction.create({
|
|
807
812
|
address: rootAddress,
|
|
808
813
|
chainId: 1,
|
|
814
|
+
client: createMetadataClient(accessKey.accessKeyAddress) as never,
|
|
809
815
|
store,
|
|
810
|
-
calls: [call],
|
|
811
816
|
})
|
|
812
817
|
|
|
813
|
-
expect(result).toMatchInlineSnapshot(`
|
|
818
|
+
expect(!!result).toMatchInlineSnapshot(`true`)
|
|
819
|
+
expect(store.getState().accessKeys[0]!.keyAuthorization).toMatchInlineSnapshot(`undefined`)
|
|
820
|
+
expect(store.getState().accessKeys[0]!.keyAuthorizationPending).toMatchInlineSnapshot(
|
|
821
|
+
`undefined`,
|
|
822
|
+
)
|
|
814
823
|
})
|
|
815
824
|
|
|
816
|
-
test('behavior:
|
|
817
|
-
const
|
|
818
|
-
const
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
address: '0x0000000000000000000000000000000000000099',
|
|
822
|
-
chainId: 1,
|
|
823
|
-
keyPair,
|
|
824
|
-
keyType: 'webCrypto',
|
|
825
|
-
scopes: [{}],
|
|
826
|
-
} as never,
|
|
827
|
-
])
|
|
825
|
+
test('behavior: reuses pending authorization when direct key check is missing', async () => {
|
|
826
|
+
const { accessKey, keyAuthorization, store } = await setup({ pending: true })
|
|
827
|
+
const { client, requests } = createFillClient(accessKey.accessKeyAddress, {
|
|
828
|
+
keyId: accounts[1]!.address,
|
|
829
|
+
})
|
|
828
830
|
|
|
829
|
-
const result =
|
|
831
|
+
const result = await AccessKeyTransaction.create({
|
|
830
832
|
address: rootAddress,
|
|
831
833
|
chainId: 1,
|
|
834
|
+
client: client as never,
|
|
832
835
|
store,
|
|
833
|
-
calls: [{ to: '0x0000000000000000000000000000000000000abc', data: '0xa9059cbb' }],
|
|
834
836
|
})
|
|
837
|
+
await result?.fill({ chainId: 1, from: rootAddress })
|
|
835
838
|
|
|
836
|
-
|
|
839
|
+
const request = requests[0] as {
|
|
840
|
+
params: readonly [{ keyAuthorization?: { keyId?: string | undefined } | undefined }]
|
|
841
|
+
}
|
|
842
|
+
expect(request.params[0].keyAuthorization?.keyId).toBe(keyAuthorization.address)
|
|
843
|
+
expect(store.getState().accessKeys[0]!.keyAuthorization).toBe(keyAuthorization)
|
|
844
|
+
expect(store.getState().accessKeys[0]!.keyAuthorizationPending).toMatchInlineSnapshot(`true`)
|
|
837
845
|
})
|
|
838
846
|
|
|
839
|
-
test('behavior:
|
|
840
|
-
const
|
|
841
|
-
const
|
|
842
|
-
const store = setup([
|
|
843
|
-
{
|
|
844
|
-
access: rootAddress,
|
|
845
|
-
address: '0x0000000000000000000000000000000000000099',
|
|
846
|
-
chainId: 1,
|
|
847
|
-
keyPair,
|
|
848
|
-
keyType: 'webCrypto',
|
|
849
|
-
scopes: [{ address: token }],
|
|
850
|
-
},
|
|
851
|
-
])
|
|
847
|
+
test('behavior: reuses pending authorization when direct key check fails', async () => {
|
|
848
|
+
const { keyAuthorization, store } = await setup({ pending: true })
|
|
849
|
+
const requests: unknown[] = []
|
|
852
850
|
|
|
853
|
-
const result =
|
|
851
|
+
const result = await AccessKeyTransaction.create({
|
|
854
852
|
address: rootAddress,
|
|
855
853
|
chainId: 1,
|
|
854
|
+
client: {
|
|
855
|
+
call: async () => {
|
|
856
|
+
throw new Error('network failed')
|
|
857
|
+
},
|
|
858
|
+
request: async (request: unknown) => {
|
|
859
|
+
requests.push(request)
|
|
860
|
+
return { capabilities: { sponsored: false }, tx: {} }
|
|
861
|
+
},
|
|
862
|
+
} as never,
|
|
856
863
|
store,
|
|
857
|
-
calls: [{ to: token, data: '0xdeadbeef' }],
|
|
858
864
|
})
|
|
865
|
+
await result?.fill({ chainId: 1, from: rootAddress })
|
|
859
866
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
{
|
|
867
|
-
access: rootAddress,
|
|
868
|
-
address: '0x0000000000000000000000000000000000000099',
|
|
869
|
-
chainId: 1,
|
|
870
|
-
keyPair,
|
|
871
|
-
keyType: 'webCrypto',
|
|
872
|
-
scopes: [{ address: '0x0000000000000000000000000000000000000abc' }],
|
|
873
|
-
},
|
|
874
|
-
])
|
|
875
|
-
|
|
876
|
-
const result = AccessKey.selectAccount({ address: rootAddress, chainId: 1, store })
|
|
877
|
-
|
|
878
|
-
expect(result).toMatchInlineSnapshot(`undefined`)
|
|
867
|
+
const request = requests[0] as {
|
|
868
|
+
params: readonly [{ keyAuthorization?: { keyId?: string | undefined } | undefined }]
|
|
869
|
+
}
|
|
870
|
+
expect(request.params[0].keyAuthorization?.keyId).toBe(keyAuthorization.address)
|
|
871
|
+
expect(store.getState().accessKeys[0]!.keyAuthorization).toBe(keyAuthorization)
|
|
872
|
+
expect(store.getState().accessKeys[0]!.keyAuthorizationPending).toMatchInlineSnapshot(`true`)
|
|
879
873
|
})
|
|
880
874
|
})
|
|
881
875
|
|
|
@@ -886,11 +880,18 @@ describe('getStatus', () => {
|
|
|
886
880
|
const accessKey = TempoAccount.fromWebCryptoP256(keyPair)
|
|
887
881
|
const keyAuthorization = createKeyAuthorization(accessKey.address)
|
|
888
882
|
|
|
889
|
-
|
|
883
|
+
addAuthorization({
|
|
884
|
+
address: rootAddress,
|
|
885
|
+
keyAuthorization,
|
|
886
|
+
keyPair,
|
|
887
|
+
state: 'signed',
|
|
888
|
+
store,
|
|
889
|
+
})
|
|
890
890
|
|
|
891
891
|
const result = await AccessKey.getStatus({
|
|
892
|
-
|
|
892
|
+
account: rootAddress,
|
|
893
893
|
chainId: 1,
|
|
894
|
+
client: createMetadataClient(accessKey.address) as never,
|
|
894
895
|
store,
|
|
895
896
|
})
|
|
896
897
|
|
|
@@ -903,96 +904,98 @@ describe('getStatus', () => {
|
|
|
903
904
|
const accessKey = TempoAccount.fromWebCryptoP256(keyPair, { access: rootAddress })
|
|
904
905
|
const keyAuthorization = createKeyAuthorization(accessKey.accessKeyAddress)
|
|
905
906
|
|
|
906
|
-
|
|
907
|
-
|
|
907
|
+
addAuthorization({
|
|
908
|
+
address: rootAddress,
|
|
909
|
+
keyAuthorization,
|
|
910
|
+
keyPair,
|
|
911
|
+
state: 'signed',
|
|
912
|
+
store,
|
|
913
|
+
})
|
|
914
|
+
markPublished({ accessKey: accessKey.accessKeyAddress, store })
|
|
908
915
|
|
|
909
916
|
const result = await AccessKey.getStatus({
|
|
910
|
-
|
|
917
|
+
account: rootAddress,
|
|
911
918
|
chainId: 1,
|
|
919
|
+
client: createMetadataClient(accessKey.accessKeyAddress) as never,
|
|
912
920
|
store,
|
|
913
921
|
})
|
|
914
922
|
|
|
915
923
|
expect(result).toMatchInlineSnapshot(`"published"`)
|
|
916
924
|
})
|
|
917
925
|
|
|
918
|
-
test('behavior:
|
|
926
|
+
test('behavior: checks pending authorization before returning pending', async () => {
|
|
919
927
|
const store = createStore()
|
|
920
928
|
const keyPair = await WebCryptoP256.createKeyPair()
|
|
921
|
-
const accessKey = TempoAccount.fromWebCryptoP256(keyPair)
|
|
922
|
-
const keyAuthorization = createKeyAuthorization(accessKey.
|
|
929
|
+
const accessKey = TempoAccount.fromWebCryptoP256(keyPair, { access: rootAddress })
|
|
930
|
+
const keyAuthorization = createKeyAuthorization(accessKey.accessKeyAddress)
|
|
923
931
|
|
|
924
|
-
|
|
932
|
+
addAuthorization({
|
|
933
|
+
address: rootAddress,
|
|
934
|
+
keyAuthorization,
|
|
935
|
+
keyPair,
|
|
936
|
+
state: 'pending',
|
|
937
|
+
store,
|
|
938
|
+
})
|
|
925
939
|
|
|
926
940
|
const result = await AccessKey.getStatus({
|
|
927
|
-
|
|
941
|
+
account: rootAddress,
|
|
928
942
|
chainId: 1,
|
|
929
|
-
|
|
943
|
+
client: createMetadataClient(accessKey.accessKeyAddress) as never,
|
|
930
944
|
store,
|
|
931
945
|
})
|
|
932
946
|
|
|
933
|
-
expect(result).toMatchInlineSnapshot(`"
|
|
947
|
+
expect(result).toMatchInlineSnapshot(`"published"`)
|
|
934
948
|
})
|
|
935
949
|
|
|
936
|
-
test('behavior: returns
|
|
950
|
+
test('behavior: returns expired for expired local key', async () => {
|
|
937
951
|
const store = createStore()
|
|
938
952
|
const keyPair = await WebCryptoP256.createKeyPair()
|
|
939
953
|
const accessKey = TempoAccount.fromWebCryptoP256(keyPair)
|
|
940
|
-
const keyAuthorization = createKeyAuthorization(accessKey.address, {
|
|
941
|
-
scopes: [{ address: '0x0000000000000000000000000000000000000abc' }],
|
|
942
|
-
})
|
|
954
|
+
const keyAuthorization = createKeyAuthorization(accessKey.address, { expiry: 100 })
|
|
943
955
|
|
|
944
|
-
|
|
956
|
+
addAuthorization({
|
|
957
|
+
address: rootAddress,
|
|
958
|
+
keyAuthorization,
|
|
959
|
+
keyPair,
|
|
960
|
+
state: 'signed',
|
|
961
|
+
store,
|
|
962
|
+
})
|
|
945
963
|
|
|
946
964
|
const result = await AccessKey.getStatus({
|
|
947
|
-
|
|
948
|
-
calls: [{ to: '0x0000000000000000000000000000000000000def', data: '0xdeadbeef' }],
|
|
965
|
+
account: rootAddress,
|
|
949
966
|
chainId: 1,
|
|
967
|
+
client: createMetadataClient(accessKey.address) as never,
|
|
968
|
+
now: 101,
|
|
950
969
|
store,
|
|
951
970
|
})
|
|
952
971
|
|
|
953
|
-
expect(result).toMatchInlineSnapshot(`"
|
|
972
|
+
expect(result).toMatchInlineSnapshot(`"expired"`)
|
|
954
973
|
})
|
|
955
|
-
})
|
|
956
974
|
|
|
957
|
-
|
|
958
|
-
test('default: removes access keys by root address', async () => {
|
|
975
|
+
test('behavior: returns missing when no local key matches the policy', async () => {
|
|
959
976
|
const store = createStore()
|
|
960
977
|
const keyPair = await WebCryptoP256.createKeyPair()
|
|
961
978
|
const accessKey = TempoAccount.fromWebCryptoP256(keyPair)
|
|
962
|
-
const keyAuthorization = createKeyAuthorization(accessKey.address
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
expect(store.getState().accessKeys.length).toMatchInlineSnapshot(`1`)
|
|
966
|
-
|
|
967
|
-
AccessKey.revoke({ address: rootAddress, store })
|
|
968
|
-
|
|
969
|
-
expect(store.getState().accessKeys).toMatchInlineSnapshot(`[]`)
|
|
970
|
-
})
|
|
971
|
-
|
|
972
|
-
test('behavior: only removes keys for matching root address', async () => {
|
|
973
|
-
const store = createStore()
|
|
974
|
-
const otherRoot = accounts[1]!.address
|
|
975
|
-
const keyPair1 = await WebCryptoP256.createKeyPair()
|
|
976
|
-
const keyPair2 = await WebCryptoP256.createKeyPair()
|
|
977
|
-
const ak1 = TempoAccount.fromWebCryptoP256(keyPair1)
|
|
978
|
-
const ak2 = TempoAccount.fromWebCryptoP256(keyPair2)
|
|
979
|
+
const keyAuthorization = createKeyAuthorization(accessKey.address, {
|
|
980
|
+
scopes: [{ address: '0x0000000000000000000000000000000000000abc' }],
|
|
981
|
+
})
|
|
979
982
|
|
|
980
|
-
|
|
983
|
+
addAuthorization({
|
|
981
984
|
address: rootAddress,
|
|
982
|
-
keyAuthorization
|
|
985
|
+
keyAuthorization,
|
|
986
|
+
keyPair,
|
|
987
|
+
state: 'signed',
|
|
983
988
|
store,
|
|
984
989
|
})
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
990
|
+
|
|
991
|
+
const result = await AccessKey.getStatus({
|
|
992
|
+
account: rootAddress,
|
|
993
|
+
calls: [{ to: '0x0000000000000000000000000000000000000def', data: '0xdeadbeef' }],
|
|
994
|
+
chainId: 1,
|
|
995
|
+
client: createMissingClient() as never,
|
|
988
996
|
store,
|
|
989
997
|
})
|
|
990
998
|
|
|
991
|
-
expect(
|
|
992
|
-
|
|
993
|
-
AccessKey.revoke({ address: rootAddress, store })
|
|
994
|
-
|
|
995
|
-
expect(store.getState().accessKeys.length).toMatchInlineSnapshot(`1`)
|
|
996
|
-
expect(store.getState().accessKeys[0]!.access).toBe(otherRoot)
|
|
999
|
+
expect(result).toMatchInlineSnapshot(`"missing"`)
|
|
997
1000
|
})
|
|
998
1001
|
})
|