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
@@ -1,5 +1,6 @@
1
1
  import { AbiEvent, Hex as Hex_ } from 'ox'
2
- import type { Errors, Hex } from 'ox'
2
+ import type { Address, Errors, Hex } from 'ox'
3
+ import type { OneOf } from '../../../core/internal/types.js'
3
4
 
4
5
  import type * as Account from '../../../core/Account.js'
5
6
  import type * as Chain from '../../../core/Chain.js'
@@ -25,7 +26,7 @@ import {
25
26
  } from '../../internal/utils.js'
26
27
 
27
28
  /**
28
- * Burns TIP-20 tokens from the caller's balance.
29
+ * Burns TIP-20 tokens from the caller's balance or a specified account with `BURN_AT_ROLE`.
29
30
  *
30
31
  * @example
31
32
  * ```ts
@@ -60,9 +61,13 @@ export namespace burn {
60
61
  export type Args = {
61
62
  /** Amount of tokens to burn, in base units or formatted decimal form. */
62
63
  amount: AmountInput
63
- /** Memo to include in the burn. */
64
- memo?: Hex.Hex | undefined
65
- } & TokenParameter
64
+ } & TokenParameter &
65
+ OneOf<
66
+ | { /** Memo to include in the burn. */ memo?: Hex.Hex | undefined }
67
+ | {
68
+ /** Address to burn from. Requires BURN_AT_ROLE. */ from: Address.Address
69
+ }
70
+ >
66
71
  export type Options = WriteParameters & Args
67
72
  export type ReturnType = write.ReturnType
68
73
  // TODO: exhaustive error type
@@ -85,7 +90,7 @@ export namespace burn {
85
90
  }
86
91
 
87
92
  /**
88
- * Defines a call to the `burn` or `burnWithMemo` function.
93
+ * Defines a call to the `burn`, `burnWithMemo`, or `burnAt` function.
89
94
  *
90
95
  * Can be passed to any action that accepts a contract call. The token is
91
96
  * selected by `token`; `amount.decimals` is inferred from the client's
@@ -98,9 +103,16 @@ export namespace burn {
98
103
  ...parameters: CallParameters<Args, Client.Client<chain>>
99
104
  ) {
100
105
  const [client, args] = resolveCallParameters(parameters)
101
- const { amount, memo, token } = args
106
+ const { amount, from, memo, token } = args
102
107
  const { address, decimals } = resolveToken(client, { token })
103
108
  const value = toBaseUnits(amount, decimals)
109
+ if (from)
110
+ return defineCall({
111
+ abi: Abis.tip20,
112
+ address,
113
+ functionName: 'burnAt',
114
+ args: [from, value],
115
+ })
104
116
  const callArgs = memo
105
117
  ? ({
106
118
  args: [value, Hex_.padLeft(memo, 32)],
@@ -169,10 +181,10 @@ export namespace burn {
169
181
  const logs extends readonly AbiEvent.extractLogs.Log[],
170
182
  >(logs: logs) {
171
183
  const [log] = AbiEvent.extractLogs(Abis.tip20, logs, {
172
- eventName: 'Burn',
184
+ eventName: ['Burn', 'BurnAt'],
173
185
  strict: true,
174
186
  })
175
- if (!log) throw new Error('`Burn` event not found.')
187
+ if (!log) throw new Error('`Burn` or `BurnAt` event not found.')
176
188
  return log
177
189
  }
178
190
  }
@@ -65,6 +65,8 @@ export namespace burnSync {
65
65
  export type Args = burn.Args
66
66
  export type Options = burn.Options & WriteSyncParameters
67
67
  export type ReturnType = {
68
+ /** BURN_AT_ROLE holder that performed the burn. */
69
+ burner?: Address.Address | undefined
68
70
  /** Address tokens were burned from. */
69
71
  from: Address.Address
70
72
  /** Burned amount, in base units. */
@@ -58,11 +58,10 @@ export type TransactionRequest = Omit<
58
58
  /** Capabilities to pass to `eth_fillTransaction`. */
59
59
  capabilities?: Capabilities.FillTransactionRequestCapabilities | undefined
60
60
  /**
61
- * Fee payer of the transaction (TIP-1 gas sponsorship). Pass `true` to defer the fee
62
- * token to an external fee payer (e.g. a relay), or a local Account to
63
- * co-sign the transaction as the fee payer.
61
+ * Fee payer for TIP-1 gas sponsorship. Pass `true` for relay sponsorship,
62
+ * an Account to co-sign locally, or an external sponsor URL allowed by the relay.
64
63
  */
65
- feePayer?: viem_Account.Account | boolean | undefined
64
+ feePayer?: viem_Account.Account | boolean | string | undefined
66
65
  /** Stored multisig operation to approve. */
67
66
  hash?: Hex.Hex | undefined
