accounts 0.17.0 → 0.17.2

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cli/Provider.d.ts.map +1 -1
  3. package/dist/cli/Provider.js +3 -1
  4. package/dist/cli/Provider.js.map +1 -1
  5. package/dist/cli/adapter.d.ts.map +1 -1
  6. package/dist/cli/adapter.js +2 -26
  7. package/dist/cli/adapter.js.map +1 -1
  8. package/dist/cli/storage.js +3 -2
  9. package/dist/cli/storage.js.map +1 -1
  10. package/dist/core/Provider.d.ts.map +1 -1
  11. package/dist/core/Provider.js +4 -2
  12. package/dist/core/Provider.js.map +1 -1
  13. package/dist/core/Schema.d.ts +8 -0
  14. package/dist/core/Schema.d.ts.map +1 -1
  15. package/dist/core/adapters/internal/fromRequest.d.ts.map +1 -1
  16. package/dist/core/adapters/internal/fromRequest.js +2 -1
  17. package/dist/core/adapters/internal/fromRequest.js.map +1 -1
  18. package/dist/core/internal/withDedupe.d.ts +3 -2
  19. package/dist/core/internal/withDedupe.d.ts.map +1 -1
  20. package/dist/core/internal/withDedupe.js +6 -5
  21. package/dist/core/internal/withDedupe.js.map +1 -1
  22. package/dist/core/zod/rpc.d.ts +5 -603
  23. package/dist/core/zod/rpc.d.ts.map +1 -1
  24. package/dist/core/zod/rpc.js +1 -0
  25. package/dist/core/zod/rpc.js.map +1 -1
  26. package/dist/server/internal/handlers/auth.d.ts +0 -20
  27. package/dist/server/internal/handlers/auth.d.ts.map +1 -1
  28. package/dist/server/internal/handlers/oidcProvider.d.ts +0 -7
  29. package/dist/server/internal/handlers/oidcProvider.d.ts.map +1 -1
  30. package/package.json +2 -2
  31. package/src/cli/Provider.localnet.test.ts +124 -9
  32. package/src/cli/Provider.ts +4 -0
  33. package/src/cli/adapter.ts +3 -27
  34. package/src/cli/storage.test.ts +20 -1
  35. package/src/cli/storage.ts +2 -2
  36. package/src/core/Provider.localnet.test.ts +24 -18
  37. package/src/core/Provider.test.ts +48 -0
  38. package/src/core/Provider.ts +4 -2
  39. package/src/core/adapters/internal/fromRequest.ts +2 -1
  40. package/src/core/internal/withDedupe.test.ts +17 -0
  41. package/src/core/internal/withDedupe.ts +7 -5
  42. package/src/core/zod/rpc.ts +1 -0
@@ -23,7 +23,7 @@ import { Account as TempoAccount, Actions, Addresses, Transaction } from 'viem/t
23
23
  import { tempo, tempoModerato } from 'viem/tempo/chains'
24
24
  import { afterAll, beforeAll, describe, expect, test } from 'vp/test'
25
25
 
26
- import { headlessWebAuthn, secp256k1 } from '../../test/adapters.js'
26
+ import { cli, headlessWebAuthn, secp256k1 } from '../../test/adapters.js'
27
27
  import { accounts, chain, getClient, http } from '../../test/config.js'
28
28
  import { createJsonStorage, createServer, type Server } from '../../test/utils.js'
29
29
  import * as Handler from '../server/Handler.js'
@@ -34,7 +34,11 @@ import * as Keystore from './Keystore.js'
34
34
  import * as Provider from './Provider.js'
35
35
  import * as Storage from './Storage.js'
36
36
 
37
+ beforeAll(cli.setup)
38
+ afterAll(cli.teardown)
39
+
37
40
  const adapters = [
41
+ { name: 'cli', adapter: cli },
38
42
  { name: 'headlessWebAuthn', adapter: headlessWebAuthn },
39
43
  { name: 'secp256k1', adapter: secp256k1 },
40
44
  ] as const
@@ -856,7 +860,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
856
860
 
