viem 2.55.16 → 2.55.17

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 (60) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/_cjs/actions/public/fillTransaction.js +45 -35
  3. package/_cjs/actions/public/fillTransaction.js.map +1 -1
  4. package/_cjs/actions/wallet/prepareTransactionRequest.js +4 -0
  5. package/_cjs/actions/wallet/prepareTransactionRequest.js.map +1 -1
  6. package/_cjs/chains/definitions/hausTestnet.js +27 -0
  7. package/_cjs/chains/definitions/hausTestnet.js.map +1 -0
  8. package/_cjs/chains/index.js +12 -10
  9. package/_cjs/chains/index.js.map +1 -1
  10. package/_cjs/errors/transaction.js +13 -1
  11. package/_cjs/errors/transaction.js.map +1 -1
  12. package/_cjs/errors/version.js +1 -1
  13. package/_cjs/index.js +5 -4
  14. package/_cjs/index.js.map +1 -1
  15. package/_cjs/op-stack/abis.js +14 -0
  16. package/_cjs/op-stack/abis.js.map +1 -1
  17. package/_cjs/op-stack/actions/estimateOperatorFee.js +26 -15
  18. package/_cjs/op-stack/actions/estimateOperatorFee.js.map +1 -1
  19. package/_esm/actions/public/fillTransaction.js +49 -36
  20. package/_esm/actions/public/fillTransaction.js.map +1 -1
  21. package/_esm/actions/wallet/prepareTransactionRequest.js +4 -0
  22. package/_esm/actions/wallet/prepareTransactionRequest.js.map +1 -1
  23. package/_esm/chains/definitions/hausTestnet.js +24 -0
  24. package/_esm/chains/definitions/hausTestnet.js.map +1 -0
  25. package/_esm/chains/index.js +1 -0
  26. package/_esm/chains/index.js.map +1 -1
  27. package/_esm/errors/transaction.js +11 -0
  28. package/_esm/errors/transaction.js.map +1 -1
  29. package/_esm/errors/version.js +1 -1
  30. package/_esm/index.js +1 -1
  31. package/_esm/index.js.map +1 -1
  32. package/_esm/op-stack/abis.js +14 -0
  33. package/_esm/op-stack/abis.js.map +1 -1
  34. package/_esm/op-stack/actions/estimateOperatorFee.js +28 -25
  35. package/_esm/op-stack/actions/estimateOperatorFee.js.map +1 -1
  36. package/_types/actions/public/fillTransaction.d.ts.map +1 -1
  37. package/_types/actions/wallet/prepareTransactionRequest.d.ts.map +1 -1
  38. package/_types/chains/definitions/hausTestnet.d.ts +52 -0
  39. package/_types/chains/definitions/hausTestnet.d.ts.map +1 -0
  40. package/_types/chains/index.d.ts +1 -0
  41. package/_types/chains/index.d.ts.map +1 -1
  42. package/_types/errors/transaction.d.ts +9 -0
  43. package/_types/errors/transaction.d.ts.map +1 -1
  44. package/_types/errors/version.d.ts +1 -1
  45. package/_types/index.d.ts +1 -1
  46. package/_types/index.d.ts.map +1 -1
  47. package/_types/op-stack/abis.d.ts +24 -0
  48. package/_types/op-stack/abis.d.ts.map +1 -1
  49. package/_types/op-stack/actions/estimateOperatorFee.d.ts +6 -2
  50. package/_types/op-stack/actions/estimateOperatorFee.d.ts.map +1 -1
  51. package/actions/public/fillTransaction.ts +53 -35
  52. package/actions/wallet/prepareTransactionRequest.ts +6 -0
  53. package/chains/definitions/hausTestnet.ts +24 -0
  54. package/chains/index.ts +1 -0
  55. package/errors/transaction.ts +21 -0
  56. package/errors/version.ts +1 -1
  57. package/index.ts +2 -0
  58. package/op-stack/abis.ts +14 -0
  59. package/op-stack/actions/estimateOperatorFee.ts +50 -36
  60. package/package.json +1 -1
@@ -4,6 +4,7 @@ import type { Client } from '../../clients/createClient.js'
4
4
  import type { Transport } from '../../clients/transports/createTransport.js'
