@aztec/ethereum 0.0.0-test.0

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 (118) hide show
  1. package/README.md +3 -0
  2. package/dest/chain.d.ts +25 -0
  3. package/dest/chain.d.ts.map +1 -0
  4. package/dest/chain.js +53 -0
  5. package/dest/client.d.ts +16 -0
  6. package/dest/client.d.ts.map +1 -0
  7. package/dest/client.js +31 -0
  8. package/dest/config.d.ts +39 -0
  9. package/dest/config.d.ts.map +1 -0
  10. package/dest/config.js +70 -0
  11. package/dest/constants.d.ts +4 -0
  12. package/dest/constants.d.ts.map +1 -0
  13. package/dest/constants.js +2 -0
  14. package/dest/contracts/empire_base.d.ts +13 -0
  15. package/dest/contracts/empire_base.d.ts.map +1 -0
  16. package/dest/contracts/empire_base.js +11 -0
  17. package/dest/contracts/fee_juice.d.ts +15 -0
  18. package/dest/contracts/fee_juice.d.ts.map +1 -0
  19. package/dest/contracts/fee_juice.js +52 -0
  20. package/dest/contracts/forwarder.d.ts +24 -0
  21. package/dest/contracts/forwarder.d.ts.map +1 -0
  22. package/dest/contracts/forwarder.js +101 -0
  23. package/dest/contracts/governance.d.ts +79 -0
  24. package/dest/contracts/governance.d.ts.map +1 -0
  25. package/dest/contracts/governance.js +247 -0
  26. package/dest/contracts/governance_proposer.d.ts +28 -0
  27. package/dest/contracts/governance_proposer.d.ts.map +1 -0
  28. package/dest/contracts/governance_proposer.js +82 -0
  29. package/dest/contracts/index.d.ts +9 -0
  30. package/dest/contracts/index.d.ts.map +1 -0
  31. package/dest/contracts/index.js +8 -0
  32. package/dest/contracts/registry.d.ts +24 -0
  33. package/dest/contracts/registry.d.ts.map +1 -0
  34. package/dest/contracts/registry.js +85 -0
  35. package/dest/contracts/rollup.d.ts +92 -0
  36. package/dest/contracts/rollup.d.ts.map +1 -0
  37. package/dest/contracts/rollup.js +234 -0
  38. package/dest/contracts/slashing_proposer.d.ts +21 -0
  39. package/dest/contracts/slashing_proposer.d.ts.map +1 -0
  40. package/dest/contracts/slashing_proposer.js +47 -0
  41. package/dest/deploy_l1_contracts.d.ts +21210 -0
  42. package/dest/deploy_l1_contracts.d.ts.map +1 -0
  43. package/dest/deploy_l1_contracts.js +687 -0
  44. package/dest/eth_cheat_codes.d.ts +147 -0
  45. package/dest/eth_cheat_codes.d.ts.map +1 -0
  46. package/dest/eth_cheat_codes.js +303 -0
  47. package/dest/index.d.ts +14 -0
  48. package/dest/index.d.ts.map +1 -0
  49. package/dest/index.js +13 -0
  50. package/dest/l1_contract_addresses.d.ts +57 -0
  51. package/dest/l1_contract_addresses.d.ts.map +1 -0
  52. package/dest/l1_contract_addresses.js +97 -0
  53. package/dest/l1_reader.d.ts +16 -0
  54. package/dest/l1_reader.d.ts.map +1 -0
  55. package/dest/l1_reader.js +27 -0
  56. package/dest/l1_tx_utils.d.ts +192 -0
  57. package/dest/l1_tx_utils.d.ts.map +1 -0
  58. package/dest/l1_tx_utils.js +641 -0
  59. package/dest/l1_tx_utils_with_blobs.d.ts +12 -0
  60. package/dest/l1_tx_utils_with_blobs.d.ts.map +1 -0
  61. package/dest/l1_tx_utils_with_blobs.js +64 -0
  62. package/dest/queries.d.ts +12 -0
  63. package/dest/queries.d.ts.map +1 -0
  64. package/dest/queries.js +35 -0
  65. package/dest/test/delayed_tx_utils.d.ts +8 -0
  66. package/dest/test/delayed_tx_utils.d.ts.map +1 -0
  67. package/dest/test/delayed_tx_utils.js +21 -0
  68. package/dest/test/eth_cheat_codes_with_state.d.ts +18 -0
  69. package/dest/test/eth_cheat_codes_with_state.d.ts.map +1 -0
  70. package/dest/test/eth_cheat_codes_with_state.js +34 -0
  71. package/dest/test/index.d.ts +6 -0
  72. package/dest/test/index.d.ts.map +1 -0
  73. package/dest/test/index.js +5 -0
  74. package/dest/test/start_anvil.d.ts +12 -0
  75. package/dest/test/start_anvil.d.ts.map +1 -0
  76. package/dest/test/start_anvil.js +46 -0
  77. package/dest/test/tx_delayer.d.ts +25 -0
  78. package/dest/test/tx_delayer.d.ts.map +1 -0
  79. package/dest/test/tx_delayer.js +116 -0
  80. package/dest/test/upgrade_utils.d.ts +11 -0
  81. package/dest/test/upgrade_utils.d.ts.map +1 -0
  82. package/dest/test/upgrade_utils.js +104 -0
  83. package/dest/types.d.ts +14 -0
  84. package/dest/types.d.ts.map +1 -0
  85. package/dest/types.js +1 -0
  86. package/dest/utils.d.ts +24 -0
  87. package/dest/utils.d.ts.map +1 -0
  88. package/dest/utils.js +209 -0
  89. package/package.json +98 -0
  90. package/src/chain.ts +71 -0
  91. package/src/client.ts +58 -0
  92. package/src/config.ts +103 -0
  93. package/src/constants.ts +4 -0
  94. package/src/contracts/empire_base.ts +19 -0
  95. package/src/contracts/fee_juice.ts +43 -0
  96. package/src/contracts/forwarder.ts +132 -0
  97. package/src/contracts/governance.ts +285 -0
  98. package/src/contracts/governance_proposer.ts +82 -0
  99. package/src/contracts/index.ts +8 -0
  100. package/src/contracts/registry.ts +106 -0
  101. package/src/contracts/rollup.ts +274 -0
  102. package/src/contracts/slashing_proposer.ts +51 -0
  103. package/src/deploy_l1_contracts.ts +948 -0
  104. package/src/eth_cheat_codes.ts +314 -0
  105. package/src/index.ts +13 -0
  106. package/src/l1_contract_addresses.ts +109 -0
  107. package/src/l1_reader.ts +42 -0
  108. package/src/l1_tx_utils.ts +847 -0
  109. package/src/l1_tx_utils_with_blobs.ts +86 -0
  110. package/src/queries.ts +58 -0
  111. package/src/test/delayed_tx_utils.ts +24 -0
  112. package/src/test/eth_cheat_codes_with_state.ts +38 -0
  113. package/src/test/index.ts +5 -0
  114. package/src/test/start_anvil.ts +52 -0
  115. package/src/test/tx_delayer.ts +163 -0
  116. package/src/test/upgrade_utils.ts +100 -0
  117. package/src/types.ts +33 -0
  118. package/src/utils.ts +276 -0
