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.
Files changed (128) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +6 -5
  3. package/dist/cli/adapter.d.ts.map +1 -1
  4. package/dist/cli/adapter.js +76 -69
  5. package/dist/cli/adapter.js.map +1 -1
  6. package/dist/core/AccessKey.d.ts +89 -106
  7. package/dist/core/AccessKey.d.ts.map +1 -1
  8. package/dist/core/AccessKey.js +230 -210
  9. package/dist/core/AccessKey.js.map +1 -1
  10. package/dist/core/Account.d.ts +0 -9
  11. package/dist/core/Account.d.ts.map +1 -1
  12. package/dist/core/Account.js +1 -13
  13. package/dist/core/Account.js.map +1 -1
  14. package/dist/core/Adapter.d.ts +24 -4
  15. package/dist/core/Adapter.d.ts.map +1 -1
  16. package/dist/core/Client.d.ts +1 -1
  17. package/dist/core/Client.d.ts.map +1 -1
  18. package/dist/core/Client.js.map +1 -1
  19. package/dist/core/Provider.d.ts +6 -7
  20. package/dist/core/Provider.d.ts.map +1 -1
  21. package/dist/core/Provider.js +73 -77
  22. package/dist/core/Provider.js.map +1 -1
  23. package/dist/core/Remote.d.ts +2 -0
  24. package/dist/core/Remote.d.ts.map +1 -1
  25. package/dist/core/Remote.js +3 -1
  26. package/dist/core/Remote.js.map +1 -1
  27. package/dist/core/Schema.d.ts +17 -7
  28. package/dist/core/Schema.d.ts.map +1 -1
  29. package/dist/core/adapters/dialog.d.ts.map +1 -1
  30. package/dist/core/adapters/dialog.js +126 -131
  31. package/dist/core/adapters/dialog.js.map +1 -1
  32. package/dist/core/adapters/local.d.ts.map +1 -1
  33. package/dist/core/adapters/local.js +92 -105
  34. package/dist/core/adapters/local.js.map +1 -1
  35. package/dist/core/adapters/privy.d.ts +164 -0
  36. package/dist/core/adapters/privy.d.ts.map +1 -0
  37. package/dist/core/adapters/privy.js +578 -0
  38. package/dist/core/adapters/privy.js.map +1 -0
  39. package/dist/core/adapters/turnkey.d.ts.map +1 -1
  40. package/dist/core/adapters/turnkey.js +136 -133
  41. package/dist/core/adapters/turnkey.js.map +1 -1
  42. package/dist/core/internal/AccessKeyTransaction.d.ts +59 -0
  43. package/dist/core/internal/AccessKeyTransaction.d.ts.map +1 -0
  44. package/dist/core/internal/AccessKeyTransaction.js +155 -0
  45. package/dist/core/internal/AccessKeyTransaction.js.map +1 -0
  46. package/dist/core/zod/rpc.d.ts +71 -4
  47. package/dist/core/zod/rpc.d.ts.map +1 -1
  48. package/dist/core/zod/rpc.js +33 -4
  49. package/dist/core/zod/rpc.js.map +1 -1
  50. package/dist/index.d.ts +1 -1
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +1 -1
  53. package/dist/index.js.map +1 -1
  54. package/dist/react-native/adapter.d.ts.map +1 -1
  55. package/dist/react-native/adapter.js +86 -75
  56. package/dist/react-native/adapter.js.map +1 -1
  57. package/dist/server/CliAuth.d.ts +55 -0
  58. package/dist/server/CliAuth.d.ts.map +1 -1
  59. package/dist/server/CliAuth.js +18 -1
  60. package/dist/server/CliAuth.js.map +1 -1
  61. package/dist/server/Kv.d.ts +19 -9
  62. package/dist/server/Kv.d.ts.map +1 -1
  63. package/dist/server/Kv.js +32 -9
  64. package/dist/server/Kv.js.map +1 -1
  65. package/dist/server/internal/handlers/auth.d.ts.map +1 -1
  66. package/dist/server/internal/handlers/auth.js +3 -1
  67. package/dist/server/internal/handlers/auth.js.map +1 -1
  68. package/dist/server/internal/handlers/codeAuth.js +1 -1
  69. package/dist/server/internal/handlers/codeAuth.js.map +1 -1
  70. package/dist/server/internal/handlers/exchange.js +1 -1
  71. package/dist/server/internal/handlers/exchange.js.map +1 -1
  72. package/dist/server/internal/handlers/relay.d.ts +18 -12
  73. package/dist/server/internal/handlers/relay.d.ts.map +1 -1
  74. package/dist/server/internal/handlers/relay.js +116 -3
  75. package/dist/server/internal/handlers/relay.js.map +1 -1
  76. package/dist/server/internal/handlers/webAuthn.d.ts +6 -1
  77. package/dist/server/internal/handlers/webAuthn.d.ts.map +1 -1
  78. package/dist/server/internal/handlers/webAuthn.js +16 -9
  79. package/dist/server/internal/handlers/webAuthn.js.map +1 -1
  80. package/package.json +3 -3
  81. package/src/cli/{Provider.test.ts → Provider.localnet.test.ts} +49 -1
  82. package/src/cli/adapter.ts +95 -76
  83. package/src/core/AccessKey.test.ts +563 -560
  84. package/src/core/AccessKey.ts +327 -327
  85. package/src/core/Account.test.ts +6 -51
  86. package/src/core/Account.ts +1 -19
  87. package/src/core/Adapter.ts +22 -4
  88. package/src/core/Client.test.ts +1 -1
  89. package/src/core/Client.ts +1 -1
  90. package/src/core/Provider.connect.browser.test.ts +1 -1
  91. package/src/core/{Provider.test.ts → Provider.localnet.test.ts} +98 -32
  92. package/src/core/Provider.ts +118 -112
  93. package/src/core/Remote.test.ts +28 -0
  94. package/src/core/Remote.ts +4 -1
  95. package/src/core/Schema.test-d.ts +41 -0
  96. package/src/core/adapters/dialog.test.ts +335 -15
  97. package/src/core/adapters/dialog.ts +120 -127
  98. package/src/core/adapters/local.test.ts +1 -1
  99. package/src/core/adapters/local.ts +95 -119
  100. package/src/core/adapters/privy.test-d.ts +128 -0
  101. package/src/core/adapters/privy.test.ts +648 -0
  102. package/src/core/adapters/privy.ts +811 -0
  103. package/src/core/adapters/turnkey.test.ts +117 -13
  104. package/src/core/adapters/turnkey.ts +132 -149
  105. package/src/core/internal/AccessKeyTransaction.ts +243 -0
  106. package/src/core/{mppx.test.ts → mppx.localnet.test.ts} +32 -2
  107. package/src/core/zod/request.test.ts +36 -0
  108. package/src/core/zod/rpc.test.ts +139 -0
  109. package/src/core/zod/rpc.ts +44 -10
  110. package/src/index.ts +1 -1
  111. package/src/react-native/{Provider.test.ts → Provider.localnet.test.ts} +65 -0
  112. package/src/react-native/adapter.ts +107 -87
  113. package/src/server/CliAuth.test-d.ts +23 -0
  114. package/src/server/CliAuth.test.ts +47 -0
  115. package/src/server/CliAuth.ts +20 -1
  116. package/src/server/Handler.test-d.ts +1 -1
  117. package/src/server/Kv.test.ts +51 -0
  118. package/src/server/Kv.ts +43 -9
  119. package/src/server/internal/handlers/auth.test.ts +10 -5
  120. package/src/server/internal/handlers/auth.ts +4 -1
  121. package/src/server/internal/handlers/codeAuth.ts +1 -1
  122. package/src/server/internal/handlers/exchange.ts +1 -1
  123. package/src/server/internal/handlers/{relay.test.ts → relay.localnet.test.ts} +4 -1
  124. package/src/server/internal/handlers/relay.ts +139 -14
  125. package/src/server/internal/handlers/session.test.ts +1 -1
  126. package/src/server/internal/handlers/webAuthn.test.ts +20 -0
  127. package/src/server/internal/handlers/webAuthn.ts +24 -12
  128. /package/src/server/internal/handlers/{exchange.test.ts → exchange.localnet.test.ts} +0 -0
