viem 3.0.0-next.12 → 3.0.0-next.13

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 (185) hide show
  1. package/dist/chains/definitions/creditCoin3Devnet.d.ts +6 -0
  2. package/dist/chains/definitions/creditCoin3Devnet.d.ts.map +1 -1
  3. package/dist/chains/definitions/creditCoin3Devnet.js +6 -0
  4. package/dist/chains/definitions/creditCoin3Devnet.js.map +1 -1
  5. package/dist/chains/definitions/creditCoin3Mainnet.d.ts +5 -1
  6. package/dist/chains/definitions/creditCoin3Mainnet.d.ts.map +1 -1
  7. package/dist/chains/definitions/creditCoin3Mainnet.js +4 -0
  8. package/dist/chains/definitions/creditCoin3Mainnet.js.map +1 -1
  9. package/dist/chains/definitions/creditCoin3Testnet.d.ts +5 -1
  10. package/dist/chains/definitions/creditCoin3Testnet.d.ts.map +1 -1
  11. package/dist/chains/definitions/creditCoin3Testnet.js +4 -0
  12. package/dist/chains/definitions/creditCoin3Testnet.js.map +1 -1
  13. package/dist/chains/definitions/crymad.d.ts +25 -0
  14. package/dist/chains/definitions/crymad.d.ts.map +1 -0
  15. package/dist/chains/definitions/crymad.js +23 -0
  16. package/dist/chains/definitions/crymad.js.map +1 -0
  17. package/dist/chains/definitions/crymadL2.d.ts +26 -0
  18. package/dist/chains/definitions/crymadL2.d.ts.map +1 -0
  19. package/dist/chains/definitions/crymadL2.js +24 -0
  20. package/dist/chains/definitions/crymadL2.js.map +1 -0
  21. package/dist/chains/definitions/kortana.d.ts +18 -0
  22. package/dist/chains/definitions/kortana.d.ts.map +1 -0
  23. package/dist/chains/definitions/kortana.js +16 -0
  24. package/dist/chains/definitions/kortana.js.map +1 -0
  25. package/dist/chains/index.d.ts +3 -0
  26. package/dist/chains/index.d.ts.map +1 -1
  27. package/dist/chains/index.js +3 -0
  28. package/dist/chains/index.js.map +1 -1
  29. package/dist/core/actions/wallet/sendCalls.d.ts.map +1 -1
  30. package/dist/core/actions/wallet/sendCalls.js +1 -0
  31. package/dist/core/actions/wallet/sendCalls.js.map +1 -1
  32. package/dist/erc4337/actions/userOperation/waitForReceipt.js +1 -1
  33. package/dist/erc4337/actions/userOperation/waitForReceipt.js.map +1 -1
  34. package/dist/node/transports/ipc.d.ts.map +1 -1
  35. package/dist/node/transports/ipc.js +20 -2
  36. package/dist/node/transports/ipc.js.map +1 -1
  37. package/dist/tempo/Abis.d.ts +778 -174
  38. package/dist/tempo/Abis.d.ts.map +1 -1
  39. package/dist/tempo/Abis.js +178 -37
  40. package/dist/tempo/Abis.js.map +1 -1
  41. package/dist/tempo/Capabilities.d.ts +7 -13
  42. package/dist/tempo/Capabilities.d.ts.map +1 -1
  43. package/dist/tempo/Client.d.ts +0 -5
  44. package/dist/tempo/Client.d.ts.map +1 -1
  45. package/dist/tempo/Client.js +3 -10
  46. package/dist/tempo/Client.js.map +1 -1
  47. package/dist/tempo/ExecutionError.d.ts +81 -0
  48. package/dist/tempo/ExecutionError.d.ts.map +1 -0
  49. package/dist/tempo/ExecutionError.js +309 -0
  50. package/dist/tempo/ExecutionError.js.map +1 -0
  51. package/dist/tempo/Relay.d.ts +303 -0
  52. package/dist/tempo/Relay.d.ts.map +1 -0
  53. package/dist/tempo/Relay.js +170 -0
  54. package/dist/tempo/Relay.js.map +1 -0
  55. package/dist/tempo/Selectors.d.ts +16 -5
  56. package/dist/tempo/Selectors.d.ts.map +1 -1
  57. package/dist/tempo/Selectors.js +16 -5
  58. package/dist/tempo/Selectors.js.map +1 -1
  59. package/dist/tempo/Transport.d.ts +11 -11
  60. package/dist/tempo/Transport.d.ts.map +1 -1
  61. package/dist/tempo/Transport.js +22 -51
  62. package/dist/tempo/Transport.js.map +1 -1
  63. package/dist/tempo/actions/token/burn.d.ts +38 -9
  64. package/dist/tempo/actions/token/burn.d.ts.map +1 -1
  65. package/dist/tempo/actions/token/burn.js +12 -5
  66. package/dist/tempo/actions/token/burn.js.map +1 -1
  67. package/dist/tempo/actions/token/burnSync.d.ts +2 -0
  68. package/dist/tempo/actions/token/burnSync.d.ts.map +1 -1
  69. package/dist/tempo/chainConfig.d.ts +4 -5
  70. package/dist/tempo/chainConfig.d.ts.map +1 -1
  71. package/dist/tempo/chainConfig.js +6 -1
  72. package/dist/tempo/chainConfig.js.map +1 -1
  73. package/dist/tempo/index.d.ts +5 -3
  74. package/dist/tempo/index.d.ts.map +1 -1
  75. package/dist/tempo/index.js +5 -3
  76. package/dist/tempo/index.js.map +1 -1
  77. package/dist/tempo/internal/relay/cache.d.ts +9 -0
  78. package/dist/tempo/internal/relay/cache.d.ts.map +1 -0
  79. package/dist/tempo/internal/relay/cache.js +72 -0
  80. package/dist/tempo/internal/relay/cache.js.map +1 -0
  81. package/dist/tempo/internal/relay/deadline.d.ts +4 -0
  82. package/dist/tempo/internal/relay/deadline.d.ts.map +1 -0
  83. package/dist/tempo/internal/relay/deadline.js +34 -0
  84. package/dist/tempo/internal/relay/deadline.js.map +1 -0
  85. package/dist/tempo/internal/relay/error.d.ts +47 -0
  86. package/dist/tempo/internal/relay/error.d.ts.map +1 -0
  87. package/dist/tempo/internal/relay/error.js +101 -0
  88. package/dist/tempo/internal/relay/error.js.map +1 -0
  89. package/dist/tempo/internal/relay/feePayer.d.ts +70 -0
  90. package/dist/tempo/internal/relay/feePayer.d.ts.map +1 -0
  91. package/dist/tempo/internal/relay/feePayer.js +442 -0
  92. package/dist/tempo/internal/relay/feePayer.js.map +1 -0
  93. package/dist/tempo/internal/relay/feeToken.d.ts +38 -0
  94. package/dist/tempo/internal/relay/feeToken.d.ts.map +1 -0
  95. package/dist/tempo/internal/relay/feeToken.js +143 -0
  96. package/dist/tempo/internal/relay/feeToken.js.map +1 -0
  97. package/dist/tempo/internal/relay/multisig.d.ts +4 -0
  98. package/dist/tempo/internal/relay/multisig.d.ts.map +1 -0
  99. package/dist/tempo/{Multisig.js → internal/relay/multisig.js} +134 -126
  100. package/dist/tempo/internal/relay/multisig.js.map +1 -0
  101. package/dist/tempo/internal/relay/plugin.d.ts +5 -0
  102. package/dist/tempo/internal/relay/plugin.d.ts.map +1 -0
  103. package/dist/tempo/internal/relay/plugin.js +10 -0
  104. package/dist/tempo/internal/relay/plugin.js.map +1 -0
  105. package/dist/tempo/internal/relay/preflight.d.ts +17 -0
  106. package/dist/tempo/internal/relay/preflight.d.ts.map +1 -0
  107. package/dist/tempo/internal/relay/preflight.js +53 -0
  108. package/dist/tempo/internal/relay/preflight.js.map +1 -0
  109. package/dist/tempo/internal/relay/request.d.ts +16 -0
  110. package/dist/tempo/internal/relay/request.d.ts.map +1 -0
  111. package/dist/tempo/internal/relay/request.js +327 -0
  112. package/dist/tempo/internal/relay/request.js.map +1 -0
  113. package/dist/tempo/internal/relay/simulate.d.ts +62 -0
  114. package/dist/tempo/internal/relay/simulate.d.ts.map +1 -0
  115. package/dist/tempo/internal/relay/simulate.js +284 -0
  116. package/dist/tempo/internal/relay/simulate.js.map +1 -0
  117. package/dist/tempo/internal/relay/utils.d.ts +346 -0
  118. package/dist/tempo/internal/relay/utils.d.ts.map +1 -0
  119. package/dist/tempo/internal/relay/utils.js +185 -0
  120. package/dist/tempo/internal/relay/utils.js.map +1 -0
  121. package/dist/tempo/internal/relay/virtualAddress.d.ts +9 -0
  122. package/dist/tempo/internal/relay/virtualAddress.d.ts.map +1 -0
  123. package/dist/tempo/internal/relay/virtualAddress.js +73 -0
  124. package/dist/tempo/internal/relay/virtualAddress.js.map +1 -0
  125. package/dist/tempo/internal/relay.d.ts +4 -0
  126. package/dist/tempo/internal/relay.d.ts.map +1 -0
  127. package/dist/tempo/internal/relay.js +132 -0
  128. package/dist/tempo/internal/relay.js.map +1 -0
  129. package/dist/tokens/definitions/ousd.d.ts +26 -0
  130. package/dist/tokens/definitions/ousd.d.ts.map +1 -0
  131. package/dist/tokens/definitions/ousd.js +26 -0
  132. package/dist/tokens/definitions/ousd.js.map +1 -0
  133. package/dist/tokens/index.d.ts +2 -0
  134. package/dist/tokens/index.d.ts.map +1 -1
  135. package/dist/tokens/index.js +2 -0
  136. package/dist/tokens/index.js.map +1 -1
  137. package/dist/tokens/sets.d.ts +33 -0
  138. package/dist/tokens/sets.d.ts.map +1 -1
  139. package/dist/tokens/sets.js +4 -1
  140. package/dist/tokens/sets.js.map +1 -1
  141. package/dist/version.d.ts +1 -1
  142. package/dist/version.js +1 -1
  143. package/package.json +1 -1
  144. package/src/chains/definitions/creditCoin3Devnet.ts +6 -0
  145. package/src/chains/definitions/creditCoin3Mainnet.ts +4 -0
  146. package/src/chains/definitions/creditCoin3Testnet.ts +4 -0
  147. package/src/chains/definitions/crymad.ts +23 -0
  148. package/src/chains/definitions/crymadL2.ts +24 -0
  149. package/src/chains/definitions/kortana.ts +16 -0
  150. package/src/chains/index.ts +3 -0
  151. package/src/core/actions/wallet/sendCalls.ts +1 -0
  152. package/src/erc4337/actions/userOperation/waitForReceipt.ts +1 -1
  153. package/src/node/transports/ipc.ts +17 -2
  154. package/src/tempo/Abis.ts +179 -37
  155. package/src/tempo/Account.ts +1 -1
  156. package/src/tempo/Capabilities.ts +7 -15
  157. package/src/tempo/Client.ts +3 -23
  158. package/src/tempo/ExecutionError.ts +428 -0
  159. package/src/tempo/Relay.ts +430 -0
  160. package/src/tempo/Selectors.ts +25 -11
  161. package/src/tempo/Transport.ts +71 -41
  162. package/src/tempo/actions/token/burn.ts +21 -9
  163. package/src/tempo/actions/token/burnSync.ts +2 -0
  164. package/src/tempo/chainConfig.ts +9 -6
  165. package/src/tempo/index.ts +7 -4
  166. package/src/tempo/internal/relay/cache.ts +82 -0
  167. package/src/tempo/internal/relay/deadline.ts +45 -0
  168. package/src/tempo/internal/relay/error.ts +129 -0
  169. package/src/tempo/internal/relay/feePayer.ts +693 -0
  170. package/src/tempo/internal/relay/feeToken.ts +190 -0
  171. package/src/tempo/{Multisig.ts → internal/relay/multisig.ts} +165 -193
  172. package/src/tempo/internal/relay/plugin.ts +13 -0
  173. package/src/tempo/internal/relay/preflight.ts +83 -0
  174. package/src/tempo/internal/relay/request.ts +401 -0
  175. package/src/tempo/internal/relay/simulate.ts +394 -0
  176. package/src/tempo/internal/relay/utils.ts +213 -0
  177. package/src/tempo/internal/relay/virtualAddress.ts +95 -0
  178. package/src/tempo/internal/relay.ts +149 -0
  179. package/src/tokens/definitions/ousd.ts +26 -0
  180. package/src/tokens/index.ts +2 -0
  181. package/src/tokens/sets.ts +4 -1
  182. package/src/version.ts +1 -1
  183. package/dist/tempo/Multisig.d.ts +0 -39
  184. package/dist/tempo/Multisig.d.ts.map +0 -1
  185. package/dist/tempo/Multisig.js.map +0 -1