@@ -0,0 +1,132 @@
1
+ import { toHex } from '@aztec/foundation/bigint-buffer';
2
+ import type { Logger } from '@aztec/foundation/log';
3
+ import { ForwarderAbi, ForwarderBytecode } from '@aztec/l1-artifacts';
4
+
5
+ import {
6
+ type EncodeFunctionDataParameters,
7
+ type GetContractReturnType,
8
+ type Hex,
9
+ encodeFunctionData,
10
+ getContract,
11
+ } from 'viem';
12
+
13
+ import { deployL1Contract } from '../deploy_l1_contracts.js';
14
+ import type { L1BlobInputs, L1GasConfig, L1TxRequest, L1TxUtils } from '../l1_tx_utils.js';
15
+ import type { L1Clients, ViemPublicClient, ViemWalletClient } from '../types.js';
16
+ import { RollupContract } from './rollup.js';
17
+
18
+ export class ForwarderContract {
19
+ private readonly forwarder: GetContractReturnType<typeof ForwarderAbi, ViemPublicClient>;
20
+
21
+ constructor(public readonly client: L1Clients['publicClient'], address: Hex, public readonly rollupAddress: Hex) {
22
+ this.forwarder = getContract({ address, abi: ForwarderAbi, client });
23
+ }
24
+
25
+ static async create(
26
+ owner: Hex,
27
+ walletClient: ViemWalletClient,
28
+ publicClient: ViemPublicClient,
29
+ logger: Logger,
30
+ rollupAddress: Hex,
31
+ ) {
32
+ logger.info('Deploying forwarder contract');
33
+
34
+ const { address, txHash } = await deployL1Contract(
35
+ walletClient,
36
+ publicClient,
37
+ ForwarderAbi,
38
+ ForwarderBytecode,
39
+ [owner],
40
+ owner,
41
+ undefined,
42
+ logger,
43
+ );
44
+
45
+ if (txHash) {
46
+ await publicClient.waitForTransactionReceipt({ hash: txHash });
47
+ }
48
+
49
+ logger.info(`Forwarder contract deployed at ${address} with owner ${owner}`);
50
+
51
+ return new ForwarderContract(publicClient, address.toString(), rollupAddress);
52
+ }
53
+
54
+ public getAddress() {
55
+ return this.forwarder.address;
56
+ }
57
+
58
+ public async forward(
59
+ requests: L1TxRequest[],
60
+ l1TxUtils: L1TxUtils,
61
+ gasConfig: L1GasConfig | undefined,
62
+ blobConfig: L1BlobInputs | undefined,
63
+ logger: Logger,
64
+ ) {
65
+ requests = requests.filter(request => request.to !== null);
66
+ const toArgs = requests.map(request => request.to!);
67
+ const dataArgs = requests.map(request => request.data!);
68
+ const forwarderFunctionData: EncodeFunctionDataParameters<typeof ForwarderAbi, 'forward'> = {
69
+ abi: ForwarderAbi,
70
+ functionName: 'forward',
71
+ args: [toArgs, dataArgs],
72
+ };
73
+ const encodedForwarderData = encodeFunctionData(forwarderFunctionData);
74
+
75
+ const { receipt, gasPrice } = await l1TxUtils.sendAndMonitorTransaction(
76
+ {
77
+ to: this.forwarder.address,
78
+ data: encodedForwarderData,
79
+ },
80
+ gasConfig,
81
+ blobConfig,
82
+ );
83
+
84
+ if (receipt.status === 'success') {
85
+ const stats = await l1TxUtils.getTransactionStats(receipt.transactionHash);
86
+ return { receipt, gasPrice, stats };
87
+ } else {
88
+ logger.error('Forwarder transaction failed', undefined, { receipt });
89
+
90
+ const args = {
91
+ ...forwarderFunctionData,
92
+ address: this.forwarder.address,
93
+ };
94
+
95
+ let errorMsg: string | undefined;
96
+
97
+ if (blobConfig) {
98
+ const maxFeePerBlobGas = blobConfig.maxFeePerBlobGas ?? gasPrice.maxFeePerBlobGas;
99
+ if (maxFeePerBlobGas === undefined) {
100
+ errorMsg = 'maxFeePerBlobGas is required to get the error message';
101
+ } else {
102
+ logger.debug('Trying to get error from reverted tx with blob config');
103
+ errorMsg = await l1TxUtils.tryGetErrorFromRevertedTx(
104
+ encodedForwarderData,
105
+ args,
106
+ {
107
+ blobs: blobConfig.blobs,
108
+ kzg: blobConfig.kzg,
109
+ maxFeePerBlobGas,
110
+ },
111
+ [
112
+ {
113
+ address: this.rollupAddress,
114
+ stateDiff: [
115
+ {
116
+ slot: toHex(RollupContract.checkBlobStorageSlot, true),
117
+ value: toHex(0n, true),
118
+ },
119
+ ],
120
+ },
121
+ ],
122
+ );
123
+ }
124
+ } else {
125
+ logger.debug('Trying to get error from reverted tx without blob config');
126
+ errorMsg = await l1TxUtils.tryGetErrorFromRevertedTx(encodedForwarderData, args, undefined, []);
127
+ }
128
+
129
+ return { receipt, gasPrice, errorMsg };
130
+ }
131
+ }
132
+ }
@@ -0,0 +1,285 @@
1
+ import { EthAddress } from '@aztec/foundation/eth-address';
2
+ import type { Logger } from '@aztec/foundation/log';
3
+ import { sleep } from '@aztec/foundation/sleep';
4
+ import { GovernanceAbi } from '@aztec/l1-artifacts';
5
+
6
+ import {
7
+ type EncodeFunctionDataParameters,
8
+ type GetContractReturnType,
9
+ type Hex,
10
+ encodeFunctionData,
11
+ getContract,
12
+ } from 'viem';
13
+
14
+ import type { L1ContractAddresses } from '../l1_contract_addresses.js';
15
+ import { L1TxUtils } from '../l1_tx_utils.js';
16
+ import type { ViemPublicClient, ViemWalletClient } from '../types.js';
17
+ import { GovernanceProposerContract } from './governance_proposer.js';
18
+
19
+ export type L1GovernanceContractAddresses = Pick<
20
+ L1ContractAddresses,
21
+ 'governanceAddress' | 'rollupAddress' | 'registryAddress' | 'governanceProposerAddress'
22
+ >;
23
+
24
+ // NOTE: Must be kept in sync with DataStructures.ProposalState in l1-contracts
25
+ export enum ProposalState {
26
+ Pending,
27
+ Active,
28
+ Queued,
29
+ Executable,
30
+ Rejected,
31
+ Executed,
32
+ Dropped,
33
+ Expired,
34
+ }
35
+
36
+ export class GovernanceContract {
37
+ private readonly publicGovernance: GetContractReturnType<typeof GovernanceAbi, ViemPublicClient>;
38
+ private readonly walletGovernance: GetContractReturnType<typeof GovernanceAbi, ViemWalletClient> | undefined;
39
+
40
+ constructor(
41
+ address: Hex,
42
+ public readonly publicClient: ViemPublicClient,
43
+ public readonly walletClient: ViemWalletClient | undefined,
44
+ ) {
45
+ this.publicGovernance = getContract({ address, abi: GovernanceAbi, client: publicClient });
46
+ this.walletGovernance = walletClient
47
+ ? getContract({ address, abi: GovernanceAbi, client: walletClient })
48
+ : undefined;
49
+ }
50
+
51
+ public get address() {
52
+ return EthAddress.fromString(this.publicGovernance.address);
53
+ }
54
+
55
+ public async getProposer() {
56
+ const governanceProposerAddress = EthAddress.fromString(await this.publicGovernance.read.governanceProposer());
57
+ return new GovernanceProposerContract(this.publicClient, governanceProposerAddress.toString());
58
+ }
59
+
60
+ public async getGovernanceAddresses(): Promise<L1GovernanceContractAddresses> {
61
+ const governanceProposer = await this.getProposer();
62
+ const [rollupAddress, registryAddress] = await Promise.all([
63
+ governanceProposer.getRollupAddress(),
64
+ governanceProposer.getRegistryAddress(),
65
+ ]);
66
+ return {
67
+ governanceAddress: this.address,
68
+ rollupAddress,
69
+ registryAddress,
70
+ governanceProposerAddress: governanceProposer.address,
71
+ };
72
+ }
73
+
74
+ public getProposal(proposalId: bigint) {
75
+ return this.publicGovernance.read.getProposal([proposalId]);
76
+ }
77
+
78
+ public async getProposalState(proposalId: bigint): Promise<ProposalState> {
79
+ const state = await this.publicGovernance.read.getProposalState([proposalId]);
80
+ if (state < 0 || state > ProposalState.Expired) {
81
+ throw new Error(`Invalid proposal state: ${state}`);
82
+ }
83
+ return state as ProposalState;
84
+ }
85
+
86
+ private assertWalletGovernance(): NonNullable<typeof this.walletGovernance> {
87
+ if (!this.walletGovernance) {
88
+ throw new Error('Wallet client is required for this operation');
89
+ }
90
+ return this.walletGovernance;
91
+ }
92
+
93
+ public async deposit(onBehalfOf: Hex, amount: bigint) {
94
+ const walletGovernance = this.assertWalletGovernance();
95
+ const depositTx = await walletGovernance.write.deposit([onBehalfOf, amount]);
96
+ await this.publicClient.waitForTransactionReceipt({ hash: depositTx });
97
+ }
98
+
99
+ public async proposeWithLock({
100
+ payloadAddress,
101
+ withdrawAddress,
102
+ }: {
103
+ payloadAddress: Hex;
104
+ withdrawAddress: Hex;
105
+ }): Promise<number> {
106
+ const walletGovernance = this.assertWalletGovernance();
107
+ const proposeTx = await walletGovernance.write.proposeWithLock([payloadAddress, withdrawAddress]);
108
+ const receipt = await this.publicClient.waitForTransactionReceipt({ hash: proposeTx });
109
+ if (receipt.status !== 'success') {
110
+ throw new Error(`Proposal failed: ${receipt.status}`);
111
+ }
112
+
113
+ const proposalId = Number(receipt.logs[1].topics[1]);
114
+ return proposalId;
115
+ }
116
+
117
+ public async awaitProposalActive({ proposalId, logger }: { proposalId: bigint; logger: Logger }) {
118
+ const state = await this.getProposalState(proposalId);
119
+ if (state === ProposalState.Active) {
120
+ return;
121
+ } else if (state !== ProposalState.Pending) {
122
+ throw new Error(`Proposal ${proposalId} is in state [${state}]: it will never be active`);
123
+ } else {
124
+ const proposal = await this.getProposal(proposalId);
125
+ const startOfActive = proposal.creation + proposal.config.votingDelay;
126
+ const block = await this.publicClient.getBlock();
127
+ // Add 12 seconds to the time to make sure we don't vote too early
128
+ const secondsToActive = Number(startOfActive - block.timestamp) + 12;
129
+ const now = new Date();
130
+ logger.info(`
131
+ The time is ${now.toISOString()}.
132
+ The proposal will be active at ${new Date(Number(startOfActive) * 1000).toISOString()}.
133
+ Waiting ${secondsToActive} seconds for proposal to be active.
134
+ `);
135
+ await sleep(secondsToActive * 1000);
136
+ }
137
+ }
138
+
139
+ public async awaitProposalExecutable({ proposalId, logger }: { proposalId: bigint; logger: Logger }) {
140
+ const state = await this.getProposalState(proposalId);
141
+ if (state === ProposalState.Executable) {
142
+ return;
143
+ } else if (
144
+ ![ProposalState.Pending, ProposalState.Active, ProposalState.Queued, ProposalState.Executable].includes(state)
145
+ ) {
146
+ throw new Error(`Proposal ${proposalId} is in state [${state}]: it will never be executable`);
147
+ } else {
148
+ const proposal = await this.getProposal(proposalId);
149
+ const startOfExecutable =
150
+ proposal.creation +
151
+ proposal.config.votingDelay +
152
+ proposal.config.votingDuration +
153
+ proposal.config.executionDelay;
154
+ const block = await this.publicClient.getBlock();
155
+ const secondsToExecutable = Number(startOfExecutable - block.timestamp) + 12;
156
+ const now = new Date();
157
+ logger.info(`
158
+ The time is ${now.toISOString()}.
159
+ The proposal will be executable at ${new Date(Number(startOfExecutable) * 1000).toISOString()}.
160
+ Waiting ${secondsToExecutable} seconds for proposal to be executable.
161
+ `);
162
+ await sleep(secondsToExecutable * 1000);
163
+ }
164
+ }
165
+
166
+ public async getPower(): Promise<bigint> {
167
+ const walletGovernance = this.assertWalletGovernance();
168
+ const now = await this.publicClient.getBlock();
169
+ return walletGovernance.read.powerAt([this.walletClient!.account.address, now.timestamp]);
170
+ }
171
+
172
+ public async vote({
173
+ proposalId,
174
+ voteAmount,
175
+ inFavor,
176
+ retries = 10,
177
+ logger,
178
+ }: {
179
+ proposalId: bigint;
180
+ voteAmount: bigint | undefined;
181
+ inFavor: boolean;
182
+ retries: number;
183
+ logger: Logger;
184
+ }) {
185
+ const walletGovernance = this.assertWalletGovernance();
186
+ const l1TxUtils = new L1TxUtils(this.publicClient, this.walletClient!, logger);
187
+ const retryDelaySeconds = 12;
188
+
189
+ voteAmount = voteAmount ?? (await this.getPower());
190
+
191
+ let success = false;
192
+ for (let i = 0; i < retries; i++) {
193
+ try {
194
+ const voteFunctionData: EncodeFunctionDataParameters<typeof GovernanceAbi, 'vote'> = {
195
+ abi: GovernanceAbi,
196
+ functionName: 'vote',
197
+ args: [proposalId, voteAmount, inFavor],
198
+ };
199
+ const encodedVoteData = encodeFunctionData(voteFunctionData);
200
+
201
+ const { receipt } = await l1TxUtils.sendAndMonitorTransaction({
202
+ to: walletGovernance.address,
203
+ data: encodedVoteData,
204
+ });
205
+
206
+ if (receipt.status === 'success') {
207
+ success = true;
208
+ break;
209
+ } else {
210
+ const args = {
211
+ ...voteFunctionData,
212
+ address: walletGovernance.address,
213
+ };
214
+ const errorMsg = await l1TxUtils.tryGetErrorFromRevertedTx(encodedVoteData, args, undefined, []);
215
+ logger.error(`Error voting on proposal ${proposalId}: ${errorMsg}`);
216
+ }
217
+ } catch (error) {
218
+ logger.error(`Error voting on proposal ${proposalId}: ${error}`);
219
+ }
220
+
221
+ logger.info(`Retrying vote on proposal ${proposalId} in ${retryDelaySeconds} seconds`);
222
+ await sleep(retryDelaySeconds * 1000);
223
+ }
224
+ if (!success) {
225
+ throw new Error(`Failed to vote on proposal ${proposalId} after ${retries} retries`);
226
+ }
227
+ logger.info(`Voted [${inFavor ? 'yea' : 'nay'}] on proposal [${proposalId}]`);
228
+ const proposal = await this.getProposal(proposalId);
229
+ logger.info(`Proposal [${proposalId}] has state [${proposal.state}]`);
230
+ logger.info(`Proposal [${proposalId}] has summedBallot yea [${proposal.summedBallot.yea}]`);
231
+ logger.info(`Proposal [${proposalId}] has summedBallot nea [${proposal.summedBallot.nea}]`);
232
+ }
233
+
234
+ public async executeProposal({
235
+ proposalId,
236
+ retries = 10,
237
+ logger,
238
+ }: {
239
+ proposalId: bigint;
240
+ retries: number;
241
+ logger: Logger;
242
+ }) {
243
+ const walletGovernance = this.assertWalletGovernance();
244
+ const l1TxUtils = new L1TxUtils(this.publicClient, this.walletClient!, logger);
245
+ const retryDelaySeconds = 12;
246
+ let success = false;
247
+ for (let i = 0; i < retries; i++) {
248
+ try {
249
+ const executeFunctionData: EncodeFunctionDataParameters<typeof GovernanceAbi, 'execute'> = {
250
+ abi: GovernanceAbi,
251
+ functionName: 'execute',
252
+ args: [proposalId],
253
+ };
254
+ const encodedExecuteData = encodeFunctionData(executeFunctionData);
255
+
256
+ const { receipt } = await l1TxUtils.sendAndMonitorTransaction({
257
+ to: walletGovernance.address,
258
+ data: encodedExecuteData,
259
+ });
260
+
261
+ if (receipt.status === 'success') {
262
+ success = true;
263
+ break;
264
+ } else {
265
+ const args = {
266
+ ...executeFunctionData,
267
+ address: walletGovernance.address,
268
+ };
269
+ const errorMsg = await l1TxUtils.tryGetErrorFromRevertedTx(encodedExecuteData, args, undefined, []);
270
+ logger.error(`Error executing proposal ${proposalId}: ${errorMsg}`);
271
+ }
272
+ } catch (error) {
273
+ logger.error(`Error executing proposal ${proposalId}: ${error}`);
274
+ }
275
+
276
+ logger.info(`Retrying execute proposal ${proposalId} in ${retryDelaySeconds} seconds`);
277
+ await sleep(retryDelaySeconds * 1000);
278
+ }
279
+ if (!success) {
280
+ throw new Error(`Failed to execute proposal ${proposalId} after ${retries} retries`);
281
+ } else {
282
+ logger.info(`Executed proposal ${proposalId}`);
283
+ }
284
+ }
285
+ }
@@ -0,0 +1,82 @@
1
+ import { memoize } from '@aztec/foundation/decorators';
2
+ import { EthAddress } from '@aztec/foundation/eth-address';
3
+ import { GovernanceProposerAbi } from '@aztec/l1-artifacts';
4
+
5
+ import { type GetContractReturnType, type Hex, type TransactionReceipt, encodeFunctionData, getContract } from 'viem';
6
+
7
+ import type { GasPrice, L1TxRequest, L1TxUtils } from '../l1_tx_utils.js';
8
+ import type { ViemPublicClient } from '../types.js';
9
+ import { type IEmpireBase, encodeVote } from './empire_base.js';
10
+
11
+ export class GovernanceProposerContract implements IEmpireBase {
12
+ private readonly proposer: GetContractReturnType<typeof GovernanceProposerAbi, ViemPublicClient>;
13
+
14
+ constructor(public readonly client: ViemPublicClient, address: Hex) {
15
+ this.proposer = getContract({ address, abi: GovernanceProposerAbi, client });
16
+ }
17
+
18
+ public get address() {
19
+ return EthAddress.fromString(this.proposer.address);
20
+ }
21
+
22
+ public async getRollupAddress() {
23
+ return EthAddress.fromString(await this.proposer.read.getInstance());
24
+ }
25
+
26
+ @memoize
27
+ public async getRegistryAddress() {
28
+ return EthAddress.fromString(await this.proposer.read.REGISTRY());
29
+ }
30
+
31
+ public getQuorumSize(): Promise<bigint> {
32
+ return this.proposer.read.N();
33
+ }
34
+
35
+ public getRoundSize(): Promise<bigint> {
36
+ return this.proposer.read.M();
37
+ }
38
+
39
+ public computeRound(slot: bigint): Promise<bigint> {
40
+ return this.proposer.read.computeRound([slot]);
41
+ }
42
+
43
+ public async getRoundInfo(
44
+ rollupAddress: Hex,
45
+ round: bigint,
46
+ ): Promise<{ lastVote: bigint; leader: Hex; executed: boolean }> {
47
+ const roundInfo = await this.proposer.read.rounds([rollupAddress, round]);
48
+ return {
49
+ lastVote: roundInfo[0],
50
+ leader: roundInfo[1],
51
+ executed: roundInfo[2],
52
+ };
53
+ }
54
+
55
+ public getProposalVotes(rollupAddress: Hex, round: bigint, proposal: Hex): Promise<bigint> {
56
+ return this.proposer.read.yeaCount([rollupAddress, round, proposal]);
57
+ }
58
+
59
+ public createVoteRequest(payload: Hex): L1TxRequest {
60
+ return {
61
+ to: this.address.toString(),
62
+ data: encodeVote(payload),
63
+ };
64
+ }
65
+
66
+ public executeProposal(
67
+ round: bigint,
68
+ l1TxUtils: L1TxUtils,
69
+ ): Promise<{
70
+ receipt: TransactionReceipt;
71
+ gasPrice: GasPrice;
72
+ }> {
73
+ return l1TxUtils.sendAndMonitorTransaction({
74
+ to: this.address.toString(),
75
+ data: encodeFunctionData({
76
+ abi: this.proposer.abi,
77
+ functionName: 'executeProposal',
78
+ args: [round],
79
+ }),
80
+ });
81
+ }
82
+ }
@@ -0,0 +1,8 @@
1
+ export * from './empire_base.js';
2
+ export * from './fee_juice.js';
3
+ export * from './forwarder.js';
4
+ export * from './governance.js';
5
+ export * from './governance_proposer.js';
6
+ export * from './registry.js';
7
+ export * from './rollup.js';
8
+ export * from './slashing_proposer.js';
@@ -0,0 +1,106 @@
1
+ import { EthAddress } from '@aztec/foundation/eth-address';
2
+ import { RegistryAbi } from '@aztec/l1-artifacts/RegistryAbi';
3
+ import { TestERC20Abi } from '@aztec/l1-artifacts/TestERC20Abi';
4
+
5
+ import {
6
+ type Chain,
7
+ type GetContractReturnType,
8
+ type Hex,
9
+ type HttpTransport,
10
+ type PublicClient,
11
+ getContract,
12
+ } from 'viem';
13
+
14
+ import type { L1ContractAddresses } from '../l1_contract_addresses.js';
15
+ import type { L1Clients, ViemPublicClient } from '../types.js';
16
+ import { GovernanceContract } from './governance.js';
17
+ import { RollupContract } from './rollup.js';
18
+
19
+ export class RegistryContract {
20
+ public address: EthAddress;
21
+ private readonly registry: GetContractReturnType<typeof RegistryAbi, PublicClient<HttpTransport, Chain>>;
22
+
23
+ constructor(public readonly client: L1Clients['publicClient'], address: Hex | EthAddress) {
24
+ if (address instanceof EthAddress) {
25
+ address = address.toString();
26
+ }
27
+ this.address = EthAddress.fromString(address);
28
+ this.registry = getContract({ address, abi: RegistryAbi, client });
29
+ }
30
+
31
+ /**
32
+ * Returns the address of the rollup for a given version.
33
+ * @param version - The version of the rollup. 'canonical' can be used to get the canonical address (i.e. the latest version).
34
+ * @returns The address of the rollup. If the rollup is not set for this version, throws an error.
35
+ */
36
+ public async getRollupAddress(version: number | bigint | 'canonical'): Promise<EthAddress> {
37
+ if (version === 'canonical') {
38
+ return this.getCanonicalAddress();
39
+ }
40
+
41
+ if (typeof version === 'number') {
42
+ version = BigInt(version);
43
+ }
44
+
45
+ const snapshot = await this.registry.read.getSnapshot([version]);
46
+ const address = EthAddress.fromString(snapshot.rollup);
47
+ if (address.equals(EthAddress.ZERO)) {
48
+ throw new Error('Rollup address is undefined');
49
+ }
50
+ return address;
51
+ }
52
+
53
+ /**
54
+ * Returns the canonical address of the rollup.
55
+ * @returns The canonical address of the rollup. If the rollup is not set, throws an error.
56
+ */
57
+ public async getCanonicalAddress(): Promise<EthAddress> {
58
+ const snapshot = await this.registry.read.getCurrentSnapshot();
59
+ const address = EthAddress.fromString(snapshot.rollup);
60
+ if (address.equals(EthAddress.ZERO)) {
61
+ throw new Error('Rollup address is undefined');
62
+ }
63
+ return address;
64
+ }
65
+
66
+ public async getGovernanceAddresses(): Promise<
67
+ Pick<L1ContractAddresses, 'governanceProposerAddress' | 'governanceAddress'>
68
+ > {
69
+ const governanceAddress = await this.registry.read.getGovernance();
70
+ const governance = new GovernanceContract(governanceAddress, this.client, undefined);
71
+ const governanceProposer = await governance.getProposer();
72
+ return {
73
+ governanceAddress: governance.address,
74
+ governanceProposerAddress: governanceProposer.address,
75
+ };
76
+ }
77
+
78
+ public static async collectAddresses(
79
+ client: ViemPublicClient,
80
+ registryAddress: Hex | EthAddress,
81
+ rollupVersion: number | bigint | 'canonical',
82
+ ): Promise<L1ContractAddresses> {
83
+ const registry = new RegistryContract(client, registryAddress);
84
+ const governanceAddresses = await registry.getGovernanceAddresses();
85
+ const rollupAddress = await registry.getRollupAddress(rollupVersion);
86
+
87
+ if (rollupAddress === undefined) {
88
+ throw new Error('Rollup address is undefined');
89
+ }
90
+
91
+ const rollup = new RollupContract(client, rollupAddress);
92
+ const addresses = await rollup.getRollupAddresses();
93
+ const feeAsset = getContract({
94
+ address: addresses.feeJuiceAddress.toString(),
95
+ abi: TestERC20Abi,
96
+ client,
97
+ });
98
+ const coinIssuer = await feeAsset.read.owner();
99
+ return {
100
+ registryAddress: registry.address,
101
+ ...governanceAddresses,
102
+ ...addresses,
103
+ coinIssuerAddress: EthAddress.fromString(coinIssuer),
104
+ };
105
+ }
106
+ }