857
861
  test('behavior: provider mints identity when configured with an issuer', async () => {
858
862
  const provider = Provider.create({
859
- adapter: adapter(),
863
+ adapter: adapter({ identity: { audience: 'https://app.example.com', issuer } }),
860
864
  identity: { audience: 'https://app.example.com', issuer },
861
865
  })
862
866
 
@@ -889,7 +893,9 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
889
893
 
890
894
  test('behavior: provider identity mint is best-effort (failure omits the claim)', async () => {
891
895
  const provider = Provider.create({
892
- adapter: adapter(),
896
+ adapter: adapter({
897
+ identity: { audience: 'https://app.example.com', issuer: `${server.url}/nowhere` },
898
+ }),
893
899
  identity: { audience: 'https://app.example.com', issuer: `${server.url}/nowhere` },
894
900
  })
895
901
 
@@ -904,7 +910,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
904
910
 
905
911
  test('behavior: provider identity is not minted unless requested', async () => {
906
912
  const provider = Provider.create({
907
- adapter: adapter(),
913
+ adapter: adapter({ identity: { audience: 'https://app.example.com', issuer } }),
908
914
  identity: { audience: 'https://app.example.com', issuer },
909
915
  })
910
916
 
@@ -1564,7 +1570,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
1564
1570
 
1565
1571
  test('behavior: includes feePayer when configured', async () => {
1566
1572
  const provider = Provider.create({
1567
- adapter: adapter(),
1573
+ adapter: adapter({ feePayer: 'https://fee-payer.example.com' }),
1568
1574
  feePayer: 'https://fee-payer.example.com',
1569
1575
  })
1570
1576
 
@@ -1783,7 +1789,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
1783
1789
  const storage = Storage.memory({ key: 'persist-invalid' })
1784
1790
  storage.setItem('store', {
1785
1791
  state: {
1786
- accounts: [{ address: '0x0000000000000000000000000000000000000001' }],
1792
+ accounts: [{ address: 'invalid' }],
1787
1793
  activeAccount: 0,
1788
1794
  chainId: chain.id,
1789
1795
  },
@@ -3228,7 +3234,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
3228
3234
 
3229
3235
  test('behavior: feePayer: true uses default from Provider.create', async () => {
3230
3236
  const provider = Provider.create({
3231
- adapter: adapter(),
3237
+ adapter: adapter({ feePayer: server.url }),
3232
3238
  chains: [chain],
3233
3239
  feePayer: server.url,
3234
3240
  })
@@ -3251,7 +3257,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
3251
3257
 
3252
3258
  test('behavior: feePayer: true on eth_sendTransactionSync', async () => {
3253
3259
  const provider = Provider.create({
3254
- adapter: adapter(),
3260
+ adapter: adapter({ feePayer: server.url }),
3255
3261
  chains: [chain],
3256
3262
  feePayer: server.url,
3257
3263
  })
@@ -3293,7 +3299,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
3293
3299
 
3294
3300
  test('behavior: precedence fee-payer-first (default) on eth_sendTransaction', async () => {
3295
3301
  const provider = Provider.create({
3296
- adapter: adapter(),
3302
+ adapter: adapter({ feePayer: server.url }),
3297
3303
  chains: [chain],
3298
3304
  feePayer: server.url,
3299
3305
  })
@@ -3314,7 +3320,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
3314
3320
 
3315
3321
  test('behavior: precedence fee-payer-first (default) on eth_sendTransactionSync', async () => {
3316
3322
  const provider = Provider.create({
3317
- adapter: adapter(),
3323
+ adapter: adapter({ feePayer: server.url }),
3318
3324
  chains: [chain],
3319
3325
  feePayer: server.url,
3320
3326
  })
@@ -3337,7 +3343,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
3337
3343
 
3338
3344
  test('behavior: precedence user-first on eth_sendTransaction', async () => {
3339
3345
  const provider = Provider.create({
3340
- adapter: adapter(),
3346
+ adapter: adapter({ feePayer: { url: server.url, precedence: 'user-first' } }),
3341
3347
  chains: [chain],
3342
3348
  feePayer: { url: server.url, precedence: 'user-first' },
3343
3349
  })
@@ -3358,7 +3364,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
3358
3364
 
3359
3365
  test('behavior: precedence user-first on eth_sendTransactionSync', async () => {
3360
3366
  const provider = Provider.create({
3361
- adapter: adapter(),
3367
+ adapter: adapter({ feePayer: { url: server.url, precedence: 'user-first' } }),
3362
3368
  chains: [chain],
3363
3369
  feePayer: { url: server.url, precedence: 'user-first' },
3364
3370
  })
@@ -3381,7 +3387,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
3381
3387
 
3382
3388
  test('behavior: precedence user-first on eth_signTransaction', async () => {
3383
3389
  const provider = Provider.create({
3384
- adapter: adapter(),
3390
+ adapter: adapter({ feePayer: { url: server.url, precedence: 'user-first' } }),
3385
3391
  chains: [chain],
3386
3392
  feePayer: { url: server.url, precedence: 'user-first' },
3387
3393
  })
@@ -3399,7 +3405,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
3399
3405
 
3400
3406
  test('behavior: feePayer: false opts out on eth_sendTransaction', async () => {
3401
3407
  const provider = Provider.create({
3402
- adapter: adapter(),
3408
+ adapter: adapter({ feePayer: server.url }),
3403
3409
  chains: [chain],
3404
3410
  feePayer: server.url,
3405
3411
  })
@@ -3422,7 +3428,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
3422
3428
 
3423
3429
  test('behavior: feePayer: false opts out on eth_sendTransactionSync', async () => {
3424
3430
  const provider = Provider.create({
3425
- adapter: adapter(),
3431
+ adapter: adapter({ feePayer: server.url }),
3426
3432
  chains: [chain],
3427
3433
  feePayer: server.url,
3428
3434
  })
@@ -3445,7 +3451,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
3445
3451
 
3446
3452
  test('behavior: feePayer: false opts out on eth_signTransaction', async () => {
3447
3453
  const provider = Provider.create({
3448
- adapter: adapter(),
3454
+ adapter: adapter({ feePayer: server.url }),
3449
3455
  chains: [chain],
3450
3456
  feePayer: server.url,
3451
3457
  })
@@ -3488,7 +3494,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
3488
3494
 
3489
3495
  test('behavior: wallet_sendCalls with feePayer: true uses provider default', async () => {
3490
3496
  const provider = Provider.create({
3491
- adapter: adapter(),
3497
+ adapter: adapter({ feePayer: server.url }),
3492
3498
  chains: [chain],
3493
3499
  feePayer: server.url,
3494
3500
  })
@@ -3517,7 +3523,7 @@ describe.each(adapters)('$name', ({ adapter, name }: (typeof adapters)[number])
3517
3523
 
3518
3524
  test('behavior: wallet_sendCalls with feePayer: false opts out', async () => {
3519
3525
  const provider = Provider.create({
3520
- adapter: adapter(),
3526
+ adapter: adapter({ feePayer: server.url }),
3521
3527
  chains: [chain],
3522
3528
  feePayer: server.url,
3523
3529
  })
@@ -11,6 +11,54 @@ import * as Storage from './Storage.js'
11
11
 
12
12
  const address = '0x0000000000000000000000000000000000000001'
13
13
 
14
+ describe('eth_fillTransaction', () => {
15
+ test('behavior: forwards Tempo-formatted request capabilities', async () => {
16
+ const capabilities: unknown[] = []
17
+ const request = tempo.formatters.transactionRequest.format(
18
+ {
19
+ capabilities: { balanceDiffs: false, errors: true },
20
+ from: address,
21
+ to: address,
22
+ type: 'tempo',
23
+ },
24
+ 'fillTransaction',
25
+ )
26
+ const provider = Provider.create({
27
+ chains: [tempo],
28
+ storage: Storage.memory(),
29
+ transports: {
30
+ [tempo.id]: custom(
31
+ {
32
+ async request(request) {
33
+ if (request.method === 'eth_fillTransaction')
34
+ capabilities.push(
35
+ (request.params?.[0] as { capabilities?: unknown } | undefined)?.capabilities,
36
+ )
37
+ throw new Error('capture complete')
38
+ },
39
+ },
40
+ { retryCount: 0 },
41
+ ),
42
+ },
43
+ })
44
+
45
+ await expect(
46
+ provider.request({
47
+ method: 'eth_fillTransaction',
48
+ params: [request as never],
49
+ }),
50
+ ).rejects.toThrow('capture complete')
51
+ expect(capabilities).toMatchInlineSnapshot(`
52
+ [
53
+ {
54
+ "balanceDiffs": false,
55
+ "errors": true,
56
+ },
57
+ ]
58
+ `)
59
+ })
60
+ })
61
+
14
62
  describe('getMppxParameters', () => {
15
63
  test('error: rejects unsupported chain IDs', () => {
16
64
  const provider = Provider.create({ storage: Storage.memory() })
@@ -176,6 +176,7 @@ export function create(options: create.Options = {}): create.ReturnType {
176
176
  Object.assign(keystores, keystores_configured ?? Keystore.defaults)
177
177
 
178
178
  const emitter = ox_Provider.createEmitter()
179
+ const requestCache = new Map<string, Promise<unknown>>()
179
180
 
180
181
  // Emit EIP-1193 events on state changes.
181
182
  store.subscribe(
@@ -573,7 +574,7 @@ export function create(options: create.Options = {}): create.ReturnType {
573
574
  })
574
575
  } else {
575
576
  try {
576
- await Actions.accessKey.revoke(getClient(), {
577
+ await Actions.accessKey.revokeSync(getClient(), {
577
578
  account: selected.account as TempoAccount.Account,
578
579
  accessKey: parameters.accessKeyAddress,
579
580
  })
@@ -1157,7 +1158,7 @@ export function create(options: create.Options = {}): create.ReturnType {
1157
1158
  calls,
1158
1159
  chainId,
1159
1160
  from: from_,
1160
- ...(feePayer ? { feePayer } : {}),
1161
+ ...(feePayer !== undefined ? { feePayer } : {}),
1161
1162
  }
1162
1163
  if (!sync) {
1163
1164
  const hash = await sendTransactionAction(txRequest, {
@@ -1783,6 +1784,7 @@ export function create(options: create.Options = {}): create.ReturnType {
1783
1784
  return result
1784
1785
  },
1785
1786
  {
1787
+ cache: requestCache,
1786
1788
  enabled: shouldDedupe,
1787
1789
  id: Json.stringify({ method, params }),
1788
1790
  },
@@ -5,6 +5,7 @@ import { z } from 'zod/mini'
5
5
  import * as Adapter from '../../Adapter.js'
6
6
  import type * as Keystore from '../../Keystore.js'
7
7
  import * as Rpc from '../../zod/rpc.js'
8
+ import * as u from '../../zod/utils.js'
8
9
 
9
10
  /**
10
11
  * Builds an adapter that forwards wallet RPC to a remote wallet through a
@@ -29,7 +30,7 @@ export function fromRequest(options: fromRequest.Options): Adapter.Adapter {
29
30
  } = options
30
31
 
31
32
  return Adapter.define(
32
- { ...(icon ? { icon } : {}), name, rdns },
33
+ { ...(icon ? { icon } : {}), name, rdns, schema: z.object({ address: u.address() }) },
33
34
  ({ getAccount, getClient, store }) => {
34
35
  // Late-bind the store so the request channel can reconcile local
35
36
  // connection state (e.g. a wallet `accountsChanged` notification).
@@ -41,6 +41,23 @@ describe('withDedupe', () => {
41
41
  expect(b).toMatchInlineSnapshot(`2`)
42
42
  })
43
43
 
44
+ test('behavior: separate caches execute independently', async () => {
45
+ let calls = 0
46
+ const fn = () => Promise.resolve(++calls)
47
+
48
+ const [a, b] = await Promise.all([
49
+ withDedupe(fn, { cache: new Map(), id: 'same' }),
50
+ withDedupe(fn, { cache: new Map(), id: 'same' }),
51
+ ])
52
+
53
+ expect([a, b]).toMatchInlineSnapshot(`
54
+ [
55
+ 1,
56
+ 2,
57
+ ]
58
+ `)
59
+ })
60
+
44
61
  test('behavior: cache is cleared after promise resolves', async () => {
45
62
  const fn = () => Promise.resolve('ok')
46
63
 
@@ -1,20 +1,22 @@
1
1
  /** Deduplicates in-flight promises by key. */
2
2
  export function withDedupe<data>(
3
3
  fn: () => Promise<data>,
4
- { enabled = true, id }: withDedupe.Options,
4
+ { cache = withDedupe.cache, enabled = true, id }: withDedupe.Options,
5
5
  ): Promise<data> {
6
6
  if (!enabled || !id) return fn()
7
- if (withDedupe.cache.get(id)) return withDedupe.cache.get(id)!
8
- const promise = fn().finally(() => withDedupe.cache.delete(id))
9
- withDedupe.cache.set(id, promise)
7
+ const cached = cache.get(id)
8
+ if (cached) return cached as Promise<data>
9
+ const promise = fn().finally(() => cache.delete(id))
10
+ cache.set(id, promise)
10
11
  return promise
11
12
  }
12
13
 
13
14
  export declare namespace withDedupe {
14
15
  type Options = {
16
+ cache?: Map<string, Promise<unknown>> | undefined
15
17
  enabled?: boolean | undefined
16
18
  id?: string | undefined
17
19
  }
18
20
  }
19
21
 
20
- withDedupe.cache = new Map<string, Promise<any>>()
22
+ withDedupe.cache = new Map<string, Promise<unknown>>()
@@ -171,6 +171,7 @@ export const transactionRequest = z.object({
171
171
  z.array(z.object({ address: u.address(), storageKeys: z.array(u.hex()) })),
172
172
  ),
173
173
  calls: z.optional(z.readonly(z.array(call))),
174
+ capabilities: z.optional(z.record(z.string(), z.unknown())),
174
175
  chainId: z.optional(u.number()),
175
176
  data: z.optional(u.hex()),
176
177
  feePayer: z.optional(z.union([z.boolean(), z.string()])),