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,83 @@
1
+ import type { Address } from 'abitype'
2
+ import { VirtualAddress } from 'ox/tempo'
3
+ import { call } from '../../../core/actions/call.js'
4
+ import type { Client } from '../../../core/Client.js'
5
+ const zeroAddress = '0x0000000000000000000000000000000000000000'
6
+ import {
7
+ decode as decodeAbiParameters,
8
+ encode as encodeAbiParameters,
9
+ } from 'ox/AbiParameters'
10
+ import * as Hex from 'ox/Hex'
11
+ import * as Addresses from '../../Addresses.js'
12
+
13
+ /** Reads fee preferences, liquid fee-token balances, and virtual masters in one RPC request. */
14
+ export async function read(
15
+ client: Client,
16
+ options: {
17
+ account?: Address | undefined
18
+ tokens?: readonly Address[] | undefined
19
+ targets?: readonly Address[] | undefined
20
+ },
21
+ ) {
22
+ const { account = zeroAddress, tokens = [], targets = [] } = options
23
+ const masterIds = [
24
+ ...new Set(targets.map((target) => VirtualAddress.parse(target).masterId)),
25
+ ]
26
+ const { data } = await call(client, {
27
+ batch: false,
28
+ data: Hex.concat(
29
+ bytecode,
30
+ encodeAbiParameters(
31
+ [
32
+ { type: 'address' },
33
+ { type: 'address' },
34
+ { type: 'address[]' },
35
+ { type: 'address' },
36
+ { type: 'bytes4[]' },
37
+ ],
38
+ [
39
+ account,
40
+ Addresses.feeManager,
41
+ tokens,
42
+ Addresses.addressRegistry,
43
+ masterIds,
44
+ ],
45
+ ),
46
+ ),
47
+ })
48
+ const [preferredToken, preferredBalance, balances, resolved, masters] =
49
+ decodeAbiParameters(
50
+ [
51
+ { type: 'address' },
52
+ { type: 'uint256' },
53
+ { type: 'uint256[]' },
54
+ { type: 'bool' },
55
+ { type: 'address[]' },
56
+ ],
57
+ data ?? '0x',
58
+ )
59
+ return {
60
+ preferredToken,
61
+ preferredBalance,
62
+ balances: tokens.map((address, index) => ({
63
+ address,
64
+ balance: balances[index]!,
65
+ })),
66
+ virtualAddresses:
67
+ resolved && targets.length > 0
68
+ ? (Object.fromEntries(
69
+ targets.map((target) => {
70
+ const master =
71
+ masters[
72
+ masterIds.indexOf(VirtualAddress.parse(target).masterId)
73
+ ]!
74
+ return [target, master === zeroAddress ? null : master]
75
+ }),
76
+ ) as Record<Address, Address | null>)
77
+ : undefined,
78
+ }
79
+ }
80
+
81
+ // forge inspect --root contracts src/deployless/RelayPreflight.sol:RelayPreflight bytecode --optimize --evm-version paris
82
+ const bytecode =
83
+ '0x608060405234801561001057600080fd5b506040516108be3803806108be83398101604081905261002f916106ed565b6040516001600160a01b038616602482015260009061008090869060440160408051601f198184030181529190526020810180516001600160e01b03908116631da931f560e31b179091526103fc16565b9150600090506001600160a01b0382111561009c57600061009e565b815b905060006001600160a01b038816156101305760405141602482015260009081906100fb908a9060440160408051601f198184030181529190526020810180516001600160e01b039081166336e2a53d60e11b179091526103fc16565b9150915081801561011357506001600160a01b038111155b1561012d578015610124578061012a565b608360961b5b92505b50505b600086516001600160401b0381111561014b5761014b610601565b604051908082528060200260200182016040528015610174578160200160208202803683370190505b506040516001600160a01b038b1660248201529091506000908190819060440160408051601f198184030181529190526020810180516001600160e01b03166370a0823160e01b179052905060005b8a51811015610270576101f78b82815181106101e1576101e16107e9565b6020026020010151838e8961042c60201b60201c565b858281518110610209576102096107e9565b602002602001018181525050866001600160a01b03168b8281518110610231576102316107e9565b60200260200101516001600160a01b031603610268576001925084818151811061025d5761025d6107e9565b602002602001015193505b6001016101c3565b508115801561028757506001600160a01b03861615155b1561029b5761029886828d8861042c565b92505b600088516001600160401b038111156102b6576102b6610601565b6040519080825280602002602001820160405280156102df578160200160208202803683370190505b509050600160005b8a518110156103c7576000806103648e8e8581518110610309576103096107e9565b602002602001015160405160240161033191906001600160e01b031991909116815260200190565b60408051601f198184030181529190526020810180516001600160e01b03908116636c2558b360e11b179091526103fc16565b9150915081158061037b57506001600160a01b0381115b1561038957600093506103bd565b8085848151811061039c5761039c6107e9565b60200260200101906001600160a01b031690816001600160a01b0316815250505b50506001016102e7565b50600088868884866040516020016103e3959493929190610844565b6040516020818303038152906040529050805160208201f35b60008060405160208185516020870188620186a0fa60203d1015169250821561042457805191505b509250929050565b60008061043986866103fc565b91505080158061045057506001600160a01b038316155b1561045f576000915050610565565b826001600160a01b0316866001600160a01b03161480610485575061048584878561056d565b15610491579050610565565b6040805160048152602481019091526020810180516001600160e01b03908116630217a4b760e41b179091526000916104cd918991906103fc16565b9150508015806104e357506001600160a01b0381115b156104f357600092505050610565565b806001600160a01b03808216908916148061051f5750846001600160a01b0316816001600160a01b0316145b156105305760009350505050610565565b61053b86898361056d565b801561054d575061054d86828761056d565b1561055d57829350505050610565565b600093505050505b949350505050565b6040516001600160a01b03808416602483015282166044820152600090819060640160408051601f198184030181529181526020820180516001600160e01b031663298d501f60e11b1781528151835193945092839189620186a0fa600060208301511160403d101582161693505050509392505050565b80516001600160a01b03811681146105fc57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561063f5761063f610601565b604052919050565b60006001600160401b0382111561066057610660610601565b5060051b60200190565b600082601f83011261067b57600080fd5b815161068e61068982610647565b610617565b8082825260208201915060208360051b8601019250858311156106b057600080fd5b602085015b838110156106e35780516001600160e01b0319811681146106d557600080fd5b8352602092830192016106b5565b5095945050505050565b600080600080600060a0868803121561070557600080fd5b61070e866105e5565b945061071c602087016105e5565b60408701519094506001600160401b0381111561073857600080fd5b8601601f8101881361074957600080fd5b805161075761068982610647565b8082825260208201915060208360051b85010192508a83111561077957600080fd5b6020840193505b828410156107a257610791846105e5565b825260209384019390910190610780565b95506107b492505050606087016105e5565b60808701519092506001600160401b038111156107d057600080fd5b6107dc8882890161066a565b9150509295509295909350565b634e487b7160e01b600052603260045260246000fd5b600081518084526020840193506020830160005b8281101561083a5781516001600160a01b0316865260209586019590910190600101610813565b5093949350505050565b6001600160a01b0386168152602080820186905260a060408301819052855190830181905260009186019060c0840190835b81811015610894578351835260209384019390920191600101610876565b5050851515606085015283810360808501526108b081866107ff565b999850505050505050505056fe'
@@ -0,0 +1,401 @@
1
+ import type { Address } from 'abitype'
2
+ import { RpcResponse } from 'ox'
3
+ import type { Transaction as core_Transaction } from 'ox/tempo'
4
+ import { tempo } from '../../../chains/index.js'
5
+ import { type Client, create as createClient } from '../../../core/Client.js'
6
+ import type * as Relay from '../../Relay.js'
7
+ import * as Transaction from 'ox/tempo/TxEnvelopeTempo'
8
+ import * as Store from './cache.js'
9
+ import * as Deadline from './deadline.js'
10
+ import { formatError, isExecutionError } from './error.js'
11
+ import { getDefaultTokens } from './feeToken.js'
12
+ import * as Utils from './utils.js'
13
+ import {
14
+ extractCalls,
15
+ getVirtualAddressTargets,
16
+ resolveVirtualAddresses,
17
+ } from './virtualAddress.js'
18
+
19
+ export type Result = {
20
+ tx: Record<string, unknown>
21
+ capabilities?: Record<string, unknown> | undefined
22
+ sponsor?: unknown
23
+ }
24
+
25
+ /** Compose middleware and execute post-fill hooks once for the selected result. */
26
+ export function compose(
27
+ downstream: Relay.handleRequest.Handler,
28
+ options: Relay.handleRequest.Options = {},
29
+ resolveClient?: ((chainId: number) => { chain: { id: number } }) | undefined,
30
+ ): Relay.handleRequest.Handler {
31
+ const options_ = options
32
+ const plugins = options.plugins ?? []
33
+ if (plugins.length === 0) return downstream
34
+ if (plugins.filter((plugin) => plugin.signTransaction).length > 1)
35
+ throw new Error('Only one relay transaction signer may be configured.')
36
+
37
+ const handle: Relay.handleRequest.Handler = async (
38
+ request,
39
+ requestOptions = {},
40
+ ) => {
41
+ const stores = new Map<Store.Store, Store.Store>()
42
+ const tokens = new Map<number, Promise<readonly Address[]>>()
43
+ const scoped = (store: Store.Store | undefined) => {
44
+ if (!store) return undefined
45
+ let result = stores.get(store)
46
+ if (!result) {
47
+ result = Store.scoped(store)!
48
+ stores.set(store, result)
49
+ }
50
+ return result
51
+ }
52
+
53
+ const execute = async (
54
+ start: number,
55
+ request: Relay.handleRequest.Request,
56
+ options: Relay.handleRequest.RequestOptions,
57
+ ) => {
58
+ const state = {
59
+ request,
60
+ options: { ...options },
61
+ result: undefined as unknown,
62
+ }
63
+ const contexts = new Map<number, Relay.Plugin.Context>()
64
+ const contextAt = (index: number): Relay.Plugin.Context => {
65
+ const existing = contexts.get(index)
66
+ if (existing) return existing
67
+ const clients = new Map<number, Client>()
68
+ const chainId = (id = state.options.chainId) => {
69
+ if (id === undefined || !Number.isSafeInteger(id) || id <= 0)
70
+ throw new RpcResponse.InvalidParamsError({
71
+ message:
72
+ 'A chain ID is required to resolve the downstream client.',
73
+ })
74
+ return id
75
+ }
76
+ const context: Relay.Plugin.Context = {
77
+ get request() {
78
+ return state.request
79
+ },
80
+ set request(value) {
81
+ state.request = value
82
+ },
83
+ get result() {
84
+ return state.result
85
+ },
86
+ set result(value) {
87
+ state.result = value
88
+ },
89
+ get options() {
90
+ return state.options
91
+ },
92
+ set options(value) {
93
+ state.options = value
94
+ },
95
+ get client() {
96
+ return context.getClient()
97
+ },
98
+ getClient(id_) {
99
+ const id = chainId(id_)
100
+ let client = clients.get(id)
101
+ if (client) return client
102
+ const upstream = resolveClient?.(id)
103
+ if (upstream?.chain && upstream.chain.id !== id)
104
+ throw new RpcResponse.InvalidParamsError({
105
+ message: 'Conflicting chain ids.',
106
+ })
107
+ client = createClient({
108
+ chain: { ...tempo, ...upstream?.chain, id },
109
+ batch: { multicall: { deployless: true } },
110
+ transport: {
111
+ key: 'relay-forward',
112
+ name: 'Relay Forwarding',
113
+ type: 'relay-forward',
114
+ setup: () => ({
115
+ retryCount: 0,
116
+ request: (async (
117
+ request: Relay.handleRequest.Request,
118
+ options?: Relay.handleRequest.RequestOptions,
119
+ ) => {
120
+ const child = await execute(
121
+ index + 1,
122
+ request as Relay.handleRequest.Request,
123
+ {
124
+ ...state.options,
125
+ ...options,
126
+ chainId: id,
127
+ },
128
+ )
129
+ return child.state.result
130
+ }) as Client['request'],
131
+ }),
132
+ },
133
+ })
134
+ clients.set(id, client)
135
+ return client
136
+ },
137
+ async resolveTokens(id_) {
138
+ const id = chainId(id_)
139
+ let result = tokens.get(id)
140
+ if (!result) {
141
+ result = Promise.resolve().then(() =>
142
+ (options_.resolveTokens ?? getDefaultTokens)(id),
143
+ )
144
+ tokens.set(id, result)
145
+ }
146
+ return result
147
+ },
148
+ getStore: scoped,
149
+ }
150
+ contexts.set(index, context)
151
+ return context
152
+ }
153
+ const dispatch = async (index: number): Promise<void> => {
154
+ if (index === plugins.length) {
155
+ const parameters = state.request.params?.[0] as
156
+ | Record<string, unknown>
157
+ | undefined
158
+ // The node must estimate unsigned sponsorship independently of the sender's fee-token balance.
159
+ const request =
160
+ state.request.method === 'eth_fillTransaction' &&
161
+ parameters?.feePayer === true &&
162
+ !parameters.feePayerSignature
163
+ ? {
164
+ ...state.request,
165
+ params: [{ ...parameters, feeToken: undefined }],
166
+ }
167
+ : state.request
168
+ requestOptions.signal?.throwIfAborted()
169
+ state.result = await downstream(request, {
170
+ ...state.options,
171
+ ...(isFill ? { signal: requestOptions.signal } : {}),
172
+ })
173
+ return
174
+ }
175
+ const plugin = plugins[index]!
176
+ const context = contextAt(index)
177
+ if (!plugin.handleRequest) return dispatch(index + 1)
178
+ let called = false
179
+ const result = await plugin.handleRequest(context, async () => {
180
+ if (called)
181
+ throw new Error(
182
+ 'next() may only be called once per middleware invocation.',
183
+ )
184
+ called = true
185
+ await dispatch(index + 1)
186
+ })
187
+ if (result !== undefined) state.result = result
188
+ }
189
+ await dispatch(start)
190
+ return { state, contextAt }
191
+ }
192
+
193
+ const isFill = request.method === 'eth_fillTransaction'
194
+ const isRaw = [
195
+ 'eth_signRawTransaction',
196
+ 'eth_sendRawTransaction',
197
+ 'eth_sendRawTransactionSync',
198
+ ].includes(request.method)
199
+ const parameters = request.params?.[0] as Record<string, unknown>
200
+ let root: Awaited<ReturnType<typeof execute>> | undefined
201
+ try {
202
+ requestOptions.signal?.throwIfAborted()
203
+ if (
204
+ isFill &&
205
+ (!parameters ||
206
+ typeof parameters !== 'object' ||
207
+ Array.isArray(parameters))
208
+ )
209
+ throw new RpcResponse.InvalidParamsError({
210
+ message: 'Expected a transaction object.',
211
+ })
212
+ if (isFill) Utils.normalizeFillTransactionRequest(parameters)
213
+ const bodyChainId = (() => {
214
+ if (isRaw && Utils.isSerializedTempoTransaction(request.params?.[0])) {
215
+ try {
216
+ return Transaction.deserialize(request.params[0]).chainId
217
+ } catch {
218
+ throw new RpcResponse.InvalidParamsError({
219
+ message: 'Invalid serialized Tempo transaction.',
220
+ })
221
+ }
222
+ }
223
+ if (!isFill || parameters.chainId === undefined) return undefined
224
+ const id = Utils.resolveChainId(parameters.chainId)
225
+ if (id === undefined || !Number.isSafeInteger(id) || id <= 0)
226
+ throw new RpcResponse.InvalidParamsError({
227
+ message: 'Invalid transaction chain ID.',
228
+ })
229
+ return id
230
+ })()
231
+ if (
232
+ bodyChainId !== undefined &&
233
+ requestOptions.chainId !== undefined &&
234
+ bodyChainId !== requestOptions.chainId
235
+ )
236
+ throw new RpcResponse.InvalidParamsError({
237
+ message: 'Conflicting chain ids.',
238
+ })
239
+ root = await execute(0, request, {
240
+ ...requestOptions,
241
+ ...((requestOptions.chainId ?? bodyChainId) !== undefined
242
+ ? { chainId: requestOptions.chainId ?? bodyChainId }
243
+ : {}),
244
+ })
245
+ if (!isFill) return root.state.result
246
+ const filled = root.state.result as Result
247
+ if (filled.capabilities?.error) return filled
248
+ const transaction = Utils.normalizeTempoTransaction(
249
+ Utils.mergeCallsFromRequest(
250
+ filled.tx,
251
+ Utils.normalizeFillTransactionRequest(parameters),
252
+ ),
253
+ )
254
+ const final: Result = {
255
+ ...filled,
256
+ tx: Utils.formatTempoTransaction(
257
+ transaction as core_Transaction.Transaction,
258
+ ),
259
+ }
260
+ const snapshot = freeze(structuredClone(final))
261
+ const targets = getVirtualAddressTargets(extractCalls(transaction))
262
+ const resolved = final.capabilities?.virtualAddresses as
263
+ | Record<Address, Address | null>
264
+ | undefined
265
+ const [patches, signatures, virtualAddresses] = await Promise.all([
266
+ Promise.all(
267
+ plugins.map((plugin, index) =>
268
+ plugin.afterFill?.(snapshot, root!.contextAt(index)),
269
+ ),
270
+ ),
271
+ Promise.all(
272
+ plugins.map((plugin, index) =>
273
+ plugin.signTransaction?.(snapshot, root!.contextAt(index)),
274
+ ),
275
+ ),
276
+ targets.length > 0 &&
277
+ !targets.every((target) => resolved && target in resolved)
278
+ ? resolveVirtualAddresses(root.contextAt(-1).client, {
279
+ calls: extractCalls(transaction),
280
+ }).catch(() => {
281
+ requestOptions.signal?.throwIfAborted()
282
+ return undefined
283
+ })
284
+ : undefined,
285
+ ])
286
+ requestOptions.signal?.throwIfAborted()
287
+ const capabilities = { ...final.capabilities }
288
+ const keys = new Set<string>()
289
+ for (const patch of patches) {
290
+ for (const [key, value] of Object.entries(patch?.capabilities ?? {})) {
291
+ if (keys.has(key))
292
+ throw new Error(`Conflicting relay capability: ${key}.`)
293
+ keys.add(key)
294
+ capabilities[key] = value
295
+ }
296
+ }
297
+ const signature = signatures.find((signature) => signature !== undefined)
298
+ if (signature) {
299
+ delete final.tx.signature
300
+ final.tx.feePayerSignature = signature
301
+ }
302
+ const sponsor = capabilities.sponsor ?? final.sponsor
303
+ return {
304
+ ...final,
305
+ capabilities: {
306
+ sponsored: !!sponsor,
307
+ ...capabilities,
308
+ ...(virtualAddresses ? { virtualAddresses } : {}),
309
+ },
310
+ }
311
+ } catch (error) {
312
+ requestOptions.signal?.throwIfAborted()
313
+ if (
314
+ isFill &&
315
+ isExecutionError(error) &&
316
+ (parameters.capabilities as Record<string, unknown> | undefined)
317
+ ?.errors === true
318
+ ) {
319
+ // Resolve errors through a downstream-only client; never restart the plugin pipeline.
320
+ const id =
321
+ requestOptions.chainId ?? Utils.resolveChainId(parameters.chainId)
322
+ const client = createClient({
323
+ chain: { ...tempo, id: id ?? tempo.id },
324
+ transport: {
325
+ key: 'relay-error',
326
+ name: 'Relay Error Enrichment',
327
+ type: 'relay-error',
328
+ setup: () => ({
329
+ request: ((
330
+ request: Relay.handleRequest.Request,
331
+ options?: Relay.handleRequest.RequestOptions,
332
+ ) =>
333
+ downstream(request, {
334
+ ...requestOptions,
335
+ ...options,
336
+ chainId: id,
337
+ signal: requestOptions.signal,
338
+ })) as Client['request'],
339
+ }),
340
+ },
341
+ })
342
+ return formatError(error, parameters, client)
343
+ }
344
+ if (!isFill && !isRaw) throw error
345
+ throw Utils.toRpcError(error)
346
+ }
347
+ }
348
+ if (
349
+ options.timeout !== undefined &&
350
+ (!Number.isSafeInteger(options.timeout) || options.timeout <= 0)
351
+ )
352
+ throw new RpcResponse.InvalidParamsError({
353
+ message: 'Expected a positive integer for timeout.',
354
+ })
355
+ if (options.timeout !== undefined && options.timeout > 2_147_483_647)
356
+ throw new RpcResponse.InvalidParamsError({
357
+ message: 'The fill timeout cannot exceed 2147483647 milliseconds.',
358
+ })
359
+ return (request, requestOptions = {}) =>
360
+ request.method === 'eth_fillTransaction'
361
+ ? Deadline.run(handle, request, requestOptions, options)
362
+ : handle(request, requestOptions)
363
+ }
364
+
365
+ /** Prevent hooks from mutating the transaction another hook signs. */
366
+ function freeze<value>(value: value): value {
367
+ if (value && typeof value === 'object') {
368
+ for (const child of Object.values(value)) freeze(child)
369
+ Object.freeze(value)
370
+ }
371
+ return value
372
+ }
373
+
374
+ /** Normalize a downstream fill without discarding capabilities added by other plugins. */
375
+ export async function fill(
376
+ client: Client,
377
+ transaction: Record<string, unknown>,
378
+ options: Relay.handleRequest.RequestOptions = {},
379
+ ): Promise<Result> {
380
+ const formatted = Utils.formatFillTransactionRequest(client, transaction)
381
+ const result = (await client.request(
382
+ {
383
+ method: 'eth_fillTransaction',
384
+ params: [formatted as never],
385
+ },
386
+ options,
387
+ )) as unknown as Result
388
+
389
+ const error = result.capabilities?.error as
390
+ | { message?: string; errorName?: string; data?: `0x${string}` }
391
+ | undefined
392
+ if (error) {
393
+ const cause = new Error(
394
+ error.message ?? error.errorName ?? 'UpstreamRevert',
395
+ )
396
+ Object.assign(cause, { name: 'UpstreamRevertError', data: error.data })
397
+ throw cause
398
+ }
399
+
400
+ return { ...result, tx: Utils.mergeCallsFromRequest(result.tx, transaction) }
401
+ }