@@ -1,11 +1,11 @@
1
1
  import { announceProvider } from 'mipd'
2
2
  import { Mppx, tempo as mppx_tempo } from 'mppx/client'
3
3
  import { Address, Hash, Hex, Json, Provider as ox_Provider, RpcResponse } from 'ox'
4
- import { KeyAuthorization } from 'ox/tempo'
5
4
  import { http, parseUnits, type Chain, type Client as ViemClient, type Transport } from 'viem'
6
- import { tempo, tempoDevnet, tempoModerato } from 'viem/chains'
5
+ import type { JsonRpcAccount } from 'viem/accounts'
7
6
  import { parseSiweMessage } from 'viem/siwe'
8
7
  import { Actions } from 'viem/tempo'
8
+ import { tempo, tempoDevnet, tempoModerato } from 'viem/tempo/chains'
9
9
  import * as z from 'zod/mini'
10
10
 
11
11
  import * as AccessKey from './AccessKey.js'
@@ -13,6 +13,7 @@ import * as Account from './Account.js'
13
13
  import type * as Adapter from './Adapter.js'
14
14
  import { dialog } from './adapters/dialog.js'
15
15
  import * as Client from './Client.js'
16
+ import * as AccessKeyTransaction from './internal/AccessKeyTransaction.js'
16
17
  import { withDedupe } from './internal/withDedupe.js'
