@aztec/ethereum 0.0.1-commit.b2a5d0dd1 → 0.0.1-commit.b3d3157a
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/client.d.ts +11 -3
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +35 -3
- package/dest/contracts/chain_state_override.d.ts +47 -7
- package/dest/contracts/chain_state_override.d.ts.map +1 -1
- package/dest/contracts/chain_state_override.js +57 -20
- package/dest/contracts/empire_base.d.ts +1 -3
- package/dest/contracts/empire_base.d.ts.map +1 -1
- package/dest/contracts/fee_asset_price_oracle.d.ts +6 -2
- package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -1
- package/dest/contracts/fee_asset_price_oracle.js +11 -6
- package/dest/contracts/governance.d.ts +108 -33
- package/dest/contracts/governance.d.ts.map +1 -1
- package/dest/contracts/governance.js +193 -12
- package/dest/contracts/governance_proposer.d.ts +33 -3
- package/dest/contracts/governance_proposer.d.ts.map +1 -1
- package/dest/contracts/governance_proposer.js +51 -9
- package/dest/contracts/inbox.js +1 -1
- package/dest/contracts/multicall.d.ts +79 -13
- package/dest/contracts/multicall.d.ts.map +1 -1
- package/dest/contracts/multicall.js +117 -95
- package/dest/contracts/outbox.d.ts +27 -4
- package/dest/contracts/outbox.d.ts.map +1 -1
- package/dest/contracts/outbox.js +34 -7
- package/dest/contracts/rollup.d.ts +327 -57
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +102 -40
- package/dest/l1_artifacts.d.ts +5108 -155
- package/dest/l1_artifacts.d.ts.map +1 -1
- package/dest/l1_contract_addresses.d.ts +61 -61
- package/dest/l1_contract_addresses.d.ts.map +1 -1
- package/dest/l1_contract_addresses.js +101 -76
- package/dest/l1_reader.d.ts +3 -5
- package/dest/l1_reader.d.ts.map +1 -1
- package/dest/l1_reader.js +3 -6
- package/dest/l1_tx_utils/l1_tx_utils.d.ts +7 -1
- package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_tx_utils.js +21 -10
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.js +2 -1
- package/dest/l1_tx_utils/tx_delayer.d.ts +5 -3
- package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -1
- package/dest/l1_tx_utils/tx_delayer.js +7 -5
- package/dest/test/eth_cheat_codes.d.ts +7 -1
- package/dest/test/eth_cheat_codes.d.ts.map +1 -1
- package/dest/test/eth_cheat_codes.js +11 -0
- package/dest/test/rollup_cheat_codes.d.ts +27 -3
- package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
- package/dest/test/rollup_cheat_codes.js +50 -6
- package/dest/utils.d.ts +9 -2
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +34 -6
- package/package.json +6 -6
- package/src/client.ts +39 -1
- package/src/contracts/chain_state_override.ts +77 -24
- package/src/contracts/empire_base.ts +0 -2
- package/src/contracts/fee_asset_price_oracle.ts +10 -5
- package/src/contracts/governance.ts +266 -9
- package/src/contracts/governance_proposer.ts +52 -5
- package/src/contracts/inbox.ts +1 -1
- package/src/contracts/multicall.ts +179 -105
- package/src/contracts/outbox.ts +42 -8
- package/src/contracts/rollup.ts +124 -42
- package/src/l1_contract_addresses.ts +120 -87
- package/src/l1_reader.ts +4 -9
- package/src/l1_tx_utils/l1_tx_utils.ts +27 -14
- package/src/l1_tx_utils/readonly_l1_tx_utils.ts +2 -1
- package/src/l1_tx_utils/tx_delayer.ts +16 -7
- package/src/test/eth_cheat_codes.ts +10 -0
- package/src/test/rollup_cheat_codes.ts +53 -5
- package/src/utils.ts +30 -6
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type { ConfigMappingsType } from '@aztec/foundation/config';
|
|
1
|
+
import type { ConfigMapping, ConfigMappingsType } from '@aztec/foundation/config';
|
|
2
2
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
3
|
-
import { schemas
|
|
3
|
+
import { schemas } from '@aztec/foundation/schemas';
|
|
4
4
|
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Required L1 contract address keys.
|
|
9
9
|
* NOTE: When changing this list, make sure to update CLI & CI scripts accordingly.
|
|
10
10
|
* For reference: https://github.com/AztecProtocol/aztec-packages/pull/5553
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
const REQUIRED_L1_CONTRACT_ADDRESS_KEYS = [
|
|
13
13
|
'rollupAddress',
|
|
14
14
|
'registryAddress',
|
|
15
15
|
'inboxAddress',
|
|
@@ -23,95 +23,128 @@ export const L1ContractsNames = [
|
|
|
23
23
|
'stakingAssetAddress',
|
|
24
24
|
] as const;
|
|
25
25
|
|
|
26
|
+
/** Optional L1 contract address keys — present only in some deployments. */
|
|
27
|
+
export const OPTIONAL_L1_CONTRACT_ADDRESS_KEYS = [
|
|
28
|
+
'feeAssetHandlerAddress',
|
|
29
|
+
'stakingAssetHandlerAddress',
|
|
30
|
+
'zkPassportVerifierAddress',
|
|
31
|
+
'gseAddress',
|
|
32
|
+
'dateGatedRelayerAddress',
|
|
33
|
+
] as const;
|
|
34
|
+
|
|
35
|
+
type RequiredContractKey = (typeof REQUIRED_L1_CONTRACT_ADDRESS_KEYS)[number];
|
|
36
|
+
type OptionalContractKey = (typeof OPTIONAL_L1_CONTRACT_ADDRESS_KEYS)[number];
|
|
37
|
+
|
|
26
38
|
/** Provides the directory of current L1 contract addresses */
|
|
27
39
|
export type L1ContractAddresses = {
|
|
28
|
-
[K in
|
|
40
|
+
[K in RequiredContractKey]: EthAddress;
|
|
29
41
|
} & {
|
|
30
|
-
|
|
31
|
-
stakingAssetHandlerAddress?: EthAddress | undefined;
|
|
32
|
-
zkPassportVerifierAddress?: EthAddress | undefined;
|
|
33
|
-
gseAddress?: EthAddress | undefined;
|
|
34
|
-
dateGatedRelayerAddress?: EthAddress | undefined;
|
|
42
|
+
[K in OptionalContractKey]?: EthAddress;
|
|
35
43
|
};
|
|
36
44
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
feeJuiceAddress: schemas.EthAddress,
|
|
44
|
-
stakingAssetAddress: schemas.EthAddress,
|
|
45
|
-
feeJuicePortalAddress: schemas.EthAddress,
|
|
46
|
-
coinIssuerAddress: schemas.EthAddress,
|
|
47
|
-
rewardDistributorAddress: schemas.EthAddress,
|
|
48
|
-
governanceProposerAddress: schemas.EthAddress,
|
|
49
|
-
governanceAddress: schemas.EthAddress,
|
|
50
|
-
feeAssetHandlerAddress: schemas.EthAddress.optional(),
|
|
51
|
-
stakingAssetHandlerAddress: schemas.EthAddress.optional(),
|
|
52
|
-
zkPassportVerifierAddress: schemas.EthAddress.optional(),
|
|
53
|
-
gseAddress: schemas.EthAddress.optional(),
|
|
54
|
-
dateGatedRelayerAddress: schemas.EthAddress.optional(),
|
|
55
|
-
}),
|
|
56
|
-
);
|
|
45
|
+
/**
|
|
46
|
+
* Names of required L1 contract addresses.
|
|
47
|
+
* NOTE: When changing the set of contracts, update CLI & CI scripts accordingly.
|
|
48
|
+
* For reference: https://github.com/AztecProtocol/aztec-packages/pull/5553
|
|
49
|
+
*/
|
|
50
|
+
export const L1ContractsNames = REQUIRED_L1_CONTRACT_ADDRESS_KEYS;
|
|
57
51
|
|
|
58
52
|
const parseEnv = (val: string) => EthAddress.fromString(val);
|
|
53
|
+
const addrEnv = (description: string, env?: ConfigMapping<EthAddress>['env']): ConfigMapping<EthAddress> => ({
|
|
54
|
+
description,
|
|
55
|
+
parseEnv,
|
|
56
|
+
...(env && { env }),
|
|
57
|
+
});
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
},
|
|
85
|
-
feeJuiceAddress: {
|
|
86
|
-
description: 'The deployed L1 Fee Juice contract address.',
|
|
87
|
-
parseEnv,
|
|
88
|
-
},
|
|
89
|
-
stakingAssetAddress: {
|
|
90
|
-
description: 'The deployed L1 staking asset contract address.',
|
|
91
|
-
parseEnv,
|
|
92
|
-
},
|
|
93
|
-
feeJuicePortalAddress: {
|
|
94
|
-
description: 'The deployed L1 Fee Juice portal contract address.',
|
|
95
|
-
parseEnv,
|
|
96
|
-
},
|
|
97
|
-
coinIssuerAddress: {
|
|
98
|
-
description: 'The deployed L1 coinIssuer contract address',
|
|
99
|
-
parseEnv,
|
|
100
|
-
},
|
|
101
|
-
rewardDistributorAddress: {
|
|
102
|
-
description: 'The deployed L1 rewardDistributor contract address',
|
|
103
|
-
parseEnv,
|
|
104
|
-
},
|
|
105
|
-
governanceProposerAddress: {
|
|
106
|
-
description: 'The deployed L1 governanceProposer contract address',
|
|
107
|
-
parseEnv,
|
|
108
|
-
},
|
|
109
|
-
governanceAddress: {
|
|
110
|
-
description: 'The deployed L1 governance contract address',
|
|
111
|
-
parseEnv,
|
|
112
|
-
},
|
|
113
|
-
stakingAssetHandlerAddress: {
|
|
114
|
-
description: 'The deployed L1 stakingAssetHandler contract address',
|
|
115
|
-
parseEnv,
|
|
116
|
-
},
|
|
59
|
+
/**
|
|
60
|
+
* Config mapping for all L1 contract addresses.
|
|
61
|
+
* This is the single source of truth for which contract addresses exist and how to parse them.
|
|
62
|
+
*/
|
|
63
|
+
export const l1ContractAddressesMapping: ConfigMappingsType<L1ContractAddresses> = {
|
|
64
|
+
rollupAddress: addrEnv('The deployed L1 rollup contract address.'),
|
|
65
|
+
registryAddress: addrEnv('The deployed L1 registry contract address.', 'REGISTRY_CONTRACT_ADDRESS'),
|
|
66
|
+
inboxAddress: addrEnv('The deployed L1 inbox contract address.'),
|
|
67
|
+
outboxAddress: addrEnv('The deployed L1 outbox contract address.'),
|
|
68
|
+
feeJuiceAddress: addrEnv('The deployed L1 Fee Juice contract address.'),
|
|
69
|
+
feeJuicePortalAddress: addrEnv('The deployed L1 Fee Juice portal contract address.'),
|
|
70
|
+
coinIssuerAddress: addrEnv('The deployed L1 coinIssuer contract address'),
|
|
71
|
+
rewardDistributorAddress: addrEnv('The deployed L1 rewardDistributor contract address'),
|
|
72
|
+
governanceProposerAddress: addrEnv('The deployed L1 governanceProposer contract address'),
|
|
73
|
+
governanceAddress: addrEnv('The deployed L1 governance contract address'),
|
|
74
|
+
stakingAssetAddress: addrEnv('The deployed L1 staking asset contract address.'),
|
|
75
|
+
feeAssetHandlerAddress: addrEnv(
|
|
76
|
+
'The deployed L1 feeAssetHandler contract address',
|
|
77
|
+
'FEE_ASSET_HANDLER_CONTRACT_ADDRESS',
|
|
78
|
+
),
|
|
79
|
+
stakingAssetHandlerAddress: addrEnv('The deployed L1 stakingAssetHandler contract address'),
|
|
80
|
+
zkPassportVerifierAddress: addrEnv('The deployed L1 ZK passport verifier contract address'),
|
|
81
|
+
gseAddress: addrEnv('The deployed L1 GSE contract address'),
|
|
82
|
+
dateGatedRelayerAddress: addrEnv('The deployed L1 date-gated relayer contract address'),
|
|
117
83
|
};
|
|
84
|
+
|
|
85
|
+
/** Keys present in {@link l1ContractAddressesMapping}. */
|
|
86
|
+
export type L1ContractAddressMappingKey = keyof typeof l1ContractAddressesMapping;
|
|
87
|
+
|
|
88
|
+
type RequiredAddressShape = { [K in RequiredContractKey]: typeof schemas.EthAddress };
|
|
89
|
+
type OptionalAddressShape = { [K in OptionalContractKey]: ReturnType<typeof schemas.EthAddress.optional> };
|
|
90
|
+
|
|
91
|
+
export const L1ContractAddressesSchema = z.object({
|
|
92
|
+
...(Object.fromEntries(REQUIRED_L1_CONTRACT_ADDRESS_KEYS.map(k => [k, schemas.EthAddress])) as RequiredAddressShape),
|
|
93
|
+
...(Object.fromEntries(
|
|
94
|
+
OPTIONAL_L1_CONTRACT_ADDRESS_KEYS.map(k => [k, schemas.EthAddress.optional()]),
|
|
95
|
+
) as OptionalAddressShape),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Selects entries from {@link l1ContractAddressesMapping} so composed configs can reuse the same
|
|
100
|
+
* env vars, descriptions, and parsers without duplicating definitions.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* const mappings = {
|
|
105
|
+
* ...pickL1ContractAddressMappings('rollupAddress'),
|
|
106
|
+
* nodeId: { ... },
|
|
107
|
+
* };
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export function pickL1ContractAddressMappings<const K extends readonly L1ContractAddressMappingKey[]>(
|
|
111
|
+
...keys: K
|
|
112
|
+
): Pick<typeof l1ContractAddressesMapping, K[number]> {
|
|
113
|
+
return Object.fromEntries(keys.map(key => [key, l1ContractAddressesMapping[key]] as const)) as Pick<
|
|
114
|
+
typeof l1ContractAddressesMapping,
|
|
115
|
+
K[number]
|
|
116
|
+
>;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Like {@link pickL1ContractAddressMappings}, but returns a fragment of {@link L1ContractAddressesSchema}'s shape for
|
|
121
|
+
* spreading into `z.object({ ...pickL1ContractAddressesSchemaShape('rollupAddress'), localField: z... })`.
|
|
122
|
+
*/
|
|
123
|
+
export function pickL1ContractAddressesSchema<const K extends readonly L1ContractAddressMappingKey[]>(
|
|
124
|
+
...keys: K
|
|
125
|
+
): Pick<typeof L1ContractAddressesSchema.shape, K[number]> {
|
|
126
|
+
return Object.fromEntries(keys.map(key => [key, L1ContractAddressesSchema.shape[key]])) as Pick<
|
|
127
|
+
typeof L1ContractAddressesSchema.shape,
|
|
128
|
+
K[number]
|
|
129
|
+
>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Builds an {@link L1ContractAddresses} object from config that stores those fields at the top level. */
|
|
133
|
+
export function pickL1ContractAddresses<T extends L1ContractAddresses>(config: T): L1ContractAddresses {
|
|
134
|
+
const result: Partial<L1ContractAddresses> = {};
|
|
135
|
+
for (const key of REQUIRED_L1_CONTRACT_ADDRESS_KEYS) {
|
|
136
|
+
result[key] = config[key];
|
|
137
|
+
}
|
|
138
|
+
for (const key of OPTIONAL_L1_CONTRACT_ADDRESS_KEYS) {
|
|
139
|
+
const v = config[key];
|
|
140
|
+
if (v !== undefined) {
|
|
141
|
+
result[key] = v;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return result as L1ContractAddresses;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function randomL1ContractAddresses(includeOptional: boolean = false): L1ContractAddresses {
|
|
148
|
+
const keys = includeOptional ? [...L1ContractsNames, ...OPTIONAL_L1_CONTRACT_ADDRESS_KEYS] : L1ContractsNames;
|
|
149
|
+
return Object.fromEntries(keys.map(name => [name, EthAddress.random()])) as L1ContractAddresses;
|
|
150
|
+
}
|
package/src/l1_reader.ts
CHANGED
|
@@ -8,29 +8,23 @@ import {
|
|
|
8
8
|
import { type L1ContractAddresses, l1ContractAddressesMapping } from './l1_contract_addresses.js';
|
|
9
9
|
|
|
10
10
|
/** Configuration of the L1GlobalReader. */
|
|
11
|
-
export
|
|
11
|
+
export type L1ReaderConfig = {
|
|
12
12
|
/** List of URLs of Ethereum RPC nodes that services will connect to (comma separated). */
|
|
13
13
|
l1RpcUrls: string[];
|
|
14
14
|
/** The RPC Url of the ethereum debug host for trace and debug methods. */
|
|
15
15
|
l1DebugRpcUrls: string[];
|
|
16
16
|
/** The chain ID of the ethereum host. */
|
|
17
17
|
l1ChainId: number;
|
|
18
|
-
/** The deployed l1 contract addresses */
|
|
19
|
-
l1Contracts: L1ContractAddresses;
|
|
20
18
|
/** The polling interval viem uses in ms */
|
|
21
19
|
viemPollingIntervalMS: number;
|
|
22
20
|
/** Timeout for HTTP requests to the L1 RPC node in ms. */
|
|
23
21
|
l1HttpTimeoutMS?: number;
|
|
24
|
-
}
|
|
22
|
+
} & L1ContractAddresses;
|
|
25
23
|
|
|
26
24
|
export const l1ReaderConfigMappings: ConfigMappingsType<L1ReaderConfig> = {
|
|
27
|
-
l1Contracts: {
|
|
28
|
-
description: 'The deployed L1 contract addresses',
|
|
29
|
-
nested: l1ContractAddressesMapping,
|
|
30
|
-
},
|
|
31
25
|
l1ChainId: {
|
|
32
26
|
env: 'L1_CHAIN_ID',
|
|
33
|
-
|
|
27
|
+
...optionalNumberConfigHelper(),
|
|
34
28
|
description: 'The chain ID of the ethereum host.',
|
|
35
29
|
},
|
|
36
30
|
l1RpcUrls: {
|
|
@@ -55,6 +49,7 @@ export const l1ReaderConfigMappings: ConfigMappingsType<L1ReaderConfig> = {
|
|
|
55
49
|
description: 'Timeout for HTTP requests to the L1 RPC node in ms.',
|
|
56
50
|
...optionalNumberConfigHelper(),
|
|
57
51
|
},
|
|
52
|
+
...l1ContractAddressesMapping,
|
|
58
53
|
};
|
|
59
54
|
|
|
60
55
|
export function getL1ReaderConfigFromEnv(): L1ReaderConfig {
|
|
@@ -131,6 +131,14 @@ export class L1TxUtils extends ReadOnlyL1TxUtils {
|
|
|
131
131
|
);
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Clears the cached `lastSentNonce` so the next `sendTransaction` call fetches the real
|
|
136
|
+
* nonce from the chain. Call this after an L1 reorg to prevent stale nonce references.
|
|
137
|
+
*/
|
|
138
|
+
public resetNonce(): void {
|
|
139
|
+
this.lastSentNonce = undefined;
|
|
140
|
+
}
|
|
141
|
+
|
|
134
142
|
public getSenderAddress() {
|
|
135
143
|
return this.address;
|
|
136
144
|
}
|
|
@@ -213,6 +221,19 @@ export class L1TxUtils extends ReadOnlyL1TxUtils {
|
|
|
213
221
|
return await this.signTransaction(txRequest as TransactionSerializable);
|
|
214
222
|
}
|
|
215
223
|
|
|
224
|
+
private async checkInterruptedOrTimedOut(gasConfig: Pick<L1TxConfig, 'txTimeoutAt'>): Promise<Date> {
|
|
225
|
+
if (this.interrupted) {
|
|
226
|
+
throw new InterruptError(`Transaction sending is interrupted`);
|
|
227
|
+
}
|
|
228
|
+
const now = new Date(await this.getL1Timestamp());
|
|
229
|
+
if (gasConfig.txTimeoutAt && now > gasConfig.txTimeoutAt) {
|
|
230
|
+
throw new TimeoutError(
|
|
231
|
+
`Transaction timed out before sending (now ${now.toISOString()} > timeoutAt ${gasConfig.txTimeoutAt.toISOString()})`,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
return now;
|
|
235
|
+
}
|
|
236
|
+
|
|
216
237
|
/**
|
|
217
238
|
* Sends a transaction with gas estimation and pricing
|
|
218
239
|
* @param request - The transaction request (to, data, value)
|
|
@@ -225,14 +246,15 @@ export class L1TxUtils extends ReadOnlyL1TxUtils {
|
|
|
225
246
|
blobInputs?: L1BlobInputs,
|
|
226
247
|
stateChange: TxUtilsState = TxUtilsState.SENT,
|
|
227
248
|
): Promise<{ txHash: Hex; state: L1TxState }> {
|
|
228
|
-
if (this.interrupted) {
|
|
229
|
-
throw new InterruptError(`Transaction sending is interrupted`);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
249
|
try {
|
|
233
250
|
const gasConfig = merge(this.config, gasConfigOverrides);
|
|
234
251
|
const account = this.getSenderAddress().toString();
|
|
235
252
|
|
|
253
|
+
// Fail fast before doing any work (gas estimation, balance check) if we've been interrupted
|
|
254
|
+
// or if the caller's deadline has already passed. The same check is repeated after gas
|
|
255
|
+
// estimation in case it took long enough to push us past the deadline.
|
|
256
|
+
await this.checkInterruptedOrTimedOut(gasConfig);
|
|
257
|
+
|
|
236
258
|
let gasLimit: bigint;
|
|
237
259
|
if (this.debugMaxGasLimit) {
|
|
238
260
|
gasLimit = MAX_L1_TX_LIMIT;
|
|
@@ -245,16 +267,7 @@ export class L1TxUtils extends ReadOnlyL1TxUtils {
|
|
|
245
267
|
|
|
246
268
|
const gasPrice = await this.getGasPrice(gasConfig, !!blobInputs);
|
|
247
269
|
|
|
248
|
-
|
|
249
|
-
throw new InterruptError(`Transaction sending is interrupted`);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const now = new Date(await this.getL1Timestamp());
|
|
253
|
-
if (gasConfig.txTimeoutAt && now > gasConfig.txTimeoutAt) {
|
|
254
|
-
throw new TimeoutError(
|
|
255
|
-
`Transaction timed out before sending (now ${now.toISOString()} > timeoutAt ${gasConfig.txTimeoutAt.toISOString()})`,
|
|
256
|
-
);
|
|
257
|
-
}
|
|
270
|
+
const now = await this.checkInterruptedOrTimedOut(gasConfig);
|
|
258
271
|
|
|
259
272
|
let txHash: Hex;
|
|
260
273
|
let nonce: number;
|
|
@@ -3,6 +3,7 @@ import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
|
3
3
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
4
4
|
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
5
5
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
6
|
+
import { getErrorCause } from '@aztec/foundation/types';
|
|
6
7
|
import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
|
|
7
8
|
|
|
8
9
|
import pickBy from 'lodash.pickby';
|
|
@@ -395,7 +396,7 @@ export class ReadOnlyL1TxUtils {
|
|
|
395
396
|
this.logger?.debug(`L1 transaction simulation succeeded`, { ...result[0].calls[0] });
|
|
396
397
|
return { gasUsed: result[0].gasUsed, result: result[0].calls[0].data as `0x${string}` };
|
|
397
398
|
} catch (err) {
|
|
398
|
-
if (err
|
|
399
|
+
if (getErrorCause(err, MethodNotFoundRpcError) || getErrorCause(err, MethodNotSupportedRpcError)) {
|
|
399
400
|
if (gasConfig.fallbackGasEstimate) {
|
|
400
401
|
this.logger?.warn(
|
|
401
402
|
`Node does not support eth_simulateV1 API. Using fallback gas estimate: ${gasConfig.fallbackGasEstimate}`,
|
|
@@ -81,7 +81,11 @@ export class Delayer {
|
|
|
81
81
|
|
|
82
82
|
public maxInclusionTimeIntoSlot: number | undefined = undefined;
|
|
83
83
|
public ethereumSlotDuration: bigint;
|
|
84
|
-
public nextWait:
|
|
84
|
+
public nextWait:
|
|
85
|
+
| { l1Timestamp: bigint; timeoutSeconds?: number }
|
|
86
|
+
| { l1BlockNumber: bigint; timeoutSeconds?: number }
|
|
87
|
+
| { indefinitely: true }
|
|
88
|
+
| undefined = undefined;
|
|
85
89
|
public sentTxHashes: Hex[] = [];
|
|
86
90
|
public cancelledTxs: Hex[] = [];
|
|
87
91
|
|
|
@@ -110,13 +114,13 @@ export class Delayer {
|
|
|
110
114
|
}
|
|
111
115
|
|
|
112
116
|
/** Delays the next tx to be sent so it lands on the given L1 block number. */
|
|
113
|
-
pauseNextTxUntilBlock(l1BlockNumber: number | bigint) {
|
|
114
|
-
this.nextWait = { l1BlockNumber: BigInt(l1BlockNumber) };
|
|
117
|
+
pauseNextTxUntilBlock(l1BlockNumber: number | bigint, timeoutSeconds?: number) {
|
|
118
|
+
this.nextWait = { l1BlockNumber: BigInt(l1BlockNumber), timeoutSeconds };
|
|
115
119
|
}
|
|
116
120
|
|
|
117
121
|
/** Delays the next tx to be sent so it lands on the given timestamp. */
|
|
118
|
-
pauseNextTxUntilTimestamp(l1Timestamp: number | bigint) {
|
|
119
|
-
this.nextWait = { l1Timestamp: BigInt(l1Timestamp) };
|
|
122
|
+
pauseNextTxUntilTimestamp(l1Timestamp: number | bigint, timeoutSeconds?: number) {
|
|
123
|
+
this.nextWait = { l1Timestamp: BigInt(l1Timestamp), timeoutSeconds };
|
|
120
124
|
}
|
|
121
125
|
|
|
122
126
|
/** Delays the next tx to be sent indefinitely. */
|
|
@@ -198,9 +202,14 @@ export function wrapClientWithDelayer<T extends ViemClient>(client: T, delayer:
|
|
|
198
202
|
// Or wait until the desired block number or timestamp
|
|
199
203
|
wait =
|
|
200
204
|
'l1BlockNumber' in waitUntil
|
|
201
|
-
? waitUntilBlock(publicClient, waitUntil.l1BlockNumber - 1n, logger)
|
|
205
|
+
? waitUntilBlock(publicClient, waitUntil.l1BlockNumber - 1n, logger, waitUntil.timeoutSeconds)
|
|
202
206
|
: 'l1Timestamp' in waitUntil
|
|
203
|
-
? waitUntilL1Timestamp(
|
|
207
|
+
? waitUntilL1Timestamp(
|
|
208
|
+
publicClient,
|
|
209
|
+
waitUntil.l1Timestamp - delayer.ethereumSlotDuration,
|
|
210
|
+
logger,
|
|
211
|
+
waitUntil.timeoutSeconds,
|
|
212
|
+
)
|
|
204
213
|
: undefined;
|
|
205
214
|
|
|
206
215
|
logger.info(`Delaying tx ${txHash} until ${inspect(waitUntil)}`, {
|
|
@@ -95,6 +95,16 @@ export class EthCheatCodes {
|
|
|
95
95
|
return parseInt(res.timestamp, 16);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Get the timestamp of the pending L1 block — the block that anvil will mine next.
|
|
100
|
+
* Reflects any prior `setNextBlockTimestamp` call or the configured block interval.
|
|
101
|
+
* @returns The pending block timestamp in seconds.
|
|
102
|
+
*/
|
|
103
|
+
public async nextBlockTimestamp(): Promise<number> {
|
|
104
|
+
const res = await this.doRpcCall('eth_getBlockByNumber', ['pending', false]);
|
|
105
|
+
return parseInt(res.timestamp, 16);
|
|
106
|
+
}
|
|
107
|
+
|
|
98
108
|
/**
|
|
99
109
|
* Advance the chain by a number of blocks
|
|
100
110
|
* @param numberOfBlocks - The number of blocks to mine
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
getContract,
|
|
16
16
|
hexToBigInt,
|
|
17
17
|
http,
|
|
18
|
+
keccak256,
|
|
18
19
|
} from 'viem';
|
|
19
20
|
|
|
20
21
|
import { EthCheatCodes } from './eth_cheat_codes.js';
|
|
@@ -178,6 +179,21 @@ export class RollupCheatCodes {
|
|
|
178
179
|
return [timestamp, nextSlot];
|
|
179
180
|
}
|
|
180
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Warps L1 time to the start of an explicit absolute slot. Unlike `advanceSlots(N)` which is
|
|
184
|
+
* relative to the current L1 timestamp at call time and therefore races with real-time
|
|
185
|
+
* progression between query and warp, this lands at the slot regardless of latency.
|
|
186
|
+
*
|
|
187
|
+
* Throws if the requested slot is in the past relative to current L1 time (anvil cannot
|
|
188
|
+
* rewind).
|
|
189
|
+
*/
|
|
190
|
+
public async advanceToSlot(slot: SlotNumber) {
|
|
191
|
+
const timestamp = await this.rollup.read.getTimestampForSlot([BigInt(slot)]);
|
|
192
|
+
await this.ethCheatCodes.warp(Number(timestamp), { silent: true, resetBlockInterval: true });
|
|
193
|
+
this.logger.warn(`Advanced to slot ${slot}`, { timestamp });
|
|
194
|
+
return timestamp;
|
|
195
|
+
}
|
|
196
|
+
|
|
181
197
|
/**
|
|
182
198
|
* Warps time in L1 equivalent to however many slots.
|
|
183
199
|
* @param howMany - The number of slots to advance.
|
|
@@ -269,12 +285,14 @@ export class RollupCheatCodes {
|
|
|
269
285
|
});
|
|
270
286
|
}
|
|
271
287
|
|
|
272
|
-
public insertOutbox(epoch: EpochNumber, outHash: bigint) {
|
|
288
|
+
public insertOutbox(epoch: EpochNumber, numCheckpointsInEpoch: number, outHash: bigint) {
|
|
273
289
|
return this.ethCheatCodes.execWithPausedAnvil(async () => {
|
|
274
290
|
const outboxAddress = await this.rollup.read.getOutbox();
|
|
275
|
-
const epochRootSlot = OutboxContract.getEpochRootStorageSlot(epoch);
|
|
291
|
+
const epochRootSlot = OutboxContract.getEpochRootStorageSlot(epoch, numCheckpointsInEpoch);
|
|
276
292
|
await this.ethCheatCodes.store(EthAddress.fromString(outboxAddress), epochRootSlot, outHash);
|
|
277
|
-
this.logger.warn(
|
|
293
|
+
this.logger.warn(
|
|
294
|
+
`Advanced outbox to epoch ${epoch} numCheckpointsInEpoch ${numCheckpointsInEpoch} with out hash ${outHash}`,
|
|
295
|
+
);
|
|
278
296
|
});
|
|
279
297
|
}
|
|
280
298
|
|
|
@@ -323,7 +341,8 @@ export class RollupCheatCodes {
|
|
|
323
341
|
}
|
|
324
342
|
|
|
325
343
|
/**
|
|
326
|
-
* Directly updates proving cost per mana.
|
|
344
|
+
* Directly updates proving cost per mana. Throws if the on-chain tx reverts
|
|
345
|
+
* (e.g. rate-limit cooldown, step cap, or floor) instead of silently succeeding.
|
|
327
346
|
* @param ethValue - The new proving cost per mana in ETH
|
|
328
347
|
*/
|
|
329
348
|
public async setProvingCostPerMana(ethValue: bigint) {
|
|
@@ -333,8 +352,37 @@ export class RollupCheatCodes {
|
|
|
333
352
|
chain: this.client.chain,
|
|
334
353
|
gasLimit: 1000000n,
|
|
335
354
|
});
|
|
336
|
-
await this.client.waitForTransactionReceipt({ hash });
|
|
355
|
+
const receipt = await this.client.waitForTransactionReceipt({ hash });
|
|
356
|
+
if (receipt.status !== 'success') {
|
|
357
|
+
throw new Error(
|
|
358
|
+
`setProvingCostPerMana(${ethValue}) reverted on L1 (tx ${hash}). ` +
|
|
359
|
+
`Likely FeeLib rate-limit (30-day cooldown or 1.5x step cap); ` +
|
|
360
|
+
`use clearProvingCostCooldown() between successive updates.`,
|
|
361
|
+
);
|
|
362
|
+
}
|
|
337
363
|
this.logger.warn(`Updated proving cost per mana to ${ethValue}`);
|
|
338
364
|
});
|
|
339
365
|
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Resets the 30-day proving-cost update cooldown enforced by FeeLib.updateProvingCostPerMana
|
|
369
|
+
* by zeroing `FeeStore.provingCostLastUpdate` directly in contract storage. Use between
|
|
370
|
+
* successive setProvingCostPerMana / bumpProvingCostPerMana calls so the later update can
|
|
371
|
+
* land instead of reverting. Does not touch L1 time, so PXE/tx-expiration state stays intact.
|
|
372
|
+
*
|
|
373
|
+
* @note This is tightly coupled to the `FeeStore` layout in
|
|
374
|
+
* l1-contracts/src/core/libraries/rollup/FeeLib.sol:
|
|
375
|
+
* slot + 0: CompressedFeeConfig config (uint256)
|
|
376
|
+
* slot + 1: L1GasOracleValues l1GasOracleValues (14+14+4 bytes, packed)
|
|
377
|
+
* slot + 2: uint64 provingCostLastUpdate (only member — zeroing the slot is safe)
|
|
378
|
+
* If the struct layout changes, update the offset below.
|
|
379
|
+
*/
|
|
380
|
+
public async clearProvingCostCooldown() {
|
|
381
|
+
const feeStoreBaseSlot = hexToBigInt(keccak256(Buffer.from('aztec.fee.storage', 'utf-8')));
|
|
382
|
+
const provingCostLastUpdateSlot = feeStoreBaseSlot + 2n;
|
|
383
|
+
await this.ethCheatCodes.store(EthAddress.fromString(this.rollup.address), provingCostLastUpdateSlot, 0n, {
|
|
384
|
+
silent: true,
|
|
385
|
+
});
|
|
386
|
+
this.logger.warn(`Cleared proving-cost update cooldown`);
|
|
387
|
+
}
|
|
340
388
|
}
|
package/src/utils.ts
CHANGED
|
@@ -27,8 +27,8 @@ export interface L2Claim {
|
|
|
27
27
|
export class FormattedViemError extends Error {
|
|
28
28
|
metaMessages?: any[];
|
|
29
29
|
|
|
30
|
-
constructor(message: string, metaMessages?: any[]) {
|
|
31
|
-
super(message);
|
|
30
|
+
constructor(message: string, metaMessages?: any[], options?: ErrorOptions) {
|
|
31
|
+
super(message, options);
|
|
32
32
|
this.name = 'FormattedViemError';
|
|
33
33
|
this.metaMessages = metaMessages;
|
|
34
34
|
}
|
|
@@ -197,6 +197,8 @@ export function formatViemError(error: any, abi: Abi = ErrorsAbi): FormattedViem
|
|
|
197
197
|
return error;
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
+
const originalError = error;
|
|
201
|
+
|
|
200
202
|
// First try to decode as a custom error using the ABI
|
|
201
203
|
try {
|
|
202
204
|
const data = getNestedErrorData(error);
|
|
@@ -207,7 +209,9 @@ export function formatViemError(error: any, abi: Abi = ErrorsAbi): FormattedViem
|
|
|
207
209
|
data: data as Hex,
|
|
208
210
|
});
|
|
209
211
|
if (decoded) {
|
|
210
|
-
return new FormattedViemError(`${decoded.errorName}(${decoded.args?.join(', ') ?? ''})`, error?.metaMessages
|
|
212
|
+
return new FormattedViemError(`${decoded.errorName}(${decoded.args?.join(', ') ?? ''})`, error?.metaMessages, {
|
|
213
|
+
cause: originalError,
|
|
214
|
+
});
|
|
211
215
|
}
|
|
212
216
|
}
|
|
213
217
|
|
|
@@ -224,7 +228,7 @@ export function formatViemError(error: any, abi: Abi = ErrorsAbi): FormattedViem
|
|
|
224
228
|
revertError.metaMessages && revertError.metaMessages?.length > 1
|
|
225
229
|
? revertError.metaMessages[1].trimStart()
|
|
226
230
|
: '';
|
|
227
|
-
return new FormattedViemError(`${errorName}${args}`, error?.metaMessages);
|
|
231
|
+
return new FormattedViemError(`${errorName}${args}`, error?.metaMessages, { cause: originalError });
|
|
228
232
|
}
|
|
229
233
|
}
|
|
230
234
|
} catch {
|
|
@@ -247,10 +251,12 @@ export function formatViemError(error: any, abi: Abi = ErrorsAbi): FormattedViem
|
|
|
247
251
|
|
|
248
252
|
// If it's a regular Error instance, return it with its message
|
|
249
253
|
if (error instanceof Error) {
|
|
250
|
-
return new FormattedViemError(truncateErrorMessage(error.message), (error as any)?.metaMessages
|
|
254
|
+
return new FormattedViemError(truncateErrorMessage(error.message), (error as any)?.metaMessages, {
|
|
255
|
+
cause: originalError,
|
|
256
|
+
});
|
|
251
257
|
}
|
|
252
258
|
|
|
253
|
-
return new FormattedViemError(truncateErrorMessage(String(error)));
|
|
259
|
+
return new FormattedViemError(truncateErrorMessage(String(error)), undefined, { cause: originalError });
|
|
254
260
|
}
|
|
255
261
|
|
|
256
262
|
function stripAbis(obj: any) {
|
|
@@ -276,6 +282,24 @@ function stripAbis(obj: any) {
|
|
|
276
282
|
});
|
|
277
283
|
}
|
|
278
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Best-effort decode of a raw revert payload (`0x...`) against an ABI.
|
|
287
|
+
* Returns a human-readable `ErrorName(arg1, arg2, ...)` string, or `undefined` if the selector
|
|
288
|
+
* is unknown or the payload is empty. Use to surface decoded error names alongside the raw
|
|
289
|
+
* payload in log lines for operators.
|
|
290
|
+
*/
|
|
291
|
+
export function tryDecodeRevertReason(data: Hex | undefined, abi: Abi): string | undefined {
|
|
292
|
+
if (!data || data === '0x') {
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
const decoded = decodeErrorResult({ abi, data });
|
|
297
|
+
return `${decoded.errorName}(${decoded.args?.join(', ') ?? ''})`;
|
|
298
|
+
} catch {
|
|
299
|
+
return undefined;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
279
303
|
export function tryGetCustomErrorName(err: any) {
|
|
280
304
|
try {
|
|
281
305
|
// See https://viem.sh/docs/contract/simulateContract#handling-custom-errors
|