@@ -0,0 +1,190 @@
1
+ import type { Address } from 'abitype'
2
+ import { RpcResponse } from 'ox'
3
+ import type { Client } from '../../../core/Client.js'
4
+ import { tokens as tokenSets } from '../../../tokens/sets.js'
5
+ import * as Actions from '../../actions/index.js'
6
+ import type * as Relay from '../../Relay.js'
7
+ import * as Store from './cache.js'
8
+ import * as Preflight from './preflight.js'
9
+ import * as Utils from './utils.js'
10
+ import { extractCalls, getVirtualAddressTargets } from './virtualAddress.js'
11
+
12
+ export function create(): Relay.Plugin {
13
+ return {
14
+ async handleRequest(context, next) {
15
+ const { request } = context
16
+ if (request.method !== 'eth_fillTransaction') return next()
17
+ const parameters = request.params![0] as Record<string, unknown>
18
+ const transaction = Utils.normalizeFillTransactionRequest(parameters)
19
+
20
+ if (transaction.feeToken) {
21
+ context.request = {
22
+ ...request,
23
+ params: [
24
+ Utils.formatFillTransactionRequest(context.client, transaction),
25
+ ],
26
+ }
27
+ return next()
28
+ }
29
+
30
+ const tokens = await context.resolveTokens()
31
+ const candidates = [
32
+ ...tokens,
33
+ ...callTargetTokens(transaction).filter(
34
+ (token) =>
35
+ !tokens.some(
36
+ (candidate) => candidate.toLowerCase() === token.toLowerCase(),
37
+ ),
38
+ ),
39
+ ]
40
+
41
+ const resolved = transaction.feePayer
42
+ ? { feeToken: tokens[0], virtualAddresses: undefined }
43
+ : await resolveFeeToken(context.client, {
44
+ account: transaction.from as Address | undefined,
45
+ feeToken: transaction.feeToken as Address | undefined,
46
+ targets: getVirtualAddressTargets(extractCalls(transaction)),
47
+ tokens: candidates,
48
+ })
49
+
50
+ const { feeToken, virtualAddresses } = resolved
51
+ const selected = { ...transaction, ...(feeToken ? { feeToken } : {}) }
52
+ context.request = {
53
+ ...request,
54
+ params: [Utils.formatFillTransactionRequest(context.client, selected)],
55
+ }
56
+ await next()
57
+ const result = context.result as Relay.Plugin.FillResult
58
+ context.result = {
59
+ ...result,
60
+ // Unsigned sponsored estimates can omit the fee token selected by this plugin.
61
+ tx: {
62
+ ...result.tx,
63
+ ...(!result.tx.feeToken && feeToken ? { feeToken } : {}),
64
+ },
65
+ ...(virtualAddresses
66
+ ? { capabilities: { ...result.capabilities, virtualAddresses } }
67
+ : {}),
68
+ }
69
+ },
70
+ }
71
+ }
72
+
73
+ export async function getDefaultTokens(
74
+ chainId: number,
75
+ ): Promise<readonly Address[]> {
76
+ return tokenSets.tempo.flatMap((token) => {
77
+ const address = (token.addresses as Record<number, Address>)[chainId]
78
+ return address ? [address] : []
79
+ })
80
+ }
81
+
82
+ /** Resolves a funded fee-token candidate with fee AMM liquidity. @internal */
83
+ export async function resolveFeeToken(
84
+ client: Client,
85
+ options: resolveFeeToken.Options,
86
+ ) {
87
+ const { feeToken, account, exclude, tokens } = options
88
+ if (feeToken || !account) return { feeToken, virtualAddresses: undefined }
89
+ const candidates = [
90
+ ...new Set(tokens?.map((token) => token.toLowerCase() as Address)),
91
+ ]
92
+ if (candidates.length > 100)
93
+ throw new RpcResponse.InvalidParamsError({
94
+ message: 'Fee-token candidates exceed the limit of 100 tokens.',
95
+ })
96
+ const { preferredToken, preferredBalance, balances, virtualAddresses } =
97
+ await Preflight.read(client, {
98
+ account,
99
+ tokens: candidates,
100
+ targets: options.targets,
101
+ })
102
+ const minimumBalance = options.minimumBalance ?? 1n
103
+ if (
104
+ preferredToken.toLowerCase() !== exclude?.toLowerCase() &&
105
+ preferredBalance >= minimumBalance
106
+ )
107
+ return { feeToken: preferredToken, virtualAddresses }
108
+
109
+ let best: { address: Address; balance: bigint } | undefined
110
+ for (const asset of balances) {
111
+ if (
112
+ asset.balance < minimumBalance ||
113
+ asset.address.toLowerCase() === exclude?.toLowerCase()
114
+ )
115
+ continue
116
+ if (!best || asset.balance > best.balance) best = asset
117
+ }
118
+ return { feeToken: best?.address, virtualAddresses }
119
+ }
120
+
121
+ export declare namespace resolveFeeToken {
122
+ type Options = {
123
+ minimumBalance?: bigint | undefined
124
+ exclude?: Address | undefined
125
+ feeToken?: Address | undefined
126
+ account?: Address | undefined
127
+ tokens?: readonly Address[] | undefined
128
+ targets?: readonly Address[] | undefined
129
+ }
130
+ }
131
+
132
+ /** Includes call-target TIP20 tokens as fee candidates, allowing transfers to pay fees with the transferred token. */
133
+ function callTargetTokens(
134
+ transaction: Record<string, unknown>,
135
+ ): readonly Address[] {
136
+ const calls = transaction.calls as readonly { to?: Address }[] | undefined
137
+ if (!calls) return []
138
+ const out: Address[] = []
139
+ const seen = new Set<string>()
140
+ for (const c of calls) {
141
+ if (!c.to) continue
142
+ const lower = c.to.toLowerCase()
143
+ if (!lower.startsWith('0x20c0')) continue
144
+ if (seen.has(lower)) continue
145
+ seen.add(lower)
146
+ out.push(c.to)
147
+ if (out.length > 100)
148
+ throw new RpcResponse.InvalidParamsError({
149
+ message: 'Fee-token candidates exceed the limit of 100 tokens.',
150
+ })
151
+ }
152
+ return out
153
+ }
154
+
155
+ export async function resolveTokenMetadata(
156
+ client: Client,
157
+ options: resolveTokenMetadata.Options,
158
+ ) {
159
+ const { token, tokenMetadata, store } = options
160
+ const meta =
161
+ tokenMetadata?.[token] ?? tokenMetadata?.[token.toLowerCase() as Address]
162
+ // Tempo simulation metadata covers TIP-20 tokens, whose decimals are always six.
163
+ if (token.toLowerCase().startsWith('0x20c0') && meta)
164
+ return { decimals: 6, symbol: meta.symbol, name: meta.name }
165
+ // TIP-20 metadata (decimals/symbol/name) is immutable per token, so cache
166
+ // long-term. Skips the multicall RPC on cache hits.
167
+ const fetcher = () => Actions.token.getMetadata(client, { token })
168
+ const fallback = store
169
+ ? await Store.memoize(fetcher, {
170
+ key: `tokenMetadata:${client.chain?.id ?? 0}:${token.toLowerCase()}`,
171
+ store,
172
+ ttl: 24 * 60 * 60 * 1000,
173
+ })
174
+ : await fetcher()
175
+ return {
176
+ decimals: fallback.decimals ?? 6,
177
+ symbol: meta?.symbol || fallback.symbol,
178
+ name: meta?.name || fallback.name,
179
+ }
180
+ }
181
+
182
+ export declare namespace resolveTokenMetadata {
183
+ type Options = {
184
+ token: Address
185
+ tokenMetadata?:
186
+ | Record<Address, { name: string; symbol: string; currency: string }>
187
+ | undefined
188
+ store?: Store.Store | undefined
189
+ }
190
+ }
@@ -8,232 +8,204 @@ import {
8
8
  SignatureEnvelope,
9
9
  TxEnvelopeTempo,
10
10
  } from 'ox/tempo'