17
18
  import * as Schema from './Schema.js'
18
19
  import * as Storage from './Storage.js'
@@ -25,8 +26,8 @@ export type Provider = ox_Provider.Provider<{ schema: Schema.Ox }> &
25
26
  ox_Provider.Emitter & {
26
27
  /** Configured chains. */
27
28
  chains: readonly [Chain, ...Chain[]]
28
- /** Returns a viem Account for the given address (or active account). */
29
- getAccount: Account.Find
29
+ /** Returns the active root account as a viem JSON-RPC account. */
30
+ getAccount(): JsonRpcAccount
30
31
  /** Returns local or on-chain publication status for an access key. */
31
32
  getAccessKeyStatus(
32
33
  options?: getAccessKeyStatus.Options | undefined,
@@ -271,8 +272,8 @@ export function create(options: create.Options = {}): create.ReturnType {
271
272
  type FillParams = z.output<typeof Rpc.transactionRequest> & {
272
273
  keyAuthorization?: unknown
273
274
  }
275
+ const client = getClient({ chainId, feePayer })
274
276
  const fill = (params: FillParams) => {
275
- const client = getClient({ chainId, feePayer })
276
277
  const fillRequest = {
277
278
  ...params,
278
279
  chainId: params.chainId ?? client.chain?.id,
@@ -292,45 +293,38 @@ export function create(options: create.Options = {}): create.ReturnType {
292
293
  // Inject pending keyAuthorization so the node accounts for
293
294
  // key authorization gas during estimation.
294
295
  if (!parameters.keyAuthorization) {
295
- const account = (() => {
296
- try {
297
- const calls =
298
- parameters.calls ??
299
- (parameters.to
300
- ? [
301
- {
302
- data: parameters.data,
303
- to: parameters.to,
304
- },
305
- ]
306
- : undefined)
307
- return getAccount({
308
- address: parameters.from,
309
- calls,
310
- chainId: parameters.chainId ?? store.getState().chainId,
311
- signable: true,
312
- })
313
- } catch {
314
- return undefined
315
- }
316
- })()
317
- if (account?.source === 'accessKey') {
318
- const keyAuth = AccessKey.getPending(account, { store })
319
- if (keyAuth) {
296
+ const state = store.getState()
297
+ const address =
298
+ parameters.from ?? state.accounts[state.activeAccount]?.address
299
+ if (address) {
300
+ const calls =
301
+ parameters.calls ??
302
+ (parameters.to
303
+ ? [
304
+ {
305
+ data: parameters.data,
306
+ to: parameters.to,
307
+ },
308
+ ]
309
+ : undefined)
310
+ const transaction = await AccessKeyTransaction.create({
311
+ address,
312
+ calls,
313
+ chainId: parameters.chainId ?? state.chainId,
314
+ client,
315
+ store,
316
+ })
317
+ if (transaction)
320
318
  try {
321
- const result = await fill({
319
+ return await transaction.fill({
322
320
  ...parameters,
323
- keyAuthorization: {
324
- address: keyAuth.address,
325
- ...KeyAuthorization.toRpc(keyAuth),
326
- } as never,
321
+ chainId: parameters.chainId ?? state.chainId,
322
+ from: parameters.from ?? address,
323
+ ...(feePayer ? { feePayer: true } : {}),
327
324
  })
328
- return result
329
- } catch (error) {
330
- AccessKey.invalidate(account, error, { store })
325
+ } catch {
331
326
  return await fill(parameters)
332
327
  }
333
- }
334
328
  }
335
329
  }
336
330
 
@@ -574,6 +568,11 @@ export function create(options: create.Options = {}): create.ReturnType {
574
568
  auth_input as NonNullable<z.output<typeof Rpc.wallet_connect.auth>>,
575
569
  )
576
570
  : undefined
571
+ if (auth_request && typeof auth_request === 'object' && !auth_request.challenge)
572
+ throw new RpcResponse.InvalidParamsError({
573
+ message:
574
+ '`auth` capability must include either `url` or an explicit `challenge` endpoint.',
575
+ })
577
576
  if (auth_request && capabilities?.personalSign)
578
577
  throw new RpcResponse.InvalidParamsError({
579
578
  message:
@@ -608,56 +607,67 @@ export function create(options: create.Options = {}): create.ReturnType {
608
607
  ? { message: auth.message }
609
608
  : capabilities?.personalSign
610
609
 
611
- const { keyAuthorization, accounts, email, personalSign, signature, username } =
612
- await (async () => {
613
- if (capabilities?.method === 'register') {
614
- // If a stored account already has this label, sign in
615
- // with its credential instead of creating a new one.
616
- const existing = capabilities.name
617
- ? store
618
- .getState()
619
- .accounts.find(
620
- (a) =>
621
- 'credential' in a &&
622
- a.label?.toLowerCase() === capabilities.name!.toLowerCase(),
623
- )
624
- : undefined
625
- if (existing && 'credential' in existing)
626
- return await actions.loadAccounts(
627
- {
628
- credentialId: existing.credential?.id,
629
- digest: capabilities.digest,
630
- authorizeAccessKey,
631
- ...(personalSign_request
632
- ? { personalSign: personalSign_request }
633
- : {}),
634
- },
635
- request,
636
- )
637
- return await actions.createAccount(
610
+ const {
611
+ accounts,
612
+ auth: auth_capability,
613
+ email,
614
+ keyAuthorization,
615
+ personalSign,
616
+ signature,
617
+ username,
618
+ } = await (async () => {
619
+ if (capabilities?.method === 'register') {
620
+ // If a stored account already has this label, sign in
621
+ // with its credential instead of creating a new one.
622
+ const existing = capabilities.name
623
+ ? store
624
+ .getState()
625
+ .accounts.find(
626
+ (a) =>
627
+ 'credential' in a &&
628
+ a.label?.toLowerCase() === capabilities.name!.toLowerCase(),
629
+ )
630
+ : undefined
631
+ if (existing && 'credential' in existing)
632
+ return await actions.loadAccounts(
638
633
  {
634
+ credentialId: existing.credential?.id,
639
635
  digest: capabilities.digest,
640
636
  authorizeAccessKey,
641
- name: capabilities.name ?? 'default',
642
- userId: capabilities.userId ?? Hex.random(16),
643
637
  ...(personalSign_request
644
638
  ? { personalSign: personalSign_request }
645
639
  : {}),
640
+ ...(capabilities.showDeposit !== undefined
641
+ ? { showDeposit: capabilities.showDeposit }
642
+ : {}),
646
643
  },
647
644
  request,
648
645
  )
649
- }
650
- return await actions.loadAccounts(
646
+ return await actions.createAccount(
651
647
  {
652
- credentialId: capabilities?.credentialId,
653
- digest: capabilities?.digest,
648
+ digest: capabilities.digest,
654
649
  authorizeAccessKey,
655
- selectAccount: capabilities?.selectAccount,
650
+ name: capabilities.name ?? 'default',
651
+ ...(capabilities.showDeposit !== undefined
652
+ ? { showDeposit: capabilities.showDeposit }
653
+ : {}),
654
+ userId: capabilities.userId ?? Hex.random(16),
656
655
  ...(personalSign_request ? { personalSign: personalSign_request } : {}),
657
656
  },
658
657
  request,
659
658
  )
660
- })()
659
+ }
660
+ return await actions.loadAccounts(
661
+ {
662
+ credentialId: capabilities?.credentialId,
663
+ digest: capabilities?.digest,
664
+ authorizeAccessKey,
665
+ selectAccount: capabilities?.selectAccount,
666
+ ...(personalSign_request ? { personalSign: personalSign_request } : {}),
667
+ },
668
+ request,
669
+ )
670
+ })()
661
671
 
662
672
  store.setState({
663
673
  accounts: resolveAccounts(accounts),
@@ -713,12 +723,14 @@ export function create(options: create.Options = {}): create.ReturnType {
713
723
  },
714
724
  }
715
725
  : {}),
716
- ...(signature && (!auth_request || auth_result)
726
+ ...(signature && (!auth_request || auth_result || !verifyUrl)
717
727
  ? { signature }
718
728
  : {}),
719
729
  ...(email !== undefined ? { email } : {}),
720
730
  ...(username !== undefined ? { username } : {}),
721
- ...(auth_result ? { auth: auth_result } : {}),
731
+ ...((auth_result ?? auth_capability)
732
+ ? { auth: auth_result ?? auth_capability }
733
+ : {}),
722
734
  ...(personalSign
723
735
  ? { personalSign: { message: personalSign.message } }
724
736
  : {}),
@@ -802,7 +814,7 @@ export function create(options: create.Options = {}): create.ReturnType {
802
814
  message: '`deposit` not supported by adapter.',
803
815
  })
804
816
  return (await actions.deposit(
805
- request._decoded.params[0],
817
+ request._decoded.params?.[0] ?? {},
806
818
  request,
807
819
  )) satisfies Rpc.wallet_deposit.Encoded['returns']
808
820
  }
@@ -973,15 +985,20 @@ export function create(options: create.Options = {}): create.ReturnType {
973
985
  ),
974
986
  {
975
987
  chains,
976
- getAccount,
988
+ getAccount() {
989
+ const account = getAccount()
990
+ return { address: account.address, type: 'json-rpc' as const }
991
+ },
977
992
  async getAccessKeyStatus(options: getAccessKeyStatus.Options = {}) {
978
993
  const state = store.getState()
979
994
  const address = options.address ?? state.accounts[state.activeAccount]?.address
980
- if (!address) return AccessKey.status.missing
995
+ if (!address) return 'missing'
981
996
  const chainId = options.chainId ?? state.chainId
997
+ const { accessKey, calls } = options
982
998
  return await AccessKey.getStatus({
983
- ...options,
984
- address,
999
+ account: address,
1000
+ ...(accessKey ? { accessKey } : {}),
1001
+ ...(calls ? { calls } : {}),
985
1002
  chainId,
986
1003
  client: provider.getClient({ chainId }),
987
1004
  store,
@@ -1031,31 +1048,22 @@ export function create(options: create.Options = {}): create.ReturnType {
1031
1048
  const polyfill = polyfill_option ?? isFetchWritable()
1032
1049
  const getClient = ({ chainId }: { chainId?: number | undefined }) => {
1033
1050
  const client = provider.getClient({ chainId })
1034
- const account = provider.getAccount()
1035
- return Object.assign(client, { account })
1051
+ const account = store.getState().accounts[store.getState().activeAccount]
1052
+ if (!account) throw new ox_Provider.DisconnectedError({ message: 'No active account.' })
1053
+ return Object.assign(client, {
1054
+ account: {
1055
+ address: account.address,
1056
+ type: 'json-rpc' as const,
1057
+ },
1058
+ })
1036
1059
  }
1037
- const mppx = Mppx.create({
1060
+ Mppx.create({
1038
1061
  methods: [
1039
1062
  mppx_tempo({ ...methodOptions, getClient, mode }),
1040
1063
  mppx_tempo.subscription({ getClient }),
1041
1064
  ],
1042
1065
  polyfill,
1043
1066
  })
1044
- mppx.onPaymentResponse(({ challenge, method }) => {
1045
- if (method.name !== 'tempo' || method.intent !== 'charge') return
1046
- const amount = challenge.request.amount
1047
- if (
1048
- typeof amount !== 'string' &&
1049
- typeof amount !== 'number' &&
1050
- typeof amount !== 'bigint' &&
1051
- typeof amount !== 'boolean'
1052
- )
1053
- return
1054
- if (BigInt(amount) === 0n) return
1055
- const account = provider.getAccount()
1056
- if ('source' in account && account.source === 'accessKey')
1057
- AccessKey.removePending(account, { store })
1058
- })
1059
1067
  }
1060
1068
 
1061
1069
  providerRef = provider
@@ -1132,7 +1140,7 @@ export declare namespace create {
1132
1140
  * @example
1133
1141
  * ```ts
1134
1142
  * import { http } from 'viem'
1135
- * import { tempo, tempoModerato } from 'viem/chains'
1143
+ * import { tempo, tempoModerato } from 'viem/tempo/chains'
1136
1144
  *
1137
1145
  * const provider = Provider.create({
1138
1146
  * transports: {
@@ -1161,7 +1169,7 @@ export declare namespace getAccessKeyStatus {
1161
1169
  }
1162
1170
 
1163
1171
  /** Access-key publication status. */
1164
- type ReturnType = AccessKey.Status
1172
+ type ReturnType = 'missing' | 'pending' | 'published' | 'expired'
1165
1173
  }
1166
1174
 
1167
1175
  export declare namespace mpp {
@@ -1266,14 +1274,16 @@ function resolveAuthEndpoint(
1266
1274
  }
1267
1275
 
1268
1276
  /**
1269
- * Pre-resolves the `auth` capability into its absolute, fully-explicit
1270
- * object form. Run once at the dapp-side Provider so forwarding adapters
1271
- * (dialog) carry absolute URLs to the wallet host — the wallet's
1272
- * `window.location.origin` belongs to the wallet, not the dapp, and
1273
- * cannot resolve relative paths correctly.
1277
+ * Pre-resolves the `auth` capability into its absolute object form. Run
1278
+ * once at the dapp-side Provider so forwarding adapters (dialog) carry
1279
+ * absolute URLs to the wallet host — the wallet's `window.location.origin`
1280
+ * belongs to the wallet, not the dapp, and cannot resolve relative paths
1281
+ * correctly.
1274
1282
  *
1275
- * `logout` is omitted when the input doesn't supply enough info to derive
1276
- * one (it's optional in the protocol).
1283
+ * Individual endpoints are omitted when the input doesn't supply enough
1284
+ * info to derive them. `logout` is optional in the protocol; `verify` can
1285
+ * also be omitted by wallet-host re-entry so the dapp-origin Provider runs
1286
+ * verification and receives the session cookie.
1277
1287
  */
1278
1288
  function absolutizeAuth(
1279
1289
  auth: NonNullable<z.output<typeof Rpc.wallet_connect.auth>>,
@@ -1297,10 +1307,6 @@ function absolutizeAuth(
1297
1307
 
1298
1308
  function assertSameAuthOrigin(auth: NonNullable<z.output<typeof Rpc.wallet_connect.auth>>): void {
1299
1309
  if (typeof auth !== 'object') return
1300
- if (!auth.challenge || !auth.verify)
1301
- throw new RpcResponse.InvalidParamsError({
1302
- message: '`auth` requires both `challenge` and `verify` endpoints.',
1303
- })
1304
1310
  const urls = [auth.challenge, auth.verify, auth.logout].filter(
1305
1311
  (u): u is string => typeof u === 'string',
1306
1312
  )
@@ -290,3 +290,31 @@ describe('validateSearch', () => {
290
290
  expect(remote.rejectAll).not.toHaveBeenCalled()
291
291
  })
292
292
  })
293
+
294
+ describe('respond', () => {
295
+ test('behavior: defer returns the provider result without sending a response', async () => {
296
+ const send = vi.fn()
297
+ const remote = Remote.create({
298
+ messenger: {
299
+ on: vi.fn(),
300
+ ready: vi.fn(),
301
+ send,
302
+ } as never,
303
+ provider: {
304
+ request: vi.fn(async () => ({ ok: true })),
305
+ } as never,
306
+ })
307
+
308
+ const result = await remote.respond(
309
+ { id: 1, jsonrpc: '2.0', method: 'wallet_connect' } as never,
310
+ { defer: true },
311
+ )
312
+
313
+ expect(result).toMatchInlineSnapshot(`
314
+ {
315
+ "ok": true,
316
+ }
317
+ `)
318
+ expect(send).not.toHaveBeenCalled()
319
+ })
320
+ })
@@ -110,6 +110,8 @@ export declare namespace ready {
110
110
 
111
111
  export declare namespace respond {
112
112
  type Options = {
113
+ /** Return the resolved result without sending the RPC response. */
114
+ defer?: boolean | undefined
113
115
  /** Error to respond with (takes precedence over result). */
114
116
  error?: { code: number; message: string } | undefined
115
117
  /**
@@ -237,7 +239,7 @@ export function create(options: create.Options): Remote {
237
239
  },
238
240
 
239
241
  async respond(request, options = {}) {
240
- const { error, onError, selector } = options
242
+ const { defer, error, onError, selector } = options
241
243
  const shared = { id: request.id, jsonrpc: '2.0' } as const
242
244
 
243
245
  if (error) {
@@ -254,6 +256,7 @@ export function create(options: create.Options): Remote {
254
256
  let result =
255
257
  'result' in options ? options.result : await provider?.request(request as never)
256
258
  if (selector) result = selector(result)
259
+ if (defer) return result
257
260
  messenger.send(
258
261
  'rpc-response',
259
262
  Object.assign(RpcResponse.from({ ...shared, result }), { _request: request }),
@@ -98,6 +98,29 @@ describe('Encoded', () => {
98
98
  >()
99
99
  })
100
100
 
101
+ test('wallet_connect: showDeposit', () => {
102
+ type Capabilities = NonNullable<
103
+ NonNullable<Rpc.wallet_connect.Encoded['params']>[number]['capabilities']
104
+ >
105
+ type Register = Extract<Capabilities, { method: 'register' }>
106
+ type Login = Extract<Capabilities, { method?: 'login' | undefined }>
107
+ type ShowDeposit = Register['showDeposit']
108
+ type ShowDepositObject = Exclude<Exclude<ShowDeposit, boolean | undefined>, undefined>
109
+
110
+ expectTypeOf<ShowDeposit>().toMatchTypeOf<
111
+ | boolean
112
+ | {
113
+ amount?: string | undefined
114
+ displayName?: string | undefined
115
+ token?: string | undefined
116
+ }
117
+ | undefined
118
+ >()
119
+ expectTypeOf<Login>().not.toHaveProperty('showDeposit')
120
+ expectTypeOf<ShowDepositObject>().not.toHaveProperty('address')
121
+ expectTypeOf<ShowDepositObject>().not.toHaveProperty('chainId')
122
+ })
123
+
101
124
  test('wallet_connect: returns', () => {
102
125
  expectTypeOf<Rpc.wallet_connect.Encoded['returns']>().toHaveProperty('accounts')
103
126
  expectTypeOf<
@@ -156,6 +179,24 @@ describe('Encoded', () => {
156
179
  }>()
157
180
  })
158
181
 
182
+ test('wallet_deposit', () => {
183
+ expectTypeOf<Rpc.wallet_deposit.Encoded>().toMatchTypeOf<{
184
+ method: 'wallet_deposit'
185
+ params:
186
+ | readonly [
187
+ {
188
+ address?: Hex | undefined
189
+ amount?: string | undefined
190
+ chainId?: Hex | undefined
191
+ displayName?: string | undefined
192
+ token?: Hex | string | undefined
193
+ },
194
+ ]
195
+ | undefined
196
+ returns: { receipts?: readonly { transactionHash: Hex }[] | undefined } | undefined
197
+ }>()
198
+ })
199
+
159
200
  test('wallet_switchEthereumChain', () => {
160
201
  expectTypeOf<Rpc.wallet_switchEthereumChain.Encoded>().toEqualTypeOf<{
161
202
  method: 'wallet_switchEthereumChain'