@aztec/ethereum 0.0.0-test.1 → 0.0.1-commit.21caa21
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.
- package/dest/account.d.ts +2 -0
- package/dest/account.d.ts.map +1 -0
- package/dest/account.js +4 -0
- package/dest/chain.d.ts +1 -1
- package/dest/client.d.ts +6 -4
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +16 -2
- package/dest/config.d.ts +111 -17
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +462 -22
- package/dest/constants.d.ts +1 -1
- package/dest/contracts/empire_base.d.ts +24 -8
- package/dest/contracts/empire_base.d.ts.map +1 -1
- package/dest/contracts/empire_base.js +75 -2
- package/dest/contracts/empire_slashing_proposer.d.ts +66 -0
- package/dest/contracts/empire_slashing_proposer.d.ts.map +1 -0
- package/dest/contracts/empire_slashing_proposer.js +200 -0
- package/dest/contracts/errors.d.ts +7 -0
- package/dest/contracts/errors.d.ts.map +1 -0
- package/dest/contracts/errors.js +12 -0
- package/dest/contracts/fee_asset_handler.d.ts +19 -0
- package/dest/contracts/fee_asset_handler.d.ts.map +1 -0
- package/dest/contracts/fee_asset_handler.js +57 -0
- package/dest/contracts/fee_juice.d.ts +6 -7
- package/dest/contracts/fee_juice.d.ts.map +1 -1
- package/dest/contracts/fee_juice.js +27 -20
- package/dest/contracts/governance.d.ts +43 -32
- package/dest/contracts/governance.d.ts.map +1 -1
- package/dest/contracts/governance.js +87 -84
- package/dest/contracts/governance_proposer.d.ts +16 -13
- package/dest/contracts/governance_proposer.d.ts.map +1 -1
- package/dest/contracts/governance_proposer.js +37 -17
- package/dest/contracts/gse.d.ts +32 -0
- package/dest/contracts/gse.d.ts.map +1 -0
- package/dest/contracts/gse.js +72 -0
- package/dest/contracts/inbox.d.ts +26 -0
- package/dest/contracts/inbox.d.ts.map +1 -0
- package/dest/contracts/inbox.js +45 -0
- package/dest/contracts/index.d.ts +9 -3
- package/dest/contracts/index.d.ts.map +1 -1
- package/dest/contracts/index.js +8 -2
- package/dest/contracts/multicall.d.ts +21 -0
- package/dest/contracts/multicall.d.ts.map +1 -0
- package/dest/contracts/multicall.js +156 -0
- package/dest/contracts/registry.d.ts +10 -5
- package/dest/contracts/registry.d.ts.map +1 -1
- package/dest/contracts/registry.js +44 -16
- package/dest/contracts/rollup.d.ts +204 -40
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +529 -79
- package/dest/contracts/slasher_contract.d.ts +44 -0
- package/dest/contracts/slasher_contract.d.ts.map +1 -0
- package/dest/contracts/slasher_contract.js +75 -0
- package/dest/contracts/tally_slashing_proposer.d.ts +139 -0
- package/dest/contracts/tally_slashing_proposer.d.ts.map +1 -0
- package/dest/contracts/tally_slashing_proposer.js +313 -0
- package/dest/contracts/utils.d.ts +3 -0
- package/dest/contracts/utils.d.ts.map +1 -0
- package/dest/contracts/utils.js +11 -0
- package/dest/deploy_l1_contracts.d.ts +577 -21114
- package/dest/deploy_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_l1_contracts.js +1225 -421
- package/dest/eth-signer/eth-signer.d.ts +21 -0
- package/dest/eth-signer/eth-signer.d.ts.map +1 -0
- package/dest/eth-signer/eth-signer.js +5 -0
- package/dest/eth-signer/index.d.ts +2 -0
- package/dest/eth-signer/index.d.ts.map +1 -0
- package/dest/eth-signer/index.js +1 -0
- package/dest/index.d.ts +7 -3
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +6 -2
- package/dest/l1_artifacts.d.ts +77344 -0
- package/dest/l1_artifacts.d.ts.map +1 -0
- package/dest/l1_artifacts.js +166 -0
- package/dest/l1_contract_addresses.d.ts +24 -4
- package/dest/l1_contract_addresses.d.ts.map +1 -1
- package/dest/l1_contract_addresses.js +22 -18
- package/dest/l1_reader.d.ts +2 -2
- package/dest/l1_reader.d.ts.map +1 -1
- package/dest/l1_reader.js +8 -8
- package/dest/l1_tx_utils/config.d.ts +59 -0
- package/dest/l1_tx_utils/config.d.ts.map +1 -0
- package/dest/l1_tx_utils/config.js +82 -0
- package/dest/l1_tx_utils/constants.d.ts +6 -0
- package/dest/l1_tx_utils/constants.d.ts.map +1 -0
- package/dest/l1_tx_utils/constants.js +14 -0
- package/dest/l1_tx_utils/factory.d.ts +24 -0
- package/dest/l1_tx_utils/factory.d.ts.map +1 -0
- package/dest/l1_tx_utils/factory.js +12 -0
- package/dest/l1_tx_utils/index.d.ts +10 -0
- package/dest/l1_tx_utils/index.d.ts.map +1 -0
- package/dest/l1_tx_utils/index.js +10 -0
- package/dest/l1_tx_utils/interfaces.d.ts +76 -0
- package/dest/l1_tx_utils/interfaces.d.ts.map +1 -0
- package/dest/l1_tx_utils/interfaces.js +4 -0
- package/dest/l1_tx_utils/l1_tx_utils.d.ts +94 -0
- package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -0
- package/dest/l1_tx_utils/l1_tx_utils.js +610 -0
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +26 -0
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +1 -0
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +26 -0
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +94 -0
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -0
- package/dest/l1_tx_utils/readonly_l1_tx_utils.js +430 -0
- package/dest/l1_tx_utils/signer.d.ts +4 -0
- package/dest/l1_tx_utils/signer.d.ts.map +1 -0
- package/dest/l1_tx_utils/signer.js +16 -0
- package/dest/l1_tx_utils/types.d.ts +67 -0
- package/dest/l1_tx_utils/types.d.ts.map +1 -0
- package/dest/l1_tx_utils/types.js +26 -0
- package/dest/l1_tx_utils/utils.d.ts +4 -0
- package/dest/l1_tx_utils/utils.d.ts.map +1 -0
- package/dest/l1_tx_utils/utils.js +14 -0
- package/dest/l1_types.d.ts +6 -0
- package/dest/l1_types.d.ts.map +1 -0
- package/dest/l1_types.js +1 -0
- package/dest/publisher_manager.d.ts +15 -0
- package/dest/publisher_manager.d.ts.map +1 -0
- package/dest/publisher_manager.js +88 -0
- package/dest/queries.d.ts +4 -2
- package/dest/queries.d.ts.map +1 -1
- package/dest/queries.js +53 -12
- package/dest/test/chain_monitor.d.ts +73 -0
- package/dest/test/chain_monitor.d.ts.map +1 -0
- package/dest/test/chain_monitor.js +215 -0
- package/dest/test/delayed_tx_utils.d.ts +8 -3
- package/dest/test/delayed_tx_utils.d.ts.map +1 -1
- package/dest/test/delayed_tx_utils.js +13 -6
- package/dest/test/eth_cheat_codes.d.ts +217 -0
- package/dest/test/eth_cheat_codes.d.ts.map +1 -0
- package/dest/test/eth_cheat_codes.js +558 -0
- package/dest/test/eth_cheat_codes_with_state.d.ts +2 -2
- package/dest/test/eth_cheat_codes_with_state.d.ts.map +1 -1
- package/dest/test/eth_cheat_codes_with_state.js +1 -1
- package/dest/test/index.d.ts +4 -1
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +3 -0
- package/dest/test/rollup_cheat_codes.d.ts +87 -0
- package/dest/test/rollup_cheat_codes.d.ts.map +1 -0
- package/dest/test/rollup_cheat_codes.js +266 -0
- package/dest/test/start_anvil.d.ts +7 -1
- package/dest/test/start_anvil.d.ts.map +1 -1
- package/dest/test/start_anvil.js +16 -7
- package/dest/test/tx_delayer.d.ts +18 -7
- package/dest/test/tx_delayer.d.ts.map +1 -1
- package/dest/test/tx_delayer.js +95 -19
- package/dest/test/upgrade_utils.d.ts +6 -5
- package/dest/test/upgrade_utils.d.ts.map +1 -1
- package/dest/test/upgrade_utils.js +23 -16
- package/dest/types.d.ts +7 -8
- package/dest/types.d.ts.map +1 -1
- package/dest/types.js +3 -1
- package/dest/utils.d.ts +2 -1
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +43 -88
- package/dest/zkPassportVerifierAddress.d.ts +15 -0
- package/dest/zkPassportVerifierAddress.d.ts.map +1 -0
- package/dest/zkPassportVerifierAddress.js +11 -0
- package/package.json +28 -19
- package/src/account.ts +5 -0
- package/src/client.ts +42 -4
- package/src/config.ts +592 -31
- package/src/contracts/empire_base.ts +77 -7
- package/src/contracts/empire_slashing_proposer.ts +265 -0
- package/src/contracts/errors.ts +13 -0
- package/src/contracts/fee_asset_handler.ts +63 -0
- package/src/contracts/fee_juice.ts +29 -15
- package/src/contracts/governance.ts +80 -77
- package/src/contracts/governance_proposer.ts +66 -24
- package/src/contracts/gse.ts +88 -0
- package/src/contracts/inbox.ts +63 -0
- package/src/contracts/index.ts +8 -2
- package/src/contracts/multicall.ts +155 -0
- package/src/contracts/registry.ts +51 -26
- package/src/contracts/rollup.ts +596 -74
- package/src/contracts/slasher_contract.ts +89 -0
- package/src/contracts/tally_slashing_proposer.ts +316 -0
- package/src/contracts/utils.ts +14 -0
- package/src/deploy_l1_contracts.ts +1459 -538
- package/src/eth-signer/eth-signer.ts +25 -0
- package/src/eth-signer/index.ts +1 -0
- package/src/index.ts +6 -2
- package/src/l1_artifacts.ts +254 -0
- package/src/l1_contract_addresses.ts +32 -19
- package/src/l1_reader.ts +9 -9
- package/src/l1_tx_utils/README.md +177 -0
- package/src/l1_tx_utils/config.ts +143 -0
- package/src/l1_tx_utils/constants.ts +18 -0
- package/src/l1_tx_utils/factory.ts +64 -0
- package/src/l1_tx_utils/index.ts +12 -0
- package/src/l1_tx_utils/interfaces.ts +86 -0
- package/src/l1_tx_utils/l1_tx_utils.ts +718 -0
- package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +77 -0
- package/src/l1_tx_utils/readonly_l1_tx_utils.ts +558 -0
- package/src/l1_tx_utils/signer.ts +28 -0
- package/src/l1_tx_utils/types.ts +85 -0
- package/src/l1_tx_utils/utils.ts +16 -0
- package/src/l1_types.ts +6 -0
- package/src/publisher_manager.ts +106 -0
- package/src/queries.ts +73 -15
- package/src/test/chain_monitor.ts +243 -0
- package/src/test/delayed_tx_utils.ts +34 -6
- package/src/test/eth_cheat_codes.ts +588 -0
- package/src/test/eth_cheat_codes_with_state.ts +1 -1
- package/src/test/index.ts +3 -0
- package/src/test/rollup_cheat_codes.ts +307 -0
- package/src/test/start_anvil.ts +22 -5
- package/src/test/tx_delayer.ts +127 -26
- package/src/test/upgrade_utils.ts +30 -21
- package/src/types.ts +10 -8
- package/src/utils.ts +49 -90
- package/src/zkPassportVerifierAddress.ts +15 -0
- package/dest/contracts/forwarder.d.ts +0 -24
- package/dest/contracts/forwarder.d.ts.map +0 -1
- package/dest/contracts/forwarder.js +0 -101
- package/dest/contracts/slashing_proposer.d.ts +0 -21
- package/dest/contracts/slashing_proposer.d.ts.map +0 -1
- package/dest/contracts/slashing_proposer.js +0 -47
- package/dest/eth_cheat_codes.d.ts +0 -147
- package/dest/eth_cheat_codes.d.ts.map +0 -1
- package/dest/eth_cheat_codes.js +0 -303
- package/dest/l1_tx_utils.d.ts +0 -192
- package/dest/l1_tx_utils.d.ts.map +0 -1
- package/dest/l1_tx_utils.js +0 -641
- package/dest/l1_tx_utils_with_blobs.d.ts +0 -12
- package/dest/l1_tx_utils_with_blobs.d.ts.map +0 -1
- package/dest/l1_tx_utils_with_blobs.js +0 -64
- package/src/contracts/forwarder.ts +0 -132
- package/src/contracts/slashing_proposer.ts +0 -51
- package/src/eth_cheat_codes.ts +0 -314
- package/src/l1_tx_utils.ts +0 -847
- package/src/l1_tx_utils_with_blobs.ts +0 -86
package/src/l1_tx_utils.ts
DELETED
|
@@ -1,847 +0,0 @@
|
|
|
1
|
-
import { compactArray, times } from '@aztec/foundation/collection';
|
|
2
|
-
import {
|
|
3
|
-
type ConfigMappingsType,
|
|
4
|
-
bigintConfigHelper,
|
|
5
|
-
getDefaultConfig,
|
|
6
|
-
numberConfigHelper,
|
|
7
|
-
} from '@aztec/foundation/config';
|
|
8
|
-
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
9
|
-
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
10
|
-
import { sleep } from '@aztec/foundation/sleep';
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
type Abi,
|
|
14
|
-
type Account,
|
|
15
|
-
type Address,
|
|
16
|
-
type BaseError,
|
|
17
|
-
type BlockOverrides,
|
|
18
|
-
type ContractFunctionExecutionError,
|
|
19
|
-
type GetTransactionReturnType,
|
|
20
|
-
type Hex,
|
|
21
|
-
MethodNotFoundRpcError,
|
|
22
|
-
MethodNotSupportedRpcError,
|
|
23
|
-
type StateOverride,
|
|
24
|
-
type TransactionReceipt,
|
|
25
|
-
formatGwei,
|
|
26
|
-
getContractError,
|
|
27
|
-
hexToBytes,
|
|
28
|
-
} from 'viem';
|
|
29
|
-
|
|
30
|
-
import type { ViemPublicClient, ViemWalletClient } from './types.js';
|
|
31
|
-
import { formatViemError } from './utils.js';
|
|
32
|
-
|
|
33
|
-
// 1_000_000_000 Gwei = 1 ETH
|
|
34
|
-
// 1_000_000_000 Wei = 1 Gwei
|
|
35
|
-
// 1_000_000_000_000_000_000 Wei = 1 ETH
|
|
36
|
-
|
|
37
|
-
const WEI_CONST = 1_000_000_000n;
|
|
38
|
-
|
|
39
|
-
// @note using this large gas limit to avoid the issue of `gas limit too low` when estimating gas in reth
|
|
40
|
-
const LARGE_GAS_LIMIT = 10_000_000n;
|
|
41
|
-
|
|
42
|
-
// setting a minimum bump percentage to 10% due to geth's implementation
|
|
43
|
-
// https://github.com/ethereum/go-ethereum/blob/e3d61e6db028c412f74bc4d4c7e117a9e29d0de0/core/txpool/legacypool/list.go#L298
|
|
44
|
-
const MIN_REPLACEMENT_BUMP_PERCENTAGE = 10;
|
|
45
|
-
|
|
46
|
-
// setting a minimum bump percentage to 100% due to geth's implementation
|
|
47
|
-
// https://github.com/ethereum/go-ethereum/blob/e3d61e6db028c412f74bc4d4c7e117a9e29d0de0/core/txpool/blobpool/config.go#L34
|
|
48
|
-
const MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE = 100;
|
|
49
|
-
|
|
50
|
-
// Avg ethereum block time is ~12s
|
|
51
|
-
const BLOCK_TIME_MS = 12_000;
|
|
52
|
-
|
|
53
|
-
export interface L1TxUtilsConfig {
|
|
54
|
-
/**
|
|
55
|
-
* How much to increase calculated gas limit.
|
|
56
|
-
*/
|
|
57
|
-
gasLimitBufferPercentage?: number;
|
|
58
|
-
/**
|
|
59
|
-
* Maximum gas price in gwei
|
|
60
|
-
*/
|
|
61
|
-
maxGwei?: bigint;
|
|
62
|
-
/**
|
|
63
|
-
* Maximum blob fee per gas in gwei
|
|
64
|
-
*/
|
|
65
|
-
maxBlobGwei?: bigint;
|
|
66
|
-
/**
|
|
67
|
-
* Priority fee bump percentage
|
|
68
|
-
*/
|
|
69
|
-
priorityFeeBumpPercentage?: number;
|
|
70
|
-
/**
|
|
71
|
-
* How much to increase priority fee by each attempt (percentage)
|
|
72
|
-
*/
|
|
73
|
-
priorityFeeRetryBumpPercentage?: number;
|
|
74
|
-
/**
|
|
75
|
-
* Fixed priority fee per gas in Gwei. Overrides any priority fee bump percentage config
|
|
76
|
-
*/
|
|
77
|
-
fixedPriorityFeePerGas?: number;
|
|
78
|
-
/**
|
|
79
|
-
* Maximum number of speed-up attempts
|
|
80
|
-
*/
|
|
81
|
-
maxAttempts?: number;
|
|
82
|
-
/**
|
|
83
|
-
* How often to check tx status
|
|
84
|
-
*/
|
|
85
|
-
checkIntervalMs?: number;
|
|
86
|
-
/**
|
|
87
|
-
* How long before considering tx stalled
|
|
88
|
-
*/
|
|
89
|
-
stallTimeMs?: number;
|
|
90
|
-
/**
|
|
91
|
-
* How long to wait for a tx to be mined before giving up
|
|
92
|
-
*/
|
|
93
|
-
txTimeoutMs?: number;
|
|
94
|
-
/**
|
|
95
|
-
* How many attempts will be done to get a tx after it was sent?
|
|
96
|
-
* First attempt is done at 1s, second at 2s, third at 3s, etc.
|
|
97
|
-
*/
|
|
98
|
-
txPropagationMaxQueryAttempts?: number;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export const l1TxUtilsConfigMappings: ConfigMappingsType<L1TxUtilsConfig> = {
|
|
102
|
-
gasLimitBufferPercentage: {
|
|
103
|
-
description: 'How much to increase calculated gas limit by (percentage)',
|
|
104
|
-
env: 'L1_GAS_LIMIT_BUFFER_PERCENTAGE',
|
|
105
|
-
...numberConfigHelper(20),
|
|
106
|
-
},
|
|
107
|
-
maxGwei: {
|
|
108
|
-
description: 'Maximum gas price in gwei',
|
|
109
|
-
env: 'L1_GAS_PRICE_MAX',
|
|
110
|
-
...bigintConfigHelper(100n),
|
|
111
|
-
},
|
|
112
|
-
maxBlobGwei: {
|
|
113
|
-
description: 'Maximum blob fee per gas in gwei',
|
|
114
|
-
env: 'L1_BLOB_FEE_PER_GAS_MAX',
|
|
115
|
-
...bigintConfigHelper(1_500n),
|
|
116
|
-
},
|
|
117
|
-
priorityFeeBumpPercentage: {
|
|
118
|
-
description: 'How much to increase priority fee by each attempt (percentage)',
|
|
119
|
-
env: 'L1_PRIORITY_FEE_BUMP_PERCENTAGE',
|
|
120
|
-
...numberConfigHelper(20),
|
|
121
|
-
},
|
|
122
|
-
priorityFeeRetryBumpPercentage: {
|
|
123
|
-
description: 'How much to increase priority fee by each retry attempt (percentage)',
|
|
124
|
-
env: 'L1_PRIORITY_FEE_RETRY_BUMP_PERCENTAGE',
|
|
125
|
-
...numberConfigHelper(50),
|
|
126
|
-
},
|
|
127
|
-
fixedPriorityFeePerGas: {
|
|
128
|
-
description: 'Fixed priority fee per gas in Gwei. Overrides any priority fee bump percentage',
|
|
129
|
-
env: 'L1_FIXED_PRIORITY_FEE_PER_GAS',
|
|
130
|
-
...numberConfigHelper(0),
|
|
131
|
-
},
|
|
132
|
-
maxAttempts: {
|
|
133
|
-
description: 'Maximum number of speed-up attempts',
|
|
134
|
-
env: 'L1_TX_MONITOR_MAX_ATTEMPTS',
|
|
135
|
-
...numberConfigHelper(3),
|
|
136
|
-
},
|
|
137
|
-
checkIntervalMs: {
|
|
138
|
-
description: 'How often to check tx status',
|
|
139
|
-
env: 'L1_TX_MONITOR_CHECK_INTERVAL_MS',
|
|
140
|
-
...numberConfigHelper(1_000),
|
|
141
|
-
},
|
|
142
|
-
stallTimeMs: {
|
|
143
|
-
description: 'How long before considering tx stalled',
|
|
144
|
-
env: 'L1_TX_MONITOR_STALL_TIME_MS',
|
|
145
|
-
...numberConfigHelper(45_000),
|
|
146
|
-
},
|
|
147
|
-
txTimeoutMs: {
|
|
148
|
-
description: 'How long to wait for a tx to be mined before giving up. Set to 0 to disable.',
|
|
149
|
-
env: 'L1_TX_MONITOR_TX_TIMEOUT_MS',
|
|
150
|
-
...numberConfigHelper(300_000), // 5 mins
|
|
151
|
-
},
|
|
152
|
-
txPropagationMaxQueryAttempts: {
|
|
153
|
-
description: 'How many attempts will be done to get a tx after it was sent',
|
|
154
|
-
env: 'L1_TX_PROPAGATION_MAX_QUERY_ATTEMPTS',
|
|
155
|
-
...numberConfigHelper(3),
|
|
156
|
-
},
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
export const defaultL1TxUtilsConfig = getDefaultConfig<L1TxUtilsConfig>(l1TxUtilsConfigMappings);
|
|
160
|
-
|
|
161
|
-
export interface L1TxRequest {
|
|
162
|
-
to: Address | null;
|
|
163
|
-
data?: Hex;
|
|
164
|
-
value?: bigint;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export type L1GasConfig = Partial<L1TxUtilsConfig> & { gasLimit?: bigint; txTimeoutAt?: Date };
|
|
168
|
-
|
|
169
|
-
export interface L1BlobInputs {
|
|
170
|
-
blobs: Uint8Array[];
|
|
171
|
-
kzg: any;
|
|
172
|
-
maxFeePerBlobGas?: bigint;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export interface GasPrice {
|
|
176
|
-
maxFeePerGas: bigint;
|
|
177
|
-
maxPriorityFeePerGas: bigint;
|
|
178
|
-
maxFeePerBlobGas?: bigint;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export type TransactionStats = {
|
|
182
|
-
/** Address of the sender. */
|
|
183
|
-
sender: string;
|
|
184
|
-
/** Hash of the transaction. */
|
|
185
|
-
transactionHash: string;
|
|
186
|
-
/** Size in bytes of the tx calldata */
|
|
187
|
-
calldataSize: number;
|
|
188
|
-
/** Gas required to pay for the calldata inclusion (depends on size and number of zeros) */
|
|
189
|
-
calldataGas: number;
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
export class L1TxUtils {
|
|
193
|
-
public readonly config: L1TxUtilsConfig;
|
|
194
|
-
private interrupted = false;
|
|
195
|
-
|
|
196
|
-
constructor(
|
|
197
|
-
public publicClient: ViemPublicClient,
|
|
198
|
-
public walletClient: ViemWalletClient,
|
|
199
|
-
protected logger: Logger = createLogger('L1TxUtils'),
|
|
200
|
-
config?: Partial<L1TxUtilsConfig>,
|
|
201
|
-
private debugMaxGasLimit: boolean = false,
|
|
202
|
-
) {
|
|
203
|
-
this.config = {
|
|
204
|
-
...defaultL1TxUtilsConfig,
|
|
205
|
-
...(config || {}),
|
|
206
|
-
};
|
|
207
|
-
this.logger?.debug('Initializing L1 TX utils with config', { config: this.config });
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
public interrupt() {
|
|
211
|
-
this.interrupted = true;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
public restart() {
|
|
215
|
-
this.interrupted = false;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
public getSenderAddress() {
|
|
219
|
-
return this.walletClient.account.address;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
public getSenderBalance(): Promise<bigint> {
|
|
223
|
-
return this.publicClient.getBalance({
|
|
224
|
-
address: this.getSenderAddress(),
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
public getBlock() {
|
|
229
|
-
return this.publicClient.getBlock();
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
public getBlockNumber() {
|
|
233
|
-
return this.publicClient.getBlockNumber();
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Sends a transaction with gas estimation and pricing
|
|
238
|
-
* @param request - The transaction request (to, data, value)
|
|
239
|
-
* @param gasConfig - Optional gas configuration
|
|
240
|
-
* @returns The transaction hash and parameters used
|
|
241
|
-
*/
|
|
242
|
-
public async sendTransaction(
|
|
243
|
-
request: L1TxRequest,
|
|
244
|
-
_gasConfig?: L1GasConfig,
|
|
245
|
-
blobInputs?: L1BlobInputs,
|
|
246
|
-
): Promise<{ txHash: Hex; gasLimit: bigint; gasPrice: GasPrice }> {
|
|
247
|
-
try {
|
|
248
|
-
const gasConfig = { ...this.config, ..._gasConfig };
|
|
249
|
-
const account = this.walletClient.account;
|
|
250
|
-
let gasLimit: bigint;
|
|
251
|
-
|
|
252
|
-
if (this.debugMaxGasLimit) {
|
|
253
|
-
gasLimit = LARGE_GAS_LIMIT;
|
|
254
|
-
} else if (gasConfig.gasLimit) {
|
|
255
|
-
gasLimit = gasConfig.gasLimit;
|
|
256
|
-
} else {
|
|
257
|
-
gasLimit = await this.estimateGas(account, request);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const gasPrice = await this.getGasPrice(gasConfig, !!blobInputs);
|
|
261
|
-
|
|
262
|
-
if (gasConfig.txTimeoutAt && Date.now() > gasConfig.txTimeoutAt.getTime()) {
|
|
263
|
-
throw new Error('Transaction timed out before sending');
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
let txHash: Hex;
|
|
267
|
-
if (blobInputs) {
|
|
268
|
-
txHash = await this.walletClient.sendTransaction({
|
|
269
|
-
...request,
|
|
270
|
-
...blobInputs,
|
|
271
|
-
gas: gasLimit,
|
|
272
|
-
maxFeePerGas: gasPrice.maxFeePerGas,
|
|
273
|
-
maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas,
|
|
274
|
-
maxFeePerBlobGas: gasPrice.maxFeePerBlobGas!,
|
|
275
|
-
});
|
|
276
|
-
} else {
|
|
277
|
-
txHash = await this.walletClient.sendTransaction({
|
|
278
|
-
...request,
|
|
279
|
-
gas: gasLimit,
|
|
280
|
-
maxFeePerGas: gasPrice.maxFeePerGas,
|
|
281
|
-
maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas,
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
this.logger?.verbose(`Sent L1 transaction ${txHash}`, {
|
|
285
|
-
gasLimit,
|
|
286
|
-
maxFeePerGas: formatGwei(gasPrice.maxFeePerGas),
|
|
287
|
-
maxPriorityFeePerGas: formatGwei(gasPrice.maxPriorityFeePerGas),
|
|
288
|
-
...(gasPrice.maxFeePerBlobGas && { maxFeePerBlobGas: formatGwei(gasPrice.maxFeePerBlobGas) }),
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
return { txHash, gasLimit, gasPrice };
|
|
292
|
-
} catch (err: any) {
|
|
293
|
-
const viemError = formatViemError(err);
|
|
294
|
-
this.logger?.error(`Failed to send L1 transaction`, viemError.message, {
|
|
295
|
-
metaMessages: viemError.metaMessages,
|
|
296
|
-
});
|
|
297
|
-
throw viemError;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Monitors a transaction until completion, handling speed-ups if needed
|
|
303
|
-
* @param request - Original transaction request (needed for speed-ups)
|
|
304
|
-
* @param initialTxHash - Hash of the initial transaction
|
|
305
|
-
* @param params - Parameters used in the initial transaction
|
|
306
|
-
* @param gasConfig - Optional gas configuration
|
|
307
|
-
*/
|
|
308
|
-
public async monitorTransaction(
|
|
309
|
-
request: L1TxRequest,
|
|
310
|
-
initialTxHash: Hex,
|
|
311
|
-
params: { gasLimit: bigint },
|
|
312
|
-
_gasConfig?: Partial<L1TxUtilsConfig> & { txTimeoutAt?: Date },
|
|
313
|
-
_blobInputs?: L1BlobInputs,
|
|
314
|
-
isCancelTx: boolean = false,
|
|
315
|
-
): Promise<TransactionReceipt> {
|
|
316
|
-
const isBlobTx = !!_blobInputs;
|
|
317
|
-
const gasConfig = { ...this.config, ..._gasConfig };
|
|
318
|
-
const account = this.walletClient.account;
|
|
319
|
-
const blobInputs = _blobInputs || {};
|
|
320
|
-
const makeGetTransactionBackoff = () =>
|
|
321
|
-
makeBackoff(times(gasConfig.txPropagationMaxQueryAttempts ?? 3, i => i + 1));
|
|
322
|
-
|
|
323
|
-
// Retry a few times, in case the tx is not yet propagated.
|
|
324
|
-
const tx = await retry<GetTransactionReturnType>(
|
|
325
|
-
() => this.publicClient.getTransaction({ hash: initialTxHash }),
|
|
326
|
-
`Getting L1 transaction ${initialTxHash}`,
|
|
327
|
-
makeGetTransactionBackoff(),
|
|
328
|
-
this.logger,
|
|
329
|
-
true,
|
|
330
|
-
);
|
|
331
|
-
|
|
332
|
-
if (!tx) {
|
|
333
|
-
throw new Error(`Failed to get L1 transaction ${initialTxHash} to monitor`);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
if (tx?.nonce === undefined || tx?.nonce === null) {
|
|
337
|
-
throw new Error(`Failed to get L1 transaction ${initialTxHash} nonce`);
|
|
338
|
-
}
|
|
339
|
-
const nonce = tx.nonce;
|
|
340
|
-
|
|
341
|
-
const txHashes = new Set<Hex>([initialTxHash]);
|
|
342
|
-
let currentTxHash = initialTxHash;
|
|
343
|
-
let attempts = 0;
|
|
344
|
-
let lastAttemptSent = Date.now();
|
|
345
|
-
let lastGasPrice: GasPrice = {
|
|
346
|
-
maxFeePerGas: tx.maxFeePerGas!,
|
|
347
|
-
maxPriorityFeePerGas: tx.maxPriorityFeePerGas!,
|
|
348
|
-
maxFeePerBlobGas: tx.maxFeePerBlobGas!,
|
|
349
|
-
};
|
|
350
|
-
const initialTxTime = lastAttemptSent;
|
|
351
|
-
|
|
352
|
-
let txTimedOut = false;
|
|
353
|
-
const isTimedOut = () =>
|
|
354
|
-
(gasConfig.txTimeoutAt && Date.now() > gasConfig.txTimeoutAt.getTime()) ||
|
|
355
|
-
(gasConfig.txTimeoutMs !== undefined && Date.now() - initialTxTime > gasConfig.txTimeoutMs) ||
|
|
356
|
-
this.interrupted ||
|
|
357
|
-
false;
|
|
358
|
-
|
|
359
|
-
while (!txTimedOut) {
|
|
360
|
-
try {
|
|
361
|
-
const currentNonce = await this.publicClient.getTransactionCount({ address: account.address });
|
|
362
|
-
if (currentNonce > nonce) {
|
|
363
|
-
for (const hash of txHashes) {
|
|
364
|
-
try {
|
|
365
|
-
const receipt = await this.publicClient.getTransactionReceipt({ hash });
|
|
366
|
-
if (receipt) {
|
|
367
|
-
if (receipt.status === 'reverted') {
|
|
368
|
-
this.logger?.error(`L1 transaction ${hash} reverted`, receipt);
|
|
369
|
-
} else {
|
|
370
|
-
this.logger?.debug(`L1 transaction ${hash} mined`);
|
|
371
|
-
}
|
|
372
|
-
return receipt;
|
|
373
|
-
}
|
|
374
|
-
} catch (err) {
|
|
375
|
-
if (err instanceof Error && err.message.includes('reverted')) {
|
|
376
|
-
throw formatViemError(err);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// Retry a few times, in case the tx is not yet propagated.
|
|
383
|
-
const tx = await retry<GetTransactionReturnType>(
|
|
384
|
-
() => this.publicClient.getTransaction({ hash: currentTxHash }),
|
|
385
|
-
`Getting L1 transaction ${currentTxHash}`,
|
|
386
|
-
makeGetTransactionBackoff(),
|
|
387
|
-
this.logger,
|
|
388
|
-
true,
|
|
389
|
-
);
|
|
390
|
-
const timePassed = Date.now() - lastAttemptSent;
|
|
391
|
-
|
|
392
|
-
if (tx && timePassed < gasConfig.stallTimeMs!) {
|
|
393
|
-
this.logger?.debug(`L1 transaction ${currentTxHash} pending. Time passed: ${timePassed}ms.`);
|
|
394
|
-
|
|
395
|
-
// Check timeout before continuing
|
|
396
|
-
txTimedOut = isTimedOut();
|
|
397
|
-
if (txTimedOut) {
|
|
398
|
-
break;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
await sleep(gasConfig.checkIntervalMs!);
|
|
402
|
-
continue;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
if (timePassed > gasConfig.stallTimeMs! && attempts < gasConfig.maxAttempts!) {
|
|
406
|
-
attempts++;
|
|
407
|
-
const newGasPrice = await this.getGasPrice(
|
|
408
|
-
gasConfig,
|
|
409
|
-
isBlobTx,
|
|
410
|
-
attempts,
|
|
411
|
-
tx.maxFeePerGas && tx.maxPriorityFeePerGas
|
|
412
|
-
? {
|
|
413
|
-
maxFeePerGas: tx.maxFeePerGas,
|
|
414
|
-
maxPriorityFeePerGas: tx.maxPriorityFeePerGas,
|
|
415
|
-
maxFeePerBlobGas: tx.maxFeePerBlobGas,
|
|
416
|
-
}
|
|
417
|
-
: undefined,
|
|
418
|
-
);
|
|
419
|
-
lastGasPrice = newGasPrice;
|
|
420
|
-
|
|
421
|
-
this.logger?.debug(
|
|
422
|
-
`L1 transaction ${currentTxHash} appears stuck. Attempting speed-up ${attempts}/${gasConfig.maxAttempts} ` +
|
|
423
|
-
`with new priority fee ${formatGwei(newGasPrice.maxPriorityFeePerGas)} gwei`,
|
|
424
|
-
{
|
|
425
|
-
maxFeePerGas: formatGwei(newGasPrice.maxFeePerGas),
|
|
426
|
-
maxPriorityFeePerGas: formatGwei(newGasPrice.maxPriorityFeePerGas),
|
|
427
|
-
...(newGasPrice.maxFeePerBlobGas && { maxFeePerBlobGas: formatGwei(newGasPrice.maxFeePerBlobGas) }),
|
|
428
|
-
},
|
|
429
|
-
);
|
|
430
|
-
|
|
431
|
-
currentTxHash = await this.walletClient.sendTransaction({
|
|
432
|
-
...request,
|
|
433
|
-
...blobInputs,
|
|
434
|
-
nonce,
|
|
435
|
-
gas: params.gasLimit,
|
|
436
|
-
maxFeePerGas: newGasPrice.maxFeePerGas,
|
|
437
|
-
maxPriorityFeePerGas: newGasPrice.maxPriorityFeePerGas,
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
txHashes.add(currentTxHash);
|
|
441
|
-
lastAttemptSent = Date.now();
|
|
442
|
-
}
|
|
443
|
-
await sleep(gasConfig.checkIntervalMs!);
|
|
444
|
-
} catch (err: any) {
|
|
445
|
-
const viemError = formatViemError(err);
|
|
446
|
-
this.logger?.warn(`Error monitoring L1 transaction ${currentTxHash}:`, viemError.message);
|
|
447
|
-
if (viemError.message?.includes('reverted')) {
|
|
448
|
-
throw viemError;
|
|
449
|
-
}
|
|
450
|
-
await sleep(gasConfig.checkIntervalMs!);
|
|
451
|
-
}
|
|
452
|
-
// Check if tx has timed out.
|
|
453
|
-
txTimedOut = isTimedOut();
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
if (!isCancelTx) {
|
|
457
|
-
// Fire cancellation without awaiting to avoid blocking the main thread
|
|
458
|
-
this.attemptTxCancellation(nonce, isBlobTx, lastGasPrice, attempts)
|
|
459
|
-
.then(cancelTxHash => {
|
|
460
|
-
this.logger?.debug(`Sent cancellation tx ${cancelTxHash} for timed out tx ${currentTxHash}`);
|
|
461
|
-
})
|
|
462
|
-
.catch(err => {
|
|
463
|
-
const viemError = formatViemError(err);
|
|
464
|
-
this.logger?.error(`Failed to send cancellation for timed out tx ${currentTxHash}:`, viemError.message, {
|
|
465
|
-
metaMessages: viemError.metaMessages,
|
|
466
|
-
});
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
this.logger?.error(`L1 transaction ${currentTxHash} timed out`, {
|
|
470
|
-
txHash: currentTxHash,
|
|
471
|
-
...tx,
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
throw new Error(`L1 transaction ${currentTxHash} timed out`);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
/**
|
|
478
|
-
* Sends a transaction and monitors it until completion
|
|
479
|
-
* @param request - The transaction request (to, data, value)
|
|
480
|
-
* @param gasConfig - Optional gas configuration
|
|
481
|
-
* @returns The receipt of the successful transaction
|
|
482
|
-
*/
|
|
483
|
-
public async sendAndMonitorTransaction(
|
|
484
|
-
request: L1TxRequest,
|
|
485
|
-
gasConfig?: L1GasConfig,
|
|
486
|
-
blobInputs?: L1BlobInputs,
|
|
487
|
-
): Promise<{ receipt: TransactionReceipt; gasPrice: GasPrice }> {
|
|
488
|
-
const { txHash, gasLimit, gasPrice } = await this.sendTransaction(request, gasConfig, blobInputs);
|
|
489
|
-
const receipt = await this.monitorTransaction(request, txHash, { gasLimit }, gasConfig, blobInputs);
|
|
490
|
-
return { receipt, gasPrice };
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* Gets the current gas price with bounds checking
|
|
495
|
-
*/
|
|
496
|
-
public async getGasPrice(
|
|
497
|
-
_gasConfig?: L1TxUtilsConfig,
|
|
498
|
-
isBlobTx: boolean = false,
|
|
499
|
-
attempt: number = 0,
|
|
500
|
-
previousGasPrice?: typeof attempt extends 0 ? never : GasPrice,
|
|
501
|
-
): Promise<GasPrice> {
|
|
502
|
-
const gasConfig = { ...this.config, ..._gasConfig };
|
|
503
|
-
const block = await this.publicClient.getBlock({ blockTag: 'latest' });
|
|
504
|
-
const baseFee = block.baseFeePerGas ?? 0n;
|
|
505
|
-
|
|
506
|
-
// Get blob base fee if available
|
|
507
|
-
let blobBaseFee = 0n;
|
|
508
|
-
if (isBlobTx) {
|
|
509
|
-
try {
|
|
510
|
-
const blobBaseFeeHex = await this.publicClient.request({ method: 'eth_blobBaseFee' });
|
|
511
|
-
blobBaseFee = BigInt(blobBaseFeeHex);
|
|
512
|
-
this.logger?.debug('L1 Blob base fee:', { blobBaseFee: formatGwei(blobBaseFee) });
|
|
513
|
-
} catch {
|
|
514
|
-
this.logger?.warn('Failed to get L1 blob base fee', attempt);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
let priorityFee: bigint;
|
|
519
|
-
if (gasConfig.fixedPriorityFeePerGas) {
|
|
520
|
-
this.logger?.debug('Using fixed priority fee per L1 gas', {
|
|
521
|
-
fixedPriorityFeePerGas: gasConfig.fixedPriorityFeePerGas,
|
|
522
|
-
});
|
|
523
|
-
// try to maintain precision up to 1000000 wei
|
|
524
|
-
priorityFee = BigInt(gasConfig.fixedPriorityFeePerGas * 1_000_000) * (WEI_CONST / 1_000_000n);
|
|
525
|
-
} else {
|
|
526
|
-
// Get initial priority fee from the network
|
|
527
|
-
priorityFee = await this.publicClient.estimateMaxPriorityFeePerGas();
|
|
528
|
-
}
|
|
529
|
-
let maxFeePerGas = baseFee;
|
|
530
|
-
|
|
531
|
-
let maxFeePerBlobGas = blobBaseFee;
|
|
532
|
-
|
|
533
|
-
// Bump base fee so it's valid for next blocks if it stalls
|
|
534
|
-
const numBlocks = Math.ceil(gasConfig.stallTimeMs! / BLOCK_TIME_MS);
|
|
535
|
-
for (let i = 0; i < numBlocks; i++) {
|
|
536
|
-
// each block can go up 12.5% from previous baseFee
|
|
537
|
-
maxFeePerGas = (maxFeePerGas * (1_000n + 125n)) / 1_000n;
|
|
538
|
-
// same for blob gas fee
|
|
539
|
-
maxFeePerBlobGas = (maxFeePerBlobGas * (1_000n + 125n)) / 1_000n;
|
|
540
|
-
}
|
|
541
|
-
if (attempt > 0) {
|
|
542
|
-
const configBump =
|
|
543
|
-
gasConfig.priorityFeeRetryBumpPercentage ?? defaultL1TxUtilsConfig.priorityFeeRetryBumpPercentage!;
|
|
544
|
-
|
|
545
|
-
// if this is a blob tx, we have to use the blob bump percentage
|
|
546
|
-
const minBumpPercentage = isBlobTx ? MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE : MIN_REPLACEMENT_BUMP_PERCENTAGE;
|
|
547
|
-
|
|
548
|
-
const bumpPercentage = configBump > minBumpPercentage ? configBump : minBumpPercentage;
|
|
549
|
-
// Calculate minimum required fees based on previous attempt
|
|
550
|
-
// multiply by 100 & divide by 100 to maintain some precision
|
|
551
|
-
const minPriorityFee =
|
|
552
|
-
(previousGasPrice!.maxPriorityFeePerGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n;
|
|
553
|
-
const minMaxFee = (previousGasPrice!.maxFeePerGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n;
|
|
554
|
-
|
|
555
|
-
// Add priority fee to maxFeePerGas
|
|
556
|
-
maxFeePerGas += priorityFee;
|
|
557
|
-
|
|
558
|
-
// Use maximum between current network values and minimum required values
|
|
559
|
-
priorityFee = priorityFee > minPriorityFee ? priorityFee : minPriorityFee;
|
|
560
|
-
maxFeePerGas = maxFeePerGas > minMaxFee ? maxFeePerGas : minMaxFee;
|
|
561
|
-
} else {
|
|
562
|
-
// first attempt, just bump priority fee, unless it's a fixed config
|
|
563
|
-
// multiply by 100 & divide by 100 to maintain some precision
|
|
564
|
-
if (!gasConfig.fixedPriorityFeePerGas) {
|
|
565
|
-
priorityFee = (priorityFee * (100_00n + BigInt((gasConfig.priorityFeeBumpPercentage || 0) * 1_00))) / 100_00n;
|
|
566
|
-
}
|
|
567
|
-
maxFeePerGas += priorityFee;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
// Ensure we don't exceed maxGwei
|
|
571
|
-
const maxGweiInWei = gasConfig.maxGwei! * WEI_CONST;
|
|
572
|
-
maxFeePerGas = maxFeePerGas > maxGweiInWei ? maxGweiInWei : maxFeePerGas;
|
|
573
|
-
|
|
574
|
-
// Ensure we don't exceed maxBlobGwei
|
|
575
|
-
if (maxFeePerBlobGas) {
|
|
576
|
-
const maxBlobGweiInWei = gasConfig.maxBlobGwei! * WEI_CONST;
|
|
577
|
-
maxFeePerBlobGas = maxFeePerBlobGas > maxBlobGweiInWei ? maxBlobGweiInWei : maxFeePerBlobGas;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// Ensure priority fee doesn't exceed max fee
|
|
581
|
-
const maxPriorityFeePerGas = priorityFee > maxFeePerGas ? maxFeePerGas : priorityFee;
|
|
582
|
-
|
|
583
|
-
if (attempt > 0 && previousGasPrice?.maxFeePerBlobGas) {
|
|
584
|
-
const bumpPercentage =
|
|
585
|
-
gasConfig.priorityFeeRetryBumpPercentage! > MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE
|
|
586
|
-
? gasConfig.priorityFeeRetryBumpPercentage!
|
|
587
|
-
: MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE;
|
|
588
|
-
|
|
589
|
-
// calculate min blob fee based on previous attempt
|
|
590
|
-
const minBlobFee = (previousGasPrice.maxFeePerBlobGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n;
|
|
591
|
-
|
|
592
|
-
// use max between current network values and min required values
|
|
593
|
-
maxFeePerBlobGas = maxFeePerBlobGas > minBlobFee ? maxFeePerBlobGas : minBlobFee;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
this.logger?.debug(`Computed L1 gas price`, {
|
|
597
|
-
attempt,
|
|
598
|
-
baseFee: formatGwei(baseFee),
|
|
599
|
-
maxFeePerGas: formatGwei(maxFeePerGas),
|
|
600
|
-
maxPriorityFeePerGas: formatGwei(maxPriorityFeePerGas),
|
|
601
|
-
...(maxFeePerBlobGas && { maxFeePerBlobGas: formatGwei(maxFeePerBlobGas) }),
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
return {
|
|
605
|
-
maxFeePerGas,
|
|
606
|
-
maxPriorityFeePerGas,
|
|
607
|
-
...(maxFeePerBlobGas && { maxFeePerBlobGas: maxFeePerBlobGas }),
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
/**
|
|
612
|
-
* Estimates gas and adds buffer
|
|
613
|
-
*/
|
|
614
|
-
public async estimateGas(
|
|
615
|
-
account: Account | Hex,
|
|
616
|
-
request: L1TxRequest,
|
|
617
|
-
_gasConfig?: L1TxUtilsConfig,
|
|
618
|
-
_blobInputs?: L1BlobInputs,
|
|
619
|
-
): Promise<bigint> {
|
|
620
|
-
const gasConfig = { ...this.config, ..._gasConfig };
|
|
621
|
-
let initialEstimate = 0n;
|
|
622
|
-
if (_blobInputs) {
|
|
623
|
-
// @note requests with blobs also require maxFeePerBlobGas to be set
|
|
624
|
-
const gasPrice = await this.getGasPrice(gasConfig, true, 0);
|
|
625
|
-
initialEstimate = await this.publicClient.estimateGas({
|
|
626
|
-
account,
|
|
627
|
-
...request,
|
|
628
|
-
..._blobInputs,
|
|
629
|
-
maxFeePerBlobGas: gasPrice.maxFeePerBlobGas!,
|
|
630
|
-
gas: LARGE_GAS_LIMIT,
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
this.logger?.debug(`L1 gas used in estimateGas by blob tx: ${initialEstimate}`);
|
|
634
|
-
} else {
|
|
635
|
-
initialEstimate = await this.publicClient.estimateGas({ account, ...request, gas: LARGE_GAS_LIMIT });
|
|
636
|
-
this.logger?.debug(`L1 gas used in estimateGas by non-blob tx: ${initialEstimate}`);
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
// Add buffer based on either fixed amount or percentage
|
|
640
|
-
const withBuffer = this.bumpGasLimit(initialEstimate, gasConfig);
|
|
641
|
-
|
|
642
|
-
return withBuffer;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
async getTransactionStats(txHash: string): Promise<TransactionStats | undefined> {
|
|
646
|
-
const tx = await this.publicClient.getTransaction({ hash: txHash as Hex });
|
|
647
|
-
if (!tx) {
|
|
648
|
-
return undefined;
|
|
649
|
-
}
|
|
650
|
-
const calldata = hexToBytes(tx.input);
|
|
651
|
-
return {
|
|
652
|
-
sender: tx.from.toString(),
|
|
653
|
-
transactionHash: tx.hash,
|
|
654
|
-
calldataSize: calldata.length,
|
|
655
|
-
calldataGas: getCalldataGasUsage(calldata),
|
|
656
|
-
};
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
public async tryGetErrorFromRevertedTx(
|
|
660
|
-
data: Hex,
|
|
661
|
-
args: {
|
|
662
|
-
args: readonly any[];
|
|
663
|
-
functionName: string;
|
|
664
|
-
abi: Abi;
|
|
665
|
-
address: Hex;
|
|
666
|
-
},
|
|
667
|
-
blobInputs: (L1BlobInputs & { maxFeePerBlobGas: bigint }) | undefined,
|
|
668
|
-
stateOverride: StateOverride = [],
|
|
669
|
-
) {
|
|
670
|
-
try {
|
|
671
|
-
await this.publicClient.simulateContract({
|
|
672
|
-
...args,
|
|
673
|
-
account: this.walletClient.account,
|
|
674
|
-
stateOverride,
|
|
675
|
-
});
|
|
676
|
-
this.logger?.trace('Simulated blob tx', { blobInputs });
|
|
677
|
-
// If the above passes, we have a blob error. We cannot simulate blob txs, and failed txs no longer throw errors.
|
|
678
|
-
// Strangely, the only way to throw the revert reason as an error and provide blobs is prepareTransactionRequest.
|
|
679
|
-
// See: https://github.com/wevm/viem/issues/2075
|
|
680
|
-
// This throws a EstimateGasExecutionError with the custom error information:
|
|
681
|
-
const request = blobInputs
|
|
682
|
-
? {
|
|
683
|
-
account: this.walletClient.account,
|
|
684
|
-
to: args.address,
|
|
685
|
-
data,
|
|
686
|
-
blobs: blobInputs.blobs,
|
|
687
|
-
kzg: blobInputs.kzg,
|
|
688
|
-
maxFeePerBlobGas: blobInputs.maxFeePerBlobGas,
|
|
689
|
-
}
|
|
690
|
-
: {
|
|
691
|
-
account: this.walletClient.account,
|
|
692
|
-
to: args.address,
|
|
693
|
-
data,
|
|
694
|
-
};
|
|
695
|
-
this.logger?.trace('Preparing tx', { request });
|
|
696
|
-
await this.walletClient.prepareTransactionRequest(request);
|
|
697
|
-
this.logger?.trace('Prepared tx');
|
|
698
|
-
return undefined;
|
|
699
|
-
} catch (simulationErr: any) {
|
|
700
|
-
// If we don't have a ContractFunctionExecutionError, we have a blob related error => use getContractError to get the error msg.
|
|
701
|
-
const contractErr =
|
|
702
|
-
simulationErr.name === 'ContractFunctionExecutionError'
|
|
703
|
-
? simulationErr
|
|
704
|
-
: getContractError(simulationErr as BaseError, {
|
|
705
|
-
args: [],
|
|
706
|
-
abi: args.abi,
|
|
707
|
-
functionName: args.functionName,
|
|
708
|
-
address: args.address,
|
|
709
|
-
sender: this.walletClient.account.address,
|
|
710
|
-
});
|
|
711
|
-
if (contractErr.name === 'ContractFunctionExecutionError') {
|
|
712
|
-
const execErr = contractErr as ContractFunctionExecutionError;
|
|
713
|
-
return tryGetCustomErrorNameContractFunction(execErr);
|
|
714
|
-
}
|
|
715
|
-
this.logger?.error(`Error getting error from simulation`, simulationErr);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
public async simulateGasUsed(
|
|
720
|
-
request: L1TxRequest & { gas?: bigint },
|
|
721
|
-
blockOverrides: BlockOverrides<bigint, number> = {},
|
|
722
|
-
stateOverrides: StateOverride = [],
|
|
723
|
-
_gasConfig?: L1TxUtilsConfig & { fallbackGasEstimate?: bigint },
|
|
724
|
-
): Promise<bigint> {
|
|
725
|
-
const gasConfig = { ...this.config, ..._gasConfig };
|
|
726
|
-
const gasPrice = await this.getGasPrice(gasConfig, false);
|
|
727
|
-
|
|
728
|
-
const nonce = await this.publicClient.getTransactionCount({ address: this.walletClient.account.address });
|
|
729
|
-
|
|
730
|
-
try {
|
|
731
|
-
const result = await this.publicClient.simulate({
|
|
732
|
-
validation: true,
|
|
733
|
-
blocks: [
|
|
734
|
-
{
|
|
735
|
-
blockOverrides,
|
|
736
|
-
stateOverrides,
|
|
737
|
-
calls: [
|
|
738
|
-
{
|
|
739
|
-
from: this.walletClient.account.address,
|
|
740
|
-
to: request.to!,
|
|
741
|
-
data: request.data,
|
|
742
|
-
maxFeePerGas: gasPrice.maxFeePerGas,
|
|
743
|
-
maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas,
|
|
744
|
-
gas: request.gas ?? LARGE_GAS_LIMIT,
|
|
745
|
-
nonce,
|
|
746
|
-
},
|
|
747
|
-
],
|
|
748
|
-
},
|
|
749
|
-
],
|
|
750
|
-
});
|
|
751
|
-
|
|
752
|
-
this.logger?.debug(`L1 gas used in simulation: ${result[0].calls[0].gasUsed}`, {
|
|
753
|
-
result,
|
|
754
|
-
});
|
|
755
|
-
if (result[0].calls[0].status === 'failure') {
|
|
756
|
-
this.logger?.error('L1 transaction Simulation failed', {
|
|
757
|
-
error: result[0].calls[0].error,
|
|
758
|
-
});
|
|
759
|
-
throw new Error(`L1 transaction simulation failed with error: ${result[0].calls[0].error.message}`);
|
|
760
|
-
}
|
|
761
|
-
return result[0].gasUsed;
|
|
762
|
-
} catch (err) {
|
|
763
|
-
if (err instanceof MethodNotFoundRpcError || err instanceof MethodNotSupportedRpcError) {
|
|
764
|
-
if (gasConfig.fallbackGasEstimate) {
|
|
765
|
-
this.logger?.warn(
|
|
766
|
-
`Node does not support eth_simulateV1 API. Using fallback gas estimate: ${gasConfig.fallbackGasEstimate}`,
|
|
767
|
-
);
|
|
768
|
-
return gasConfig.fallbackGasEstimate;
|
|
769
|
-
}
|
|
770
|
-
this.logger?.error('Node does not support eth_simulateV1 API');
|
|
771
|
-
}
|
|
772
|
-
throw err;
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
public bumpGasLimit(gasLimit: bigint, _gasConfig?: L1TxUtilsConfig): bigint {
|
|
777
|
-
const gasConfig = { ...this.config, ..._gasConfig };
|
|
778
|
-
return gasLimit + (gasLimit * BigInt((gasConfig?.gasLimitBufferPercentage || 0) * 1_00)) / 100_00n;
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
/**
|
|
782
|
-
* Attempts to cancel a transaction by sending a 0-value tx to self with same nonce but higher gas prices
|
|
783
|
-
* @param nonce - The nonce of the transaction to cancel
|
|
784
|
-
* @param previousGasPrice - The gas price of the previous transaction
|
|
785
|
-
* @param attempts - The number of attempts to cancel the transaction
|
|
786
|
-
* @returns The hash of the cancellation transaction
|
|
787
|
-
*/
|
|
788
|
-
protected async attemptTxCancellation(nonce: number, isBlobTx = false, previousGasPrice?: GasPrice, attempts = 0) {
|
|
789
|
-
if (isBlobTx) {
|
|
790
|
-
throw new Error('Cannot cancel blob transactions, please use L1TxUtilsWithBlobsClass');
|
|
791
|
-
}
|
|
792
|
-
const account = this.walletClient.account;
|
|
793
|
-
|
|
794
|
-
// Get gas price with higher priority fee for cancellation
|
|
795
|
-
const cancelGasPrice = await this.getGasPrice(
|
|
796
|
-
{
|
|
797
|
-
...this.config,
|
|
798
|
-
// Use high bump for cancellation to ensure it replaces the original tx
|
|
799
|
-
priorityFeeRetryBumpPercentage: 150, // 150% bump should be enough to replace any tx
|
|
800
|
-
},
|
|
801
|
-
isBlobTx,
|
|
802
|
-
attempts + 1,
|
|
803
|
-
previousGasPrice,
|
|
804
|
-
);
|
|
805
|
-
|
|
806
|
-
this.logger?.debug(`Attempting to cancel transaction with nonce ${nonce}`, {
|
|
807
|
-
maxFeePerGas: formatGwei(cancelGasPrice.maxFeePerGas),
|
|
808
|
-
maxPriorityFeePerGas: formatGwei(cancelGasPrice.maxPriorityFeePerGas),
|
|
809
|
-
});
|
|
810
|
-
const request = {
|
|
811
|
-
to: account.address,
|
|
812
|
-
value: 0n,
|
|
813
|
-
};
|
|
814
|
-
|
|
815
|
-
// Send 0-value tx to self with higher gas price
|
|
816
|
-
const cancelTxHash = await this.walletClient.sendTransaction({
|
|
817
|
-
...request,
|
|
818
|
-
nonce,
|
|
819
|
-
gas: 21_000n, // Standard ETH transfer gas
|
|
820
|
-
maxFeePerGas: cancelGasPrice.maxFeePerGas,
|
|
821
|
-
maxPriorityFeePerGas: cancelGasPrice.maxPriorityFeePerGas,
|
|
822
|
-
});
|
|
823
|
-
const receipt = await this.monitorTransaction(
|
|
824
|
-
request,
|
|
825
|
-
cancelTxHash,
|
|
826
|
-
{ gasLimit: 21_000n },
|
|
827
|
-
undefined,
|
|
828
|
-
undefined,
|
|
829
|
-
true,
|
|
830
|
-
);
|
|
831
|
-
|
|
832
|
-
return receipt.transactionHash;
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
export function tryGetCustomErrorNameContractFunction(err: ContractFunctionExecutionError) {
|
|
837
|
-
return compactArray([err.shortMessage, ...(err.metaMessages ?? []).slice(0, 2).map(s => s.trim())]).join(' ');
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
/*
|
|
841
|
-
* Returns cost of calldata usage in Ethereum.
|
|
842
|
-
* @param data - Calldata.
|
|
843
|
-
* @returns 4 for each zero byte, 16 for each nonzero.
|
|
844
|
-
*/
|
|
845
|
-
export function getCalldataGasUsage(data: Uint8Array) {
|
|
846
|
-
return data.filter(byte => byte === 0).length * 4 + data.filter(byte => byte !== 0).length * 16;
|
|
847
|
-
}
|