11
- import { getNumber as getBlockNumber } from '../core/actions/block/getNumber.js'
12
- import { create as createClient } from '../core/Client.js'
13
- import { custom } from '../core/transports/custom.js'
14
- type EIP1193RequestOptions = Parameters<
15
- ReturnType<typeof createClient>['request']
16
- >[1]
17
-
18
- import * as Abis from './Abis.js'
19
- import { getConfigCommitment } from './actions/multisig/getConfigCommitment.js'
20
- import * as Addresses from './Addresses.js'
21
- import * as ConfigStore from './multisig/Config.js'
22
- import * as OperationStore from './multisig/Operation.js'
23
- import type * as Store from './Store.js'
11
+ import { getNumber as getBlockNumber } from '../../../core/actions/block/getNumber.js'
12
+ import { create as createClient } from '../../../core/Client.js'
13
+ import * as Abis from '../../Abis.js'
14
+ import * as Addresses from '../../Addresses.js'
15
+ import { getConfigCommitment } from '../../actions/multisig/getConfigCommitment.js'
16
+ import * as ConfigStore from '../../multisig/Config.js'
17
+ import * as OperationStore from '../../multisig/Operation.js'
18
+ import type * as Relay from '../../Relay.js'
19
+ import type * as Store from '../../Store.js'
20
+ import * as Plugin from './plugin.js'
24
21
 