5
5
  import type { BaseError } from '../../errors/base.js'
6
6
  import { BaseFeeScalarError } from '../../errors/fee.js'
7
+ import { FeePayerNonceMismatchError } from '../../errors/transaction.js'
7
8
  import type { ErrorType } from '../../errors/utils.js'
8
9
  import type { Account, GetAccountParameter } from '../../types/account.js'
9
10
  import type { ExtractCapabilities } from '../../types/capabilities.js'
@@ -194,44 +195,61 @@ export async function fillTransaction<
194
195
  // Rewrite fields.
195
196
  transaction.data = transaction.input
196
197
 
197
- // Preference supplied fees (some nodes do not take these preferences).
198
- if (transaction.gas) transaction.gas = parameters.gas ?? transaction.gas
199
- if (transaction.gasPrice)
200
- transaction.gasPrice = parameters.gasPrice ?? transaction.gasPrice
201
- if (transaction.maxFeePerBlobGas)
202
- transaction.maxFeePerBlobGas =
203
- parameters.maxFeePerBlobGas ?? transaction.maxFeePerBlobGas
204
- if (transaction.maxFeePerGas)
205
- transaction.maxFeePerGas =
206
- parameters.maxFeePerGas ?? transaction.maxFeePerGas
207
- if (transaction.maxPriorityFeePerGas)
208
- transaction.maxPriorityFeePerGas =
209
- parameters.maxPriorityFeePerGas ?? transaction.maxPriorityFeePerGas
210
- if (typeof transaction.nonce !== 'undefined')
211
- transaction.nonce = parameters.nonce ?? transaction.nonce
198
+ const hasFeePayerSignature =
199
+ typeof transaction.feePayerSignature !== 'undefined' &&
200
+ transaction.feePayerSignature !== null
212
201
 
