accounts 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/cli/adapter.d.ts +0 -2
- package/dist/cli/adapter.d.ts.map +1 -1
- package/dist/cli/adapter.js +96 -72
- package/dist/cli/adapter.js.map +1 -1
- package/dist/core/Adapter.d.ts +6 -3
- package/dist/core/Adapter.d.ts.map +1 -1
- package/dist/core/Provider.d.ts.map +1 -1
- package/dist/core/Provider.js +14 -9
- package/dist/core/Provider.js.map +1 -1
- package/dist/core/Schema.d.ts +29 -23
- 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 +15 -4
- 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 +4 -1
- package/dist/core/adapters/local.js.map +1 -1
- package/dist/core/zod/rpc.d.ts +17 -14
- package/dist/core/zod/rpc.d.ts.map +1 -1
- package/dist/core/zod/rpc.js +6 -2
- package/dist/core/zod/rpc.js.map +1 -1
- package/dist/server/CliAuth.d.ts +1 -0
- package/dist/server/CliAuth.d.ts.map +1 -1
- package/dist/server/CliAuth.js +3 -1
- package/dist/server/CliAuth.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/Provider.test.ts +173 -12
- package/src/cli/adapter.ts +112 -80
- package/src/core/Adapter.ts +6 -3
- package/src/core/Provider.browser.test.ts +8 -7
- package/src/core/Provider.connect.browser.test.ts +3 -3
- package/src/core/Provider.test.ts +24 -10
- package/src/core/Provider.ts +12 -7
- package/src/core/Schema.test-d.ts +5 -12
- package/src/core/adapters/dialog.ts +20 -4
- package/src/core/adapters/local.ts +5 -2
- package/src/core/zod/rpc.ts +7 -2
- package/src/server/CliAuth.ts +2 -1
|
@@ -998,15 +998,29 @@ describe.each(adapters)('$name', ({ adapter }: (typeof adapters)[number]) => {
|
|
|
998
998
|
})
|
|
999
999
|
|
|
1000
1000
|
describe('wallet_authorizeAccessKey', () => {
|
|
1001
|
+
test('behavior: without publicKey or address requires a connected account', async () => {
|
|
1002
|
+
const provider = Provider.create({ adapter: adapter(), chains: [chain] })
|
|
1003
|
+
|
|
1004
|
+
await expect(
|
|
1005
|
+
provider.request({
|
|
1006
|
+
method: 'wallet_authorizeAccessKey',
|
|
1007
|
+
params: [{ expiry: Expiry.days(1) }],
|
|
1008
|
+
}),
|
|
1009
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
1010
|
+
`[Provider.DisconnectedError: No active account.]`,
|
|
1011
|
+
)
|
|
1012
|
+
})
|
|
1013
|
+
|
|
1001
1014
|
test('default: grants an access key and returns its address', async () => {
|
|
1002
1015
|
const provider = Provider.create({ adapter: adapter(), chains: [chain] })
|
|
1003
|
-
await connect(provider)
|
|
1016
|
+
const rootAddress = await connect(provider)
|
|
1004
1017
|
|
|
1005
1018
|
const result = await provider.request({
|
|
1006
1019
|
method: 'wallet_authorizeAccessKey',
|
|
1007
1020
|
params: [{ expiry: Expiry.days(1) }],
|
|
1008
1021
|
})
|
|
1009
|
-
expect(result.keyId).toMatch(/^0x[0-9a-fA-F]{40}$/)
|
|
1022
|
+
expect(result.keyAuthorization.keyId).toMatch(/^0x[0-9a-fA-F]{40}$/)
|
|
1023
|
+
expect(result.rootAddress).toBe(rootAddress)
|
|
1010
1024
|
})
|
|
1011
1025
|
|
|
1012
1026
|
test('behavior: granted access key is used for sendTransactionSync', async () => {
|
|
@@ -1035,8 +1049,8 @@ describe.each(adapters)('$name', ({ adapter }: (typeof adapters)[number]) => {
|
|
|
1035
1049
|
method: 'wallet_authorizeAccessKey',
|
|
1036
1050
|
params: [{ expiry }],
|
|
1037
1051
|
})
|
|
1038
|
-
expect(result.keyId).toMatch(/^0x[0-9a-fA-F]{40}$/)
|
|
1039
|
-
expect(result.expiry).toBe(Hex.fromNumber(expiry))
|
|
1052
|
+
expect(result.keyAuthorization.keyId).toMatch(/^0x[0-9a-fA-F]{40}$/)
|
|
1053
|
+
expect(result.keyAuthorization.expiry).toBe(Hex.fromNumber(expiry))
|
|
1040
1054
|
})
|
|
1041
1055
|
|
|
1042
1056
|
test('behavior: expired access key falls back to root account', async () => {
|
|
@@ -1086,7 +1100,7 @@ describe.each(adapters)('$name', ({ adapter }: (typeof adapters)[number]) => {
|
|
|
1086
1100
|
},
|
|
1087
1101
|
],
|
|
1088
1102
|
})
|
|
1089
|
-
expect(result.limits).toMatchInlineSnapshot(`
|
|
1103
|
+
expect(result.keyAuthorization.limits).toMatchInlineSnapshot(`
|
|
1090
1104
|
[
|
|
1091
1105
|
{
|
|
1092
1106
|
"limit": "0x4c4b40",
|
|
@@ -1162,14 +1176,14 @@ describe.each(adapters)('$name', ({ adapter }: (typeof adapters)[number]) => {
|
|
|
1162
1176
|
await connect(provider)
|
|
1163
1177
|
|
|
1164
1178
|
const connected = (await provider.request({ method: 'eth_accounts' }))[0]!
|
|
1165
|
-
const {
|
|
1179
|
+
const { keyAuthorization } = await provider.request({
|
|
1166
1180
|
method: 'wallet_authorizeAccessKey',
|
|
1167
1181
|
params: [{ expiry: Expiry.days(1) }],
|
|
1168
1182
|
})
|
|
1169
1183
|
|
|
1170
1184
|
await provider.request({
|
|
1171
1185
|
method: 'wallet_revokeAccessKey',
|
|
1172
|
-
params: [{ address: connected, accessKeyAddress:
|
|
1186
|
+
params: [{ address: connected, accessKeyAddress: keyAuthorization.address }],
|
|
1173
1187
|
})
|
|
1174
1188
|
|
|
1175
1189
|
// After revoking, sendTransactionSync should use root key (still works)
|
|
@@ -1344,8 +1358,8 @@ describe.each(adapters)('$name', ({ adapter }: (typeof adapters)[number]) => {
|
|
|
1344
1358
|
method: 'wallet_authorizeAccessKey',
|
|
1345
1359
|
params: [{ ...accessKeyAccount, expiry: Expiry.days(1) }],
|
|
1346
1360
|
})
|
|
1347
|
-
expect(result.keyId).toBe(accessKeyAccount.address)
|
|
1348
|
-
expect(result.keyType).toBe('p256')
|
|
1361
|
+
expect(result.keyAuthorization.keyId).toBe(accessKeyAccount.address)
|
|
1362
|
+
expect(result.keyAuthorization.keyType).toBe('p256')
|
|
1349
1363
|
})
|
|
1350
1364
|
|
|
1351
1365
|
test('behavior: external key authorization can be used to send a transaction', async () => {
|
|
@@ -1365,7 +1379,7 @@ describe.each(adapters)('$name', ({ adapter }: (typeof adapters)[number]) => {
|
|
|
1365
1379
|
const receipt = await sendTransactionSync(client, {
|
|
1366
1380
|
account: TempoAccount.fromWebCryptoP256(keyPair, { access: rootAddress }),
|
|
1367
1381
|
calls: [transferCall],
|
|
1368
|
-
keyAuthorization: KeyAuthorization.fromRpc(result),
|
|
1382
|
+
keyAuthorization: KeyAuthorization.fromRpc(result.keyAuthorization),
|
|
1369
1383
|
})
|
|
1370
1384
|
expect(receipt.status).toBe('success')
|
|
1371
1385
|
})
|
package/src/core/Provider.ts
CHANGED
|
@@ -112,11 +112,14 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
112
112
|
return merged
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
/** Resolves the `feePayer` field from a transaction request into
|
|
115
|
+
/** Resolves the `feePayer` field from a transaction request into an absolute URL string or `undefined`. */
|
|
116
116
|
function resolveFeePayer(feePayer: string | boolean | undefined): string | undefined {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return undefined
|
|
117
|
+
const url =
|
|
118
|
+
typeof feePayer === 'string' ? feePayer : feePayer === true ? feePayerUrl : undefined
|
|
119
|
+
if (!url) return undefined
|
|
120
|
+
if (url.startsWith('http://') || url.startsWith('https://')) return url
|
|
121
|
+
if (typeof window !== 'undefined') return new URL(url, window.location.origin).href
|
|
122
|
+
return url
|
|
120
123
|
}
|
|
121
124
|
|
|
122
125
|
const provider = Object.assign(
|
|
@@ -437,7 +440,6 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
437
440
|
return
|
|
438
441
|
|
|
439
442
|
case 'wallet_authorizeAccessKey': {
|
|
440
|
-
assertConnected()
|
|
441
443
|
if (!actions.authorizeAccessKey)
|
|
442
444
|
throw new ox_Provider.UnsupportedMethodError({
|
|
443
445
|
message: '`authorizeAccessKey` not supported by adapter.',
|
|
@@ -445,8 +447,11 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
445
447
|
const decoded = request._decoded.params[0]
|
|
446
448
|
const result = await actions.authorizeAccessKey(decoded, request)
|
|
447
449
|
return {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
+
keyAuthorization: {
|
|
451
|
+
...result.keyAuthorization,
|
|
452
|
+
address: result.keyAuthorization.keyId,
|
|
453
|
+
},
|
|
454
|
+
rootAddress: result.rootAddress,
|
|
450
455
|
} satisfies Rpc.wallet_authorizeAccessKey.Encoded['returns']
|
|
451
456
|
}
|
|
452
457
|
|
|
@@ -106,18 +106,11 @@ describe('Encoded', () => {
|
|
|
106
106
|
})
|
|
107
107
|
|
|
108
108
|
test('wallet_authorizeAccessKey', () => {
|
|
109
|
-
expectTypeOf<Rpc.wallet_authorizeAccessKey.Encoded>().
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
expiry: number
|
|
115
|
-
keyType?: 'secp256k1' | 'p256' | 'webAuthn' | undefined
|
|
116
|
-
limits?: readonly { token: Hex; limit: Hex }[] | undefined
|
|
117
|
-
publicKey?: Hex | undefined
|
|
118
|
-
},
|
|
119
|
-
]
|
|
120
|
-
}>()
|
|
109
|
+
expectTypeOf<Rpc.wallet_authorizeAccessKey.Encoded>().toHaveProperty('method')
|
|
110
|
+
expectTypeOf<Rpc.wallet_authorizeAccessKey.Encoded['returns']>().toHaveProperty('rootAddress')
|
|
111
|
+
expectTypeOf<Rpc.wallet_authorizeAccessKey.Encoded['returns']>().toHaveProperty(
|
|
112
|
+
'keyAuthorization',
|
|
113
|
+
)
|
|
121
114
|
})
|
|
122
115
|
|
|
123
116
|
test('wallet_disconnect', () => {
|
|
@@ -33,6 +33,13 @@ export function dialog(options: dialog.Options = {}): Adapter.Adapter {
|
|
|
33
33
|
rdns = 'xyz.tempo',
|
|
34
34
|
} = options
|
|
35
35
|
|
|
36
|
+
if (typeof window !== 'undefined' && !window.isSecureContext)
|
|
37
|
+
console.warn(
|
|
38
|
+
'[accounts] Detected insecure context (HTTP).',
|
|
39
|
+
`\n\nThe Tempo Wallet iframe dialog is not supported on HTTP origins (${window.location.origin})`,
|
|
40
|
+
'due to lack of WebAuthn passkey support in non-secure contexts.',
|
|
41
|
+
)
|
|
42
|
+
|
|
36
43
|
return Adapter.define({ icon, name, rdns }, ({ getAccount, getClient, store }) => {
|
|
37
44
|
const listeners = new Set<(requestQueue: readonly Store.QueuedRequest[]) => void>()
|
|
38
45
|
const requestStore = ox_RpcRequest.createStore()
|
|
@@ -282,7 +289,10 @@ export function dialog(options: dialog.Options = {}): Adapter.Adapter {
|
|
|
282
289
|
return await account.signTransaction(prepared as never)
|
|
283
290
|
})
|
|
284
291
|
if (result !== undefined) return result
|
|
285
|
-
return await provider.request(
|
|
292
|
+
return await provider.request({
|
|
293
|
+
...request,
|
|
294
|
+
params: [z.encode(Rpc.transactionRequest, parameters)] as const,
|
|
295
|
+
})
|
|
286
296
|
},
|
|
287
297
|
|
|
288
298
|
async signTypedData(_params, request) {
|
|
@@ -309,7 +319,10 @@ export function dialog(options: dialog.Options = {}): Adapter.Adapter {
|
|
|
309
319
|
})
|
|
310
320
|
})
|
|
311
321
|
if (result !== undefined) return result
|
|
312
|
-
return await provider.request(
|
|
322
|
+
return await provider.request({
|
|
323
|
+
...request,
|
|
324
|
+
params: [z.encode(Rpc.transactionRequest, parameters)] as const,
|
|
325
|
+
})
|
|
313
326
|
},
|
|
314
327
|
|
|
315
328
|
async sendTransactionSync(parameters, request) {
|
|
@@ -332,7 +345,10 @@ export function dialog(options: dialog.Options = {}): Adapter.Adapter {
|
|
|
332
345
|
})
|
|
333
346
|
})
|
|
334
347
|
if (result !== undefined) return result
|
|
335
|
-
return await provider.request(
|
|
348
|
+
return await provider.request({
|
|
349
|
+
...request,
|
|
350
|
+
params: [z.encode(Rpc.transactionRequest, parameters)] as const,
|
|
351
|
+
})
|
|
336
352
|
},
|
|
337
353
|
|
|
338
354
|
async authorizeAccessKey(parameters, request) {
|
|
@@ -345,7 +361,7 @@ export function dialog(options: dialog.Options = {}): Adapter.Adapter {
|
|
|
345
361
|
|
|
346
362
|
if (accessKey) {
|
|
347
363
|
const account = getAccount({ accessKey: false, signable: false })
|
|
348
|
-
saveAccessKey(account.address, result, accessKey.keyPair)
|
|
364
|
+
saveAccessKey(account.address, result.keyAuthorization, accessKey.keyPair)
|
|
349
365
|
}
|
|
350
366
|
|
|
351
367
|
return result
|
|
@@ -73,7 +73,7 @@ export function local(options: local.Options): Adapter.Adapter {
|
|
|
73
73
|
options: {
|
|
74
74
|
signature?: Hex.Hex | undefined
|
|
75
75
|
} = {},
|
|
76
|
-
)
|
|
76
|
+
) {
|
|
77
77
|
const { keyPair } = prepared
|
|
78
78
|
|
|
79
79
|
const keyAuthorization = await (async () => {
|
|
@@ -140,7 +140,10 @@ export function local(options: local.Options): Adapter.Adapter {
|
|
|
140
140
|
async authorizeAccessKey(parameters) {
|
|
141
141
|
const prepared = await prepareKeyAuthorization(parameters)
|
|
142
142
|
const account = getAccount({ accessKey: false, signable: true })
|
|
143
|
-
|
|
143
|
+
const keyAuthorization = await signKeyAuthorization(account, prepared, {
|
|
144
|
+
signature: parameters.signature,
|
|
145
|
+
})
|
|
146
|
+
return { keyAuthorization, rootAddress: account.address }
|
|
144
147
|
},
|
|
145
148
|
async loadAccounts(parameters) {
|
|
146
149
|
const { authorizeAccessKey, ...rest } =
|
package/src/core/zod/rpc.ts
CHANGED
|
@@ -67,7 +67,7 @@ export const transactionRequest = z.object({
|
|
|
67
67
|
),
|
|
68
68
|
calls: z.optional(z.readonly(z.array(call))),
|
|
69
69
|
chainId: z.optional(u.number()),
|
|
70
|
-
feePayer: z.optional(z.union([z.boolean(), z.
|
|
70
|
+
feePayer: z.optional(z.union([z.boolean(), z.string()])),
|
|
71
71
|
feeToken: z.optional(u.address()),
|
|
72
72
|
from: z.optional(u.address()),
|
|
73
73
|
gas: z.optional(u.bigint()),
|
|
@@ -259,10 +259,15 @@ export namespace wallet_authorizeAccessKey {
|
|
|
259
259
|
publicKey: z.optional(u.hex()),
|
|
260
260
|
})
|
|
261
261
|
|
|
262
|
+
const returns = z.object({
|
|
263
|
+
keyAuthorization,
|
|
264
|
+
rootAddress: u.address(),
|
|
265
|
+
})
|
|
266
|
+
|
|
262
267
|
export const schema = Schema.defineItem({
|
|
263
268
|
method: z.literal('wallet_authorizeAccessKey'),
|
|
264
269
|
params: z.readonly(z.tuple([parameters])),
|
|
265
|
-
returns
|
|
270
|
+
returns,
|
|
266
271
|
})
|
|
267
272
|
export type Encoded = Schema.Encoded<typeof schema>
|
|
268
273
|
export type Decoded = Schema.Decoded<typeof schema>
|
package/src/server/CliAuth.ts
CHANGED
|
@@ -27,6 +27,7 @@ export const keyAuthorization = z.object({
|
|
|
27
27
|
/** Request body for `POST /auth/pkce/code`. */
|
|
28
28
|
export const createRequest = z.object({
|
|
29
29
|
account: z.optional(u.address()),
|
|
30
|
+
chainId: z.optional(u.bigint()),
|
|
30
31
|
codeChallenge: z.string(),
|
|
31
32
|
expiry: z.optional(z.number()),
|
|
32
33
|
keyType: z.optional(keyType),
|
|
@@ -345,7 +346,6 @@ export async function createDeviceCode(
|
|
|
345
346
|
options: createDeviceCode.Options,
|
|
346
347
|
): Promise<createDeviceCode.ReturnType> {
|
|
347
348
|
const {
|
|
348
|
-
chainId = BigInt(tempo.id),
|
|
349
349
|
now = Date.now,
|
|
350
350
|
policy = Policy.allow(),
|
|
351
351
|
random = randomBytes,
|
|
@@ -353,6 +353,7 @@ export async function createDeviceCode(
|
|
|
353
353
|
store = Store.memory(),
|
|
354
354
|
ttlMs = 10 * 60 * 1_000,
|
|
355
355
|
} = options
|
|
356
|
+
const chainId = request.chainId ?? options.chainId ?? BigInt(tempo.id)
|
|
356
357
|
const { account, codeChallenge, pubKey } = request
|
|
357
358
|
const keyType = request.keyType ?? 'secp256k1'
|
|
358
359
|
const approved = await policy.validate({
|