68
67
  /** Owner signing a multisig approval. */
@@ -90,7 +89,7 @@ export type TransactionRequestRpc = TransactionRequestTempo.Rpc
90
89
  */
91
90
  export type Envelope = TxEnvelopeTempo.TxEnvelopeTempo & {
92
91
  /** Fee payer of the transaction (TIP-1 gas sponsorship). */
93
- feePayer?: viem_Account.Account | boolean | undefined
92
+ feePayer?: viem_Account.Account | boolean | string | undefined
94
93
  /** Owner signing a multisig approval. */
95
94
  owner?: RootAccount | undefined
96
95
  /** Multisig account, config, and modeled approvals for gas estimation. */
@@ -293,6 +292,9 @@ export const chainConfig = {
293
292
  )
294
293
  Object.assign(request, {
295
294
  ...storedTransaction,
295
+ gas: storedTransaction.gas ?? 0n,
296
+ maxFeePerGas: storedTransaction.maxFeePerGas ?? 0n,
297
+ maxPriorityFeePerGas: storedTransaction.maxPriorityFeePerGas ?? 0n,
296
298
  nonce: Number(storedTransaction.nonce ?? 0n),
297
299
  account: request.account,
298
300
  from: operation.account,
@@ -436,7 +438,7 @@ export const chainConfig = {
436
438
  request.nonce = 0
437
439
  } else if (useExpiringNonce) {
438
440
  request.nonceKey = maxUint256
439
- request.nonce = 0
441
+ request.nonce ??= 0
440
442
  if (typeof request.validAfter === 'undefined')
441
443
  request.validAfter = randomValidAfter()
442
444
  if (typeof request.validBefore === 'undefined')
@@ -765,6 +767,7 @@ function encodeRequest(
765
767
  // fee payer commits to it when co-signing. (ox withholds `feeToken` only
766
768
  // for external sponsorship, where the fee payer picks the token.)
767
769
  if (typeof feePayer === 'object') rpc.feePayer = true
770
+ if (typeof feePayer === 'string') Object.assign(rpc, { feePayer })
768
771
 
769
772
  return rpc
770
773
  }
@@ -58,6 +58,9 @@ export { tempoActions, type Decorator as TempoActions } from './Decorator.js'
58
58
  /** Tempo errors. */
59
59
  export * from './errors.js'
60
60
 
61
+ /** Decodes Tempo precompile reverts into readable execution errors. */
62
+ export * as ExecutionError from './ExecutionError.js'
63
+
61
64
  /** Helpers producing unix timestamps for key expiries. */
62
65
  export * as Expiry from './Expiry.js'
63
66
 
@@ -70,6 +73,9 @@ export * as KeyAuthorizationManager from './KeyAuthorizationManager.js'
70
73
  /** P256 (secp256r1) key utilities. Re-exports `ox/P256`. */
71
74
  export * as P256 from './P256.js'
72
75
 
76
+ /** Composable relay request handlers. */
77
+ export * as Relay from './Relay.js'
78
+
73
79
  /** Call scopes restricting access keys to targets & selectors. */
74
80
  export * as Scopes from './Scopes.js'
75
81
 
@@ -80,7 +86,7 @@ export * as Selectors from './Selectors.js'
80
86
  export * as Store from './Store.js'
81
87
 
82
88
  /** Relay transport: routes fee sponsorship traffic to a fee payer service. */
83
- export { withMultisig, withRelay, type Relay } from './Transport.js'
89
+ export { withRelay } from './Transport.js'
84
90
 
85
91
  /** WebAuthn P256 credential creation & signing. */
86
92
  export * as WebAuthnP256 from './WebAuthnP256.js'
@@ -93,8 +99,5 @@ export * as WebCryptoP256 from './WebCryptoP256.js'
93
99
  // `viem/tempo` never imports `viem`. See `internal/inference.ts`.
94
100
  export type * from '../internal/inference.js'
95
101
 
96
- /** Coordinates native multisig approvals over JSON-RPC. */
97
- export * as Multisig from './Multisig.js'
98
-
99
102
  /** Tempo Zone chains. */
100
103
  export * as Zone from './Zone.js'
@@ -0,0 +1,82 @@
1
+ import { withTimeout } from '../../../core/internal/promise.js'
2
+ import type * as Store_ from '../../Store.js'
3
+
4
+ export type Store = Store_.Store
5
+
6
+ // Scope in-flight work to one request so Workers never share request-owned I/O.
7
+ const flights = new WeakMap<Store, Map<string, Promise<unknown>>>()
8
+ const prefix = 'viem:relay:cache:'
9
+
10
+ export function scoped(store: Store | undefined): Store | undefined {
11
+ if (!store) return undefined
12
+ return {
13
+ getItem: (key) => store.getItem(key),
14
+ setItem: (key, value) => store.setItem(key, value),
15
+ removeItem: (key) => store.removeItem(key),
16
+ }
17
+ }
18
+
19
+ export async function memoize<value>(
20
+ fetch: () => Promise<value>,
21
+ options: { key: string; store: Store; ttl: number },
22
+ ): Promise<value> {
23
+ const { store, ttl } = options
24
+ const key = `${prefix}${options.key}`
25
+ let active = true
26
+ const timeoutError = new Error('Relay cache operation timed out.')
27
+ return withTimeout(
28
+ async () => {
29
+ const raw = await store.getItem(key)
30
+ if (!active) throw timeoutError
31
+ if (raw) {
32
+ try {
33
+ const entry = JSON.parse(raw, (_key, item) => {
34
+ if (typeof item !== 'string' || !item.startsWith(prefix))
35
+ return item
36
+ if (item.startsWith(`${prefix}string:`))
37
+ return item.slice(`${prefix}string:`.length)
38
+ if (item.startsWith(`${prefix}bigint:`))
39
+ return BigInt(item.slice(`${prefix}bigint:`.length))
40
+ throw new TypeError('Invalid relay cache value.')
41
+ }) as { value: value; expiresAt: number }
42
+ if (entry.expiresAt > Date.now()) return entry.value
43
+ } catch {
44
+ // Refresh malformed or older entries.
45
+ }
46
+ }
47
+ let pending = flights.get(store)
48
+ if (!pending) {
49
+ pending = new Map()
50
+ flights.set(store, pending)
51
+ }
52
+ const existing = pending.get(key)
53
+ if (existing) return existing as Promise<value>
54
+ const result = (async () => {
55
+ const value = await fetch()
56
+ if (!active) throw timeoutError
57
+ if (value !== undefined)
58
+ await store.setItem(
59
+ key,
60
+ JSON.stringify(
61
+ { value, expiresAt: Date.now() + ttl },
62
+ (_key, item) => {
63
+ if (typeof item === 'bigint') return `${prefix}bigint:${item}`
64
+ if (typeof item === 'string' && item.startsWith(prefix))
65
+ return `${prefix}string:${item}`
66
+ return item
67
+ },
68
+ ),
69
+ )
70
+ return value
71
+ })().finally(() => pending.delete(key))
72
+ pending.set(key, result)
73
+ return result
74
+ },
75
+ {
76
+ timeout: 30_000,
77
+ errorInstance: timeoutError,
78
+ },
79
+ ).finally(() => {
80
+ active = false
81
+ })
82
+ }
@@ -0,0 +1,45 @@
1
+ import { RpcResponse } from 'ox'
2
+ import type * as Relay from '../../Relay.js'
3
+
4
+ /** Bounds the complete fill, including callbacks that do not observe abort signals. */
5
+ export async function run(
6
+ handler: Relay.handleRequest.Handler,
7
+ request: Relay.handleRequest.Request,
8
+ options: Relay.handleRequest.RequestOptions,
9
+ config: Relay.handleRequest.Options,
10
+ ) {
11
+ const controller = new AbortController()
12
+ const abort = () => controller.abort(options.signal?.reason)
13
+ options.signal?.addEventListener('abort', abort, { once: true })
14
+ if (options.signal?.aborted) abort()
15
+ const timeout = setTimeout(
16
+ () =>
17
+ controller.abort(
18
+ new RpcResponse.LimitExceededError({
19
+ message: 'Relay fill exceeded its deadline.',
20
+ }),
21
+ ),
22
+ config.timeout ?? 10_000,
23
+ )
24
+ const signal = controller.signal
25
+
26
+ let rejectAbort!: (reason: unknown) => void
27
+ const aborted = new Promise<never>((_, reject) => {
28
+ rejectAbort = reject
29
+ })
30
+ const onAbort = () => rejectAbort(signal.reason)
31
+ signal.addEventListener('abort', onAbort, { once: true })
32
+ try {
33
+ signal.throwIfAborted()
34
+ return await Promise.race([
35
+ handler(request, { ...options, signal }),
36
+ aborted,
37
+ ])
38
+ } finally {
39
+ clearTimeout(timeout)
40
+ options.signal?.removeEventListener('abort', abort)
41
+ signal.removeEventListener('abort', onAbort)
42
+ // Prevent timed-out hooks from dispatching additional I/O after the response completes.
43
+ controller.abort()
44
+ }
45
+ }
@@ -0,0 +1,129 @@
1
+ import type { Address } from 'abitype'
2
+ import { Hex } from 'ox'
3
+ import type { Client } from '../../../core/Client.js'
4
+ import { format as formatUnits } from 'ox/Value'
5
+ import * as ExecutionError from '../../ExecutionError.js'
6
+ import type * as Store from './cache.js'
7
+ import { resolveTokenMetadata } from './feeToken.js'
8
+ import { simulateAndParseDiffs } from './simulate.js'
9
+ import * as Utils from './utils.js'
10
+ import { extractCalls, resolveVirtualAddresses } from './virtualAddress.js'
11
+
12
+ /** Distinguishes execution reverts from transport and plugin failures. */
13
+ export function isExecutionError(error: unknown): error is Error {
14
+ if (!(error instanceof Error)) return false
15
+
16
+ const seen = new Set<unknown>()
17
+ const pending: unknown[] = [error]
18
+ while (pending.length) {
19
+ const current = pending.pop()
20
+ if (!current || typeof current !== 'object' || seen.has(current)) continue
21
+ seen.add(current)
22
+
23
+ const candidate = current as Record<string, unknown>
24
+ if (
25
+ candidate.code === 3 ||
26
+ candidate.name === 'UpstreamRevertError' ||
27
+ candidate.name === 'ExecutionRevertedError' ||
28
+ candidate.name === 'ContractFunctionRevertedError' ||
29
+ [candidate.message, candidate.details].some(
30
+ (message) =>
31
+ typeof message === 'string' && /^execution reverted\b/i.test(message),
32
+ )
33
+ )
34
+ return true
35
+
36
+ pending.push(candidate.cause, candidate.error)
37
+ }
38
+ return false
39
+ }
40
+
41
+ const zeroAddress = '0x0000000000000000000000000000000000000000'
42
+
43
+ export async function formatError(
44
+ error: Error,
45
+ parameters: Record<string, unknown>,
46
+ client: Client,
47
+ simulateStore?: Store.Store,
48
+ ) {
49
+ const revert = ExecutionError.from(error)
50
+
51
+ const stub = {
52
+ from: parameters.from,
53
+ to: parameters.to ?? null,
54
+ gas: '0x0',
55
+ nonce: '0x0',
56
+ value: '0x0',
57
+ maxFeePerGas: '0x0',
58
+ maxPriorityFeePerGas: '0x0',
59
+ }
60
+
61
+ if (revert.errorName === 'InsufficientBalance' && revert.args?.length === 3) {
62
+ const [available, required, token] = revert.args
63
+
64
+ const normalized = Utils.normalizeFillTransactionRequest(parameters)
65
+
66
+ // Simulate from zero address for optimistic balance diffs.
67
+ const optimisticCalls = normalized ? extractCalls(normalized) : undefined
68
+ const [{ balanceDiffs, tokenMetadata }, virtualAddresses] = optimisticCalls
69
+ ? await Promise.all([
70
+ simulateAndParseDiffs(client, {
71
+ account: zeroAddress,
72
+ calls: optimisticCalls,
73
+ store: simulateStore,
74
+ }),
75
+ resolveVirtualAddresses(client, { calls: optimisticCalls }).catch(
76
+ () => undefined,
77
+ ),
78
+ ])
79
+ : [{ balanceDiffs: undefined, tokenMetadata: undefined }, undefined]
80
+
81
+ // Re-key balance diffs from zero address to the real sender.
82
+ const senderDiffs =
83
+ parameters.from && balanceDiffs
84
+ ? { [parameters.from as Address]: balanceDiffs[zeroAddress] ?? [] }
85
+ : balanceDiffs
86
+
87
+ const metadata = await resolveTokenMetadata(client, {
88
+ token,
89
+ tokenMetadata,
90
+ store: simulateStore,
91
+ }).catch(() => undefined)
92
+ const deficit = required - available
93
+
94
+ return {
95
+ tx: stub,
96
+ capabilities: {
97
+ balanceDiffs: senderDiffs,
98
+ error: ExecutionError.serialize(revert),
99
+ insufficientFunds: metadata
100
+ ? {
101
+ amount: Hex.fromNumber(deficit) as `0x${string}`,
102
+ decimals: metadata.decimals,
103
+ formatted: formatUnits(deficit, metadata.decimals),
104
+ token,
105
+ symbol: metadata.symbol,
106
+ }
107
+ : undefined,
108
+ sponsored: false,
109
+ ...(virtualAddresses ? { virtualAddresses } : {}),
110
+ },
111
+ }
112
+ }
113
+
114
+ const normalized = Utils.normalizeFillTransactionRequest(parameters)
115
+ const virtualAddresses = normalized
116
+ ? await resolveVirtualAddresses(client, {
117
+ calls: extractCalls(normalized),
118
+ }).catch(() => undefined)
119
+ : undefined
120
+
121
+ return {
122
+ tx: stub,
123
+ capabilities: {
124
+ error: ExecutionError.serialize(revert),
125
+ sponsored: false,
126
+ ...(virtualAddresses ? { virtualAddresses } : {}),
127
+ },
128
+ }
129
+ }