213
- // Build fee multiplier function.
214
- const feeMultiplier = await (async () => {
215
- if (typeof chain?.fees?.baseFeeMultiplier === 'function') {
216
- const block = await getAction(client, getBlock, 'getBlock')({})
217
- return chain.fees.baseFeeMultiplier({
218
- block,
219
- client,
220
- request: parameters,
221
- } as ChainFeesFnParameters)
222
- }
223
- return chain?.fees?.baseFeeMultiplier ?? 1.2
224
- })()
225
- if (feeMultiplier < 1) throw new BaseFeeScalarError()
202
+ if (
203
+ hasFeePayerSignature &&
204
+ typeof nonce !== 'undefined' &&
205
+ transaction.nonce !== nonce
206
+ )
207
+ throw new FeePayerNonceMismatchError({
208
+ filledNonce: transaction.nonce,
209
+ requestedNonce: nonce,
210
+ })
211
+
212
+ // A fee payer signature commits to the filled transaction, so preserve
213
+ // its signed fields regardless of who produced the signature.
214
+ // https://tips.sh/0001#fee-payer-signature
215
+ if (!hasFeePayerSignature) {
216
+ // Preference supplied fees (some nodes do not take these preferences).
217
+ if (transaction.gas) transaction.gas = parameters.gas ?? transaction.gas
218
+ if (transaction.gasPrice)
219
+ transaction.gasPrice = parameters.gasPrice ?? transaction.gasPrice
220
+ if (transaction.maxFeePerBlobGas)
221
+ transaction.maxFeePerBlobGas =
222
+ parameters.maxFeePerBlobGas ?? transaction.maxFeePerBlobGas
223
+ if (transaction.maxFeePerGas)
224
+ transaction.maxFeePerGas =
225
+ parameters.maxFeePerGas ?? transaction.maxFeePerGas
226
+ if (transaction.maxPriorityFeePerGas)
227
+ transaction.maxPriorityFeePerGas =
228
+ parameters.maxPriorityFeePerGas ?? transaction.maxPriorityFeePerGas
229
+ if (typeof transaction.nonce !== 'undefined')
230
+ transaction.nonce = parameters.nonce ?? transaction.nonce
231
+
232
+ // Build fee multiplier function.
233
+ const feeMultiplier = await (async () => {
234
+ if (typeof chain?.fees?.baseFeeMultiplier === 'function') {
235
+ const block = await getAction(client, getBlock, 'getBlock')({})
236
+ return chain.fees.baseFeeMultiplier({
237
+ block,
238
+ client,
239
+ request: parameters,
240
+ } as ChainFeesFnParameters)
241
+ }
242
+ return chain?.fees?.baseFeeMultiplier ?? 1.2
243
+ })()
244
+ if (feeMultiplier < 1) throw new BaseFeeScalarError()
226
245
 
227
- const decimals = feeMultiplier.toString().split('.')[1]?.length ?? 0
228
- const denominator = 10 ** decimals
229
- const multiplyFee = (base: bigint) =>
230
- (base * BigInt(Math.round(feeMultiplier * denominator))) /
231
- BigInt(denominator)
246
+ const decimals = feeMultiplier.toString().split('.')[1]?.length ?? 0
247
+ const denominator = 10 ** decimals
248
+ const multiplyFee = (base: bigint) =>
249
+ (base * BigInt(Math.round(feeMultiplier * denominator))) /
250
+ BigInt(denominator)
232
251
 
233
- // Apply fee multiplier.
234
- if (!transaction.feePayerSignature) {
252
+ // Apply fee multiplier.
235
253
  if (transaction.maxFeePerGas && !parameters.maxFeePerGas)
236
254
  transaction.maxFeePerGas = multiplyFee(transaction.maxFeePerGas)
237
255
  if (transaction.gasPrice && !parameters.gasPrice)
@@ -29,6 +29,7 @@ import {
29
29
  Eip1559FeesNotSupportedError,
30
30
  MaxFeePerGasTooLowError,
31
31
  } from '../../errors/fee.js'
32
+ import { FeePayerNonceMismatchError } from '../../errors/transaction.js'
32
33
  import type { DeriveAccount, GetAccountParameter } from '../../types/account.js'
33
34
  import type { Block } from '../../types/block.js'
34
35
  import type { ExtractCapabilities } from '../../types/capabilities.js'
@@ -509,6 +510,11 @@ export async function prepareTransactionRequest<
509
510
 
510
511
  if (error.name !== 'TransactionExecutionError') return request
511
512
 
513
+ const nonceMismatch = error.walk?.(
514
+ (error) => error instanceof FeePayerNonceMismatchError,
515
+ )
516
+ if (nonceMismatch) throw e
517
+
512
518
  const executionReverted = error.walk?.((e) => {
513
519
  const error = e as BaseError
514
520
  return error.name === 'ExecutionRevertedError'
@@ -0,0 +1,24 @@
1
+ import { defineChain } from '../../utils/chain/defineChain.js'
2
+
3
+ export const hausTestnet = /*#__PURE__*/ defineChain({
4
+ id: 2_443,
5
+ name: 'Haus Chain Testnet',
6
+ nativeCurrency: {
7
+ decimals: 18,
8
+ name: 'Haus',
9
+ symbol: 'HAUS',
10
+ },
11
+ rpcUrls: {
12
+ default: {
13
+ http: ['https://rpc-testnet.hausserver.xyz'],
14
+ },
15
+ },
16
+ blockExplorers: {
17
+ default: {
18
+ name: 'Haus Chain Testnet Explorer',
19
+ url: 'https://explorer-testnet.hausserver.xyz',
20
+ apiUrl: 'https://explorer-testnet.hausserver.xyz/api',
21
+ },
22
+ },
23
+ testnet: true,
24
+ })
package/chains/index.ts CHANGED
@@ -267,6 +267,7 @@ export { hardhat } from './definitions/hardhat.js'
267
267
  export { harmonyOne } from './definitions/harmonyOne.js'
268
268
  export { hashkey } from './definitions/hashKeyChain.js'
269
269
  export { hashkeyTestnet } from './definitions/hashkeyChainTestnet.js'
270
+ export { hausTestnet } from './definitions/hausTestnet.js'
270
271
  export { haustTestnet } from './definitions/haustTestnet.js'
271
272
  export { hedera } from './definitions/hedera.js'
272
273
  export { hederaPreviewnet } from './definitions/hederaPreviewnet.js'
@@ -42,6 +42,27 @@ export class FeeConflictError extends BaseError {
42
42
  }
43
43
  }
44
44
 
45
+ export type FeePayerNonceMismatchErrorType = FeePayerNonceMismatchError & {
46
+ name: 'FeePayerNonceMismatchError'
47
+ }
48
+ export class FeePayerNonceMismatchError extends BaseError {
49
+ constructor({
50
+ filledNonce,
51
+ requestedNonce,
52
+ }: {
53
+ filledNonce: number | undefined
54
+ requestedNonce: number
55
+ }) {
56
+ super('The filled transaction nonce does not match the requested nonce.', {
57
+ metaMessages: [
58
+ `Requested Nonce: ${requestedNonce}`,
59
+ `Filled Nonce: ${filledNonce}`,
60
+ ],
61
+ name: 'FeePayerNonceMismatchError',
62
+ })
63
+ }
64
+ }
65
+
45
66
  export type InvalidLegacyVErrorType = InvalidLegacyVError & {
46
67
  name: 'InvalidLegacyVError'
47
68
  }
package/errors/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '2.55.16'
1
+ export const version = '2.55.17'
package/index.ts CHANGED
@@ -1036,6 +1036,8 @@ export {
1036
1036
  export {
1037
1037
  FeeConflictError,
1038
1038
  type FeeConflictErrorType,
1039
+ FeePayerNonceMismatchError,
1040
+ type FeePayerNonceMismatchErrorType,
1039
1041
  InvalidLegacyVError,
1040
1042
  type InvalidLegacyVErrorType,
1041
1043
  InvalidSerializableTransactionError,
package/op-stack/abis.ts CHANGED
@@ -46,6 +46,20 @@ export const gasPriceOracleAbi = [
46
46
  stateMutability: 'view',
47
47
  type: 'function',
48
48
  },
49
+ {
50
+ inputs: [{ internalType: 'uint256', name: '_gasUsed', type: 'uint256' }],
51
+ name: 'getOperatorFee',
52
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
53
+ stateMutability: 'view',
54
+ type: 'function',
55
+ },
56
+ {
57
+ inputs: [],
58
+ name: 'isJovian',
59
+ outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
60
+ stateMutability: 'view',
61
+ type: 'function',
62
+ },
49
63
  {
50
64
  inputs: [],
51
65
  name: 'l1BaseFee',
@@ -18,7 +18,7 @@ import type { TransactionRequestEIP1559 } from '../../types/transaction.js'
18
18
  import type { RequestErrorType } from '../../utils/buildRequest.js'
19
19
  import { getChainContractAddress } from '../../utils/chain/getChainContractAddress.js'
20
20
  import type { HexToNumberErrorType } from '../../utils/encoding/fromHex.js'
21
- import { l1BlockAbi } from '../abis.js'
21
+ import { gasPriceOracleAbi, l1BlockAbi } from '../abis.js'
22
22
  import { contracts } from '../contracts.js'
23
23
 
24
24
  export type EstimateOperatorFeeParameters<
@@ -28,7 +28,12 @@ export type EstimateOperatorFeeParameters<
28
28
  > = Omit<TransactionRequestEIP1559, 'from'> &
29
29
  GetAccountParameter<TAccount> &
30
30
  GetChainParameter<TChain, TChainOverride> & {
31
- /** L1 block attributes contract address. */
31
+ /** Gas price oracle address. */
32
+ gasPriceOracleAddress?: Address | undefined
33
+ /**
34
+ * L1 block attributes contract address.
35
+ * @deprecated Use `gasPriceOracleAddress` instead.
36
+ */
32
37
  l1BlockAddress?: Address | undefined
33
38
  }
34
39
 
@@ -46,7 +51,6 @@ export type EstimateOperatorFeeErrorType =
46
51
  *
47
52
  * Operator fees are part of the Isthmus upgrade and allow OP Stack operators
48
53
  * to recover costs related to Alt-DA, ZK proving, or custom gas tokens.
49
- * Returns 0 for pre-Isthmus chains or when operator fee functions don't exist.
50
54
  *
51
55
  * @param client - Client to use
52
56
  * @param parameters - {@link EstimateOperatorFeeParameters}
@@ -75,45 +79,55 @@ export async function estimateOperatorFee<
75
79
  client: Client<Transport, TChain, TAccount>,
76
80
  args: EstimateOperatorFeeParameters<TChain, TAccount, TChainOverride>,
77
81
  ): Promise<EstimateOperatorFeeReturnType> {
78
- const { chain = client.chain, l1BlockAddress: l1BlockAddress_ } = args
82
+ const {
83
+ chain = client.chain,
84
+ gasPriceOracleAddress: gasPriceOracleAddress_,
85
+ l1BlockAddress,
86
+ } = args
79
87
 
80
- const l1BlockAddress = (() => {
81
- if (l1BlockAddress_) return l1BlockAddress_
82
- if (chain?.contracts?.l1Block)
88
+ const gasPriceOracleAddress = (() => {
89
+ if (gasPriceOracleAddress_) return gasPriceOracleAddress_
90
+ if (chain)
83
91
  return getChainContractAddress({
84
92
  chain,
85
- contract: 'l1Block',
93
+ contract: 'gasPriceOracle',
86
94
  })
87
- return contracts.l1Block.address
88
- })()
95
+ return contracts.gasPriceOracle.address
96
+ })() as Address
89
97
 
90
- // Try to get operator fee parameters. If any of these calls fail,
91
- // it means this is a pre-Isthmus chain and operator fees don't apply
92
- try {
93
- // Get operator fee parameters first to fail fast if not supported
94
- const [operatorFeeScalar, operatorFeeConstant] = await Promise.all([
95
- readContract(client, {
96
- abi: l1BlockAbi,
97
- address: l1BlockAddress,
98
- functionName: 'operatorFeeScalar',
99
- }),
100
- readContract(client, {
101
- abi: l1BlockAbi,
102
- address: l1BlockAddress,
103
- functionName: 'operatorFeeConstant',
104
- }),
105
- ])
98
+ const gasUsed = await estimateGas(client, args as EstimateGasParameters)
106
99
 
107
- // Estimate gas for the actual transaction
108
- const gasUsed = await estimateGas(client, args as EstimateGasParameters)
100
+ if (l1BlockAddress) {
101
+ // `isJovian` is unavailable on Isthmus Gas Price Oracle implementations.
102
+ const [operatorFeeScalar, operatorFeeConstant, isJovian] =
103
+ await Promise.all([
104
+ readContract(client, {
105
+ abi: l1BlockAbi,
106
+ address: l1BlockAddress,
107
+ functionName: 'operatorFeeScalar',
108
+ }),
109
+ readContract(client, {
110
+ abi: l1BlockAbi,
111
+ address: l1BlockAddress,
112
+ functionName: 'operatorFeeConstant',
113
+ }),
114
+ readContract(client, {
115
+ abi: gasPriceOracleAbi,
116
+ address: gasPriceOracleAddress,
117
+ functionName: 'isJovian',
118
+ }).catch(() => false),
119
+ ])
109
120
 
110
- // Calculate operator fee: saturatingAdd(saturatingMul(gasUsed, scalar) / 1e6, constant)
111
- // Using saturating arithmetic to prevent overflow
112
- const scaledFee = (gasUsed * BigInt(operatorFeeScalar)) / 1_000_000n
113
- return scaledFee + BigInt(operatorFeeConstant)
114
- } catch {
115
- // If any call fails, this is likely a pre-Isthmus chain or the contract
116
- // doesn't support these functions. Return 0 for operator fee.
117
- return 0n
121
+ const scalar = BigInt(operatorFeeScalar)
122
+ const constant = BigInt(operatorFeeConstant)
123
+ if (isJovian) return gasUsed * scalar * 100n + constant
124
+ return (gasUsed * scalar) / 1_000_000n + constant
118
125
  }
126
+
127
+ return readContract(client, {
128
+ abi: gasPriceOracleAbi,
129
+ address: gasPriceOracleAddress,
130
+ functionName: 'getOperatorFee',
131
+ args: [gasUsed],
132
+ })
119
133
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "viem",
3
3
  "description": "TypeScript Interface for Ethereum",
4
- "version": "2.55.16",
4
+ "version": "2.55.17",
5
5
  "main": "./_cjs/index.js",
6
6
  "module": "./_esm/index.js",
7
7
  "types": "./_types/index.d.ts",