25
22
  const submissionTtl = 30_000
26
23
  const pollingInterval = 100
27
24
 
28
- /**
29
- * Creates an RPC request handler that coordinates native multisig approvals.
30
- *
31
- * @param next - Downstream RPC request handler.
32
- * @param parameters - Handler parameters.
33
- * @returns The multisig-aware RPC request handler.
34
- */
35
- export function handleRequest(
36
- next: handleRequest.Handler,
37
- parameters: handleRequest.Parameters,
38
- ): handleRequest.Handler {
39
- if (!parameters.store.compareAndSet)
25
+ /** Creates middleware for multisig approval coordination. @internal */
26
+ export function create(
27
+ options: Relay.multisig.Options,
28
+ ): Relay.multisig.ReturnType {
29
+ if (!options.store.compareAndSet)
40
30
  throw new RpcResponse.InvalidParamsError({
41
31
  message:
42
32
  'Multisig coordination requires a store with atomic `compareAndSet`.',
43
33
  })
44
- return async (request, requestOptions_) => {
45
- const requestOptions = await resolveRequestOptions({
46
- request,
47
- requestOptions: requestOptions_,
48
- store: parameters.store,
49
- })
50
- const client = createClient({
51
- transport: custom({
52
- request: ({ method, params }, options) =>
53
- next({ method, params }, { ...requestOptions, ...options }),
54
- }),
55
- })
56
-
57
- if (request.method === 'multisig_getConfig') {
58
- const value = request.params?.[0]
59
- const address =
60
- value && typeof value === 'object' && 'address' in value
61
- ? value.address
62
- : undefined
63
- if (
64
- typeof address !== 'string' ||
65
- !Address_.validate(address) ||
66
- Hex.toBigInt(address) === 0n
67
- )
68
- throw new RpcResponse.InvalidParamsError({
69
- message: 'Expected a multisig account address.',
70
- })
71
- const blockNumber = await getBlockNumber(client, { cacheTime: 0 })
72
- const commitment = await getConfigCommitment(client, {
73
- account: address,
74
- blockNumber,
34
+ return Plugin.multisig({
35
+ async handleRequest(context, forward) {
36
+ const { request, options: requestOptions_ } = context
37
+ const requestOptions = await resolveRequestOptions({
38
+ request,
39
+ requestOptions: requestOptions_,
40
+ store: options.store,
75
41
  })
76
- const config = await ConfigStore.read(parameters.store, {
77
- address,
78
- commitment,
42
+ context.options = requestOptions ?? {}
43
+ const next: Relay.handleRequest.Handler = async (request, options) => {
44
+ return context
45
+ .getClient(options?.chainId)
46
+ .request(request as never, options)
47
+ }
48
+ const client = createClient({
49
+ transport: {
50
+ key: 'multisig-forward',
51
+ name: 'Multisig Forwarding',
52
+ type: 'multisig-forward',
53
+ setup: () => ({
54
+ request: ((
55
+ request: Relay.handleRequest.Request,
56
+ options?: Relay.handleRequest.RequestOptions,
57
+ ) =>
58
+ next(request, { ...requestOptions, ...options })) as ReturnType<
59
+ typeof createClient
60
+ >['request'],
61
+ }),
62
+ },
79
63
  })
80
- if (!config) return null
81
- return MultisigConfig.toRpc(config)
82
- }
83
64
 
84
- if (request.method === 'multisig_getOperation') {
85
- const hash = request.params?.[0]
86
- if (typeof hash !== 'string' || !Hash.validate(hash))
87
- throw new RpcResponse.InvalidParamsError({
88
- message: 'Expected a multisig operation hash.',
65
+ if (request.method === 'multisig_getConfig') {
66
+ const value = request.params?.[0]
67
+ const address =
68
+ value && typeof value === 'object' && 'address' in value
69
+ ? value.address
70
+ : undefined
71
+ if (
72
+ typeof address !== 'string' ||
73
+ !Address_.validate(address) ||
74
+ Hex.toBigInt(address) === 0n
75
+ )
76
+ throw new RpcResponse.InvalidParamsError({
77
+ message: 'Expected a multisig account address.',
78
+ })
79
+ const blockNumber = await getBlockNumber(client, { cacheTime: 0 })
80
+ const commitment = await getConfigCommitment(client, {
81
+ account: address,
82
+ blockNumber,
89
83
  })
90
- const operation = await OperationStore.read(parameters.store, hash)
91
- return operation ? MultisigOperation.toRpc(operation) : null
92
- }
84
+ const config = await ConfigStore.read(options.store, {
85
+ address,
86
+ commitment,
87
+ })
88
+ if (!config) return null
89
+ return MultisigConfig.toRpc(config)
90
+ }
93
91
 
94
- if (request.method === 'multisig_approveKeyAuthorization')
95
- return await approveKeyAuthorization({
96
- client,
97
- request,
98
- store: parameters.store,
99
- })
92
+ if (request.method === 'multisig_getOperation') {
93
+ const hash = request.params?.[0]
94
+ if (typeof hash !== 'string' || !Hash.validate(hash))
95
+ throw new RpcResponse.InvalidParamsError({
96
+ message: 'Expected a multisig operation hash.',
97
+ })
98
+ const operation = await OperationStore.read(options.store, hash)
99
+ return operation ? MultisigOperation.toRpc(operation) : null
100
+ }
100
101
 
101
- if (
102
- request.method === 'eth_getTransactionByHash' ||
103
- request.method === 'eth_getTransactionReceipt'
104
- ) {
105
- const hash = request.params?.[0]
106
- if (typeof hash !== 'string' || !Hash.validate(hash))
107
- return await next(request, requestOptions)
108
- const operation = await OperationStore.read(parameters.store, hash)
109
- if (!operation || operation.type !== 'transaction')
110
- return await next(request, requestOptions)
111
- if (request.method === 'eth_getTransactionReceipt') {
112
- if (operation.status === 'pending') return null
102
+ if (request.method === 'multisig_approveKeyAuthorization')
103
+ return await approveKeyAuthorization({
104
+ client,
105
+ request,
106
+ store: options.store,
107
+ })
108
+
109
+ if (
110
+ request.method === 'eth_getTransactionByHash' ||
111
+ request.method === 'eth_getTransactionReceipt'
112
+ ) {
113
+ const hash = request.params?.[0]
114
+ if (typeof hash !== 'string' || !Hash.validate(hash)) return forward()
115
+ const operation = await OperationStore.read(options.store, hash)
116
+ if (!operation || operation.type !== 'transaction') return forward()
117
+ if (request.method === 'eth_getTransactionReceipt') {
118
+ if (operation.status === 'pending') return null
119
+ const transactionHash = await getSubmittedTransactionHash(
120
+ options.store,
121
+ operation,
122
+ )
123
+ if (!transactionHash) return null
124
+ const receipt = await next(
125
+ {
126
+ ...request,
127
+ params: [transactionHash],
128
+ },
129
+ requestOptions,
130
+ )
131
+ if (!receipt || typeof receipt !== 'object') return receipt
132
+ const success =
133
+ operation.status === 'submitting'
134
+ ? await completeSubmission(
135
+ options.store,
136
+ operation,
137
+ transactionHash,
138
+ )
139
+ : operation
140
+ return {
141
+ ...receipt,
142
+ multisig: MultisigOperation.toRpc(success),
143
+ }
144
+ }
145
+ if (operation.status === 'pending')
146
+ return await toTransaction(client, operation)
113
147
  const transactionHash = await getSubmittedTransactionHash(
114
- parameters.store,
148
+ options.store,
115
149
  operation,
116
150
  )
117
- if (!transactionHash) return null
118
- const receipt = await next(
151
+ if (!transactionHash) return await toTransaction(client, operation)
152
+ const transaction = await next(
119
153
  {
120
154
  ...request,
121
155
  params: [transactionHash],
122
156
  },
123
157
  requestOptions,
124
158
  )
125
- if (!receipt || typeof receipt !== 'object') return receipt
159
+ if (!transaction || typeof transaction !== 'object')
160
+ return await toTransaction(client, operation)
126
161
  const success =
127
162
  operation.status === 'submitting'
128
163
  ? await completeSubmission(
129
- parameters.store,
164
+ options.store,
130
165
  operation,
131
166
  transactionHash,
132
167
  )
133
168
  : operation
134
169
  return {
135
- ...receipt,
170
+ ...transaction,
136
171
  multisig: MultisigOperation.toRpc(success),
137
172
  }
138
173
  }
139
- if (operation.status === 'pending')
140
- return await toTransaction(client, operation)
141
- const transactionHash = await getSubmittedTransactionHash(
142
- parameters.store,
143
- operation,
144
- )
145
- if (!transactionHash) return await toTransaction(client, operation)
146
- const transaction = await next(
147
- {
148
- ...request,
149
- params: [transactionHash],
150
- },
151
- requestOptions,
174
+
175
+ if (
176
+ request.method !== 'eth_sendRawTransaction' &&
177
+ request.method !== 'eth_sendRawTransactionSync' &&
178
+ request.method !== 'multisig_approveRawTransaction' &&
179
+ request.method !== 'multisig_approveRawTransactionSync'
152
180
  )
153
- if (!transaction || typeof transaction !== 'object')
154
- return await toTransaction(client, operation)
155
- const success =
156
- operation.status === 'submitting'
157
- ? await completeSubmission(
158
- parameters.store,
159
- operation,
160
- transactionHash,
161
- )
162
- : operation
163
- return {
164
- ...transaction,
165
- multisig: MultisigOperation.toRpc(success),
166
- }
167
- }
181
+ return forward()
168
182
 
169
- if (
170
- request.method !== 'eth_sendRawTransaction' &&
171
- request.method !== 'eth_sendRawTransactionSync' &&
172
- request.method !== 'multisig_approveRawTransaction' &&
173
- request.method !== 'multisig_approveRawTransactionSync'
174
- )
175
- return await next(request, requestOptions)
183
+ const standard =
184
+ request.method === 'eth_sendRawTransaction' ||
185
+ request.method === 'eth_sendRawTransactionSync'
186
+ const serialized = request.params?.[0]
187
+ if (!isSerializedTempoTransaction(serialized)) {
188
+ if (standard) return forward()
189
+ throw new RpcResponse.InvalidParamsError({
190
+ message: 'Expected a serialized Tempo multisig transaction.',
191
+ })
192
+ }
176
193
 
177
- const standard =
178
- request.method === 'eth_sendRawTransaction' ||
179
- request.method === 'eth_sendRawTransactionSync'
180
- const serialized = request.params?.[0]
181
- if (!isSerializedTempoTransaction(serialized)) {
182
- if (standard) return await next(request, requestOptions)
183
- throw new RpcResponse.InvalidParamsError({
184
- message: 'Expected a serialized Tempo multisig transaction.',
194
+ return await submit({
195
+ client,
196
+ method: request.method,
197
+ next,
198
+ request,
199
+ requestOptions,
200
+ serialized,
201
+ store: options.store,
185
202
  })
186
- }
187
-
188
- return await submit({
189
- client,
190
- method: request.method,
191
- next,
192
- request,
193
- requestOptions,
194
- serialized,
195
- store: parameters.store,
196
- })
197
- }
198
- }
199
-
200
- export declare namespace handleRequest {
201
- /** RPC request handler. */
202
- export type Handler = (
203
- request: Request,
204
- options?: RequestOptions | undefined,
205
- ) => Promise<unknown>
206
-
207
- /** RPC request passed to a handler. */
208
- export type Request = {
209
- /** RPC method name. */
210
- method: string
211
- /** RPC method parameters. */
212
- params?: readonly unknown[] | undefined
213
- }
214
-
215
- /** Options for one handled request. */
216
- export type RequestOptions = EIP1193RequestOptions & {
217
- /** Chain selected by the caller or inferred from the multisig request. */
218
- chainId?: number | undefined
219
- }
220
-
221
- /** Parameters for {@link handleRequest}. */
222
- export type Parameters = {
223
- /** Store shared by multisig coordinators. */
224
- store: Store.Atomic
225
- }
226
-
227
- /** Error type for {@link handleRequest}. */
228
- export type ErrorType =
229
- | ConfigStore.InvalidStoreValueError
230
- | OperationStore.InvalidStoreValueError
231
- | OperationStore.StoreConflictError
232
- | RpcResponse.InvalidParamsError
203
+ },
204
+ })
233
205
  }
234
206
 
235
207
  /** Collects approvals and submits a transaction after quorum. @internal */
236
- // biome-ignore lint/correctness/noUnusedVariables: _
208
+ // biome-ignore lint/correctness/noUnusedVariables: declaration merge
237
209
  async function submit(options: submit.Options) {
238
210
  const transaction = (() => {
239
211
  try {
@@ -548,11 +520,11 @@ declare namespace submit {
548
520
  | 'multisig_approveRawTransaction'
549
521
  | 'multisig_approveRawTransactionSync'
550
522
  /** Downstream RPC request handler. */
551
- next: handleRequest.Handler
523
+ next: Relay.handleRequest.Handler
552
524
  /** Original RPC request. */
553
- request: handleRequest.Request
525
+ request: Relay.handleRequest.Request
554
526
  /** Original request overrides. */
555
- requestOptions?: handleRequest.RequestOptions | undefined
527
+ requestOptions?: Relay.handleRequest.RequestOptions | undefined
556
528
  /** Serialized Tempo transaction. */
557
529
  serialized: Hex.Hex
558
530
  /** Shared multisig store. */
@@ -755,7 +727,7 @@ declare namespace approveKeyAuthorization {
755
727
  /** Client used to validate configs. */
756
728
  client: ReturnType<typeof createClient>
757
729
  /** Original RPC request. */
758
- request: handleRequest.Request
730
+ request: Relay.handleRequest.Request
759
731
  /** Shared multisig store. */
760
732
  store: Store.Atomic
761
733
  }
@@ -1245,10 +1217,10 @@ function getTransactionHash(result: unknown): Hex.Hex {
1245
1217
 
1246
1218
  /** Resolves and validates the chain used by a handled request. */
1247
1219
  async function resolveRequestOptions(options: {
1248
- request: handleRequest.Request
1249
- requestOptions?: handleRequest.RequestOptions | undefined
1220
+ request: Relay.handleRequest.Request
1221
+ requestOptions?: Relay.handleRequest.RequestOptions | undefined
1250
1222
  store: Store.Store
1251
- }): Promise<handleRequest.RequestOptions | undefined> {
1223
+ }): Promise<Relay.handleRequest.RequestOptions | undefined> {
1252
1224
  const { request, requestOptions, store } = options
1253
1225
  const chainId_explicit = requestOptions?.chainId
1254
1226
  if (
@@ -1274,7 +1246,7 @@ async function resolveRequestOptions(options: {
1274
1246
 
1275
1247
  /** Resolves a chain from request fields, envelopes, or stored operations. */
1276
1248
  async function resolveRequestChainId(
1277
- request: handleRequest.Request,
1249
+ request: Relay.handleRequest.Request,
1278
1250
  store: Store.Store,
1279
1251
  ) {
1280
1252
  const value = request.params?.[0]
@@ -0,0 +1,13 @@
1
+ import type * as Relay from '../../Relay.js'
2
+
3
+ const multisigPlugins = new WeakSet<Relay.Plugin>()
4
+
5
+ /** Registers the built-in multisig plugin for transport capability detection. */
6
+ export function multisig(plugin: Relay.Plugin): Relay.multisig.ReturnType {
7
+ multisigPlugins.add(plugin)
8
+ return plugin as Relay.multisig.ReturnType
9
+ }
10
+
11
+ export function isMultisig(plugin: Relay.Plugin): boolean {
12
+ return multisigPlugins.has(plugin)
13
+ }