@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
package/dest/utils.js ADDED
@@ -0,0 +1,209 @@
1
+ import { ErrorsAbi } from '@aztec/l1-artifacts';
2
+ import { BaseError, ContractFunctionRevertedError, decodeErrorResult, decodeEventLog } from 'viem';
3
+ export class FormattedViemError extends Error {
4
+ metaMessages;
5
+ constructor(message, metaMessages){
6
+ super(message);
7
+ this.name = 'FormattedViemError';
8
+ this.metaMessages = metaMessages;
9
+ }
10
+ }
11
+ export function extractEvent(logs, address, abi, eventName, filter, logger) {
12
+ const event = tryExtractEvent(logs, address, abi, eventName, filter, logger);
13
+ if (!event) {
14
+ throw new Error(`Failed to find matching event ${eventName} for contract ${address}`);
15
+ }
16
+ return event;
17
+ }
18
+ function tryExtractEvent(logs, address, abi, eventName, filter, logger) {
19
+ for (const log of logs){
20
+ if (log.address.toLowerCase() === address.toLowerCase()) {
21
+ try {
22
+ const decodedEvent = decodeEventLog({
23
+ abi,
24
+ ...log
25
+ });
26
+ if (decodedEvent.eventName === eventName) {
27
+ const matchingEvent = decodedEvent;
28
+ if (!filter || filter(matchingEvent)) {
29
+ return matchingEvent;
30
+ }
31
+ }
32
+ } catch (err) {
33
+ logger?.warn(`Failed to decode event log for contract ${address}: ${err}`);
34
+ }
35
+ }
36
+ }
37
+ }
38
+ export function prettyLogViemErrorMsg(err) {
39
+ if (err instanceof BaseError) {
40
+ const revertError = err.walk((err)=>err instanceof ContractFunctionRevertedError);
41
+ if (revertError instanceof ContractFunctionRevertedError) {
42
+ const errorName = revertError.data?.errorName ?? '';
43
+ const args = revertError.metaMessages && revertError.metaMessages?.length > 1 ? revertError.metaMessages[1].trimStart() : '';
44
+ return `${errorName}${args}`;
45
+ }
46
+ }
47
+ return err?.message ?? err;
48
+ }
49
+ function getNestedErrorData(error) {
50
+ // If nothing, bail
51
+ if (!error) {
52
+ return undefined;
53
+ }
54
+ // If it's an object with a `data` property, return it
55
+ // (Remember to check TS type-safely or cast as needed)
56
+ if (typeof error === 'object' && error !== null && 'data' in error) {
57
+ const possibleData = error.data;
58
+ if (typeof possibleData === 'string' && possibleData.startsWith('0x')) {
59
+ return possibleData;
60
+ }
61
+ }
62
+ // If it has a `cause`, recurse
63
+ if (typeof error === 'object' && error !== null && 'cause' in error) {
64
+ return getNestedErrorData(error.cause);
65
+ }
66
+ // Not found
67
+ return undefined;
68
+ }
69
+ /**
70
+ * Formats a Viem error into a FormattedViemError instance.
71
+ * @param error - The error to format.
72
+ * @param abi - The ABI to use for decoding.
73
+ * @returns A FormattedViemError instance.
74
+ */ export function formatViemError(error, abi = ErrorsAbi) {
75
+ // If error is already a FormattedViemError, return it as is
76
+ if (error instanceof FormattedViemError) {
77
+ return error;
78
+ }
79
+ // First try to decode as a custom error using the ABI
80
+ try {
81
+ const data = getNestedErrorData(error);
82
+ if (data) {
83
+ // Try to decode the error data using the ABI
84
+ const decoded = decodeErrorResult({
85
+ abi,
86
+ data: data
87
+ });
88
+ if (decoded) {
89
+ return new FormattedViemError(`${decoded.errorName}(${decoded.args?.join(', ') ?? ''})`, error?.metaMessages);
90
+ }
91
+ }
92
+ // If it's a BaseError, try to get the custom error through ContractFunctionRevertedError
93
+ if (error instanceof BaseError) {
94
+ const revertError = error.walk((err)=>err instanceof ContractFunctionRevertedError);
95
+ if (revertError instanceof ContractFunctionRevertedError) {
96
+ let errorName = revertError.data?.errorName;
97
+ if (!errorName) {
98
+ errorName = revertError.signature ?? '';
99
+ }
100
+ const args = revertError.metaMessages && revertError.metaMessages?.length > 1 ? revertError.metaMessages[1].trimStart() : '';
101
+ return new FormattedViemError(`${errorName}${args}`, error?.metaMessages);
102
+ }
103
+ }
104
+ } catch (decodeErr) {
105
+ // If decoding fails, we fall back to the original formatting
106
+ }
107
+ // If it's a regular Error instance, return it with its message
108
+ if (error instanceof Error) {
109
+ return error;
110
+ }
111
+ // Original formatting logic for non-custom errors
112
+ const truncateHex = (hex, length = 100)=>{
113
+ if (!hex || typeof hex !== 'string') {
114
+ return hex;
115
+ }
116
+ if (!hex.startsWith('0x')) {
117
+ return hex;
118
+ }
119
+ if (hex.length <= length * 2) {
120
+ return hex;
121
+ }
122
+ return `${hex.slice(0, length)}...${hex.slice(-length)}`;
123
+ };
124
+ const formatRequestBody = (body)=>{
125
+ try {
126
+ const parsed = JSON.parse(body);
127
+ // Recursively process all parameters that might contain hex strings
128
+ const processParams = (obj)=>{
129
+ if (Array.isArray(obj)) {
130
+ return obj.map((item)=>processParams(item));
131
+ }
132
+ if (typeof obj === 'object' && obj !== null) {
133
+ const result = {};
134
+ for (const [key, value] of Object.entries(obj)){
135
+ result[key] = processParams(value);
136
+ }
137
+ return result;
138
+ }
139
+ if (typeof obj === 'string') {
140
+ if (obj.startsWith('0x')) {
141
+ return truncateHex(obj);
142
+ }
143
+ }
144
+ return obj;
145
+ };
146
+ // Process the entire request body
147
+ const processed = processParams(parsed);
148
+ return JSON.stringify(processed, null, 2);
149
+ } catch {
150
+ return body;
151
+ }
152
+ };
153
+ const truncateHexStringsInText = (text)=>{
154
+ const hexRegex = /\b0x[a-fA-F0-9]{10,}/g;
155
+ return text.replace(hexRegex, (hex)=>truncateHex(hex));
156
+ };
157
+ const extractAndFormatRequestBody = (message)=>{
158
+ // First handle Request body JSON
159
+ const requestBodyRegex = /Request body: ({[\s\S]*?})\n/g;
160
+ let result = message.replace(requestBodyRegex, (match, body)=>{
161
+ return `Request body: ${formatRequestBody(body)}\n`;
162
+ });
163
+ // Then handle Arguments section
164
+ const argsRegex = /((?:Request |Estimate Gas )?Arguments:[\s\S]*?(?=\n\n|$))/g;
165
+ result = result.replace(argsRegex, (section)=>{
166
+ const lines = section.split('\n');
167
+ const processedLines = lines.map((line)=>{
168
+ // Check if line contains a colon followed by content
169
+ const colonIndex = line.indexOf(':');
170
+ if (colonIndex !== -1) {
171
+ const [prefix, content] = [
172
+ line.slice(0, colonIndex + 1),
173
+ line.slice(colonIndex + 1).trim()
174
+ ];
175
+ // If content contains a hex string, truncate it
176
+ if (content.includes('0x')) {
177
+ const hexMatches = content.match(/0x[a-fA-F0-9]+/g) || [];
178
+ let processedContent = content;
179
+ hexMatches.forEach((hex)=>{
180
+ processedContent = processedContent.replace(hex, truncateHex(hex));
181
+ });
182
+ return `${prefix} ${processedContent}`;
183
+ }
184
+ }
185
+ return line;
186
+ });
187
+ return processedLines.join('\n');
188
+ });
189
+ // Finally, catch any remaining hex strings in the message
190
+ result = truncateHexStringsInText(result);
191
+ return result;
192
+ };
193
+ const formattedRes = extractAndFormatRequestBody(error?.message || String(error));
194
+ return new FormattedViemError(formattedRes.replace(/\\n/g, '\n'), error?.metaMessages);
195
+ }
196
+ export function tryGetCustomErrorName(err) {
197
+ try {
198
+ // See https://viem.sh/docs/contract/simulateContract#handling-custom-errors
199
+ if (err.name === 'ViemError' || err.name === 'ContractFunctionExecutionError') {
200
+ const baseError = err;
201
+ const revertError = baseError.walk((err)=>err.name === 'ContractFunctionRevertedError');
202
+ if (revertError) {
203
+ return revertError.data?.errorName;
204
+ }
205
+ }
206
+ } catch (_e) {
207
+ return undefined;
208
+ }
209
+ }
package/package.json ADDED
@@ -0,0 +1,98 @@
1
+ {
2
+ "name": "@aztec/ethereum",
3
+ "version": "0.0.0-test.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": "./dest/index.js",
7
+ "./eth-cheatcodes": "./dest/eth_cheat_codes.js",
8
+ "./test": "./dest/test/index.js",
9
+ "./contracts": "./dest/contracts/index.js",
10
+ "./deploy-l1-contracts": "./dest/deploy_l1_contracts.js",
11
+ "./l1-contract-addresses": "./dest/l1_contract_addresses.js",
12
+ "./l1-tx-utils-with-blobs": "./dest/l1_tx_utils_with_blobs.js",
13
+ "./utils": "./dest/utils.js"
14
+ },
15
+ "typedocOptions": {
16
+ "entryPoints": [
17
+ "./src/index.ts"
18
+ ],
19
+ "name": "Ethereum",
20
+ "tsconfig": "./tsconfig.json"
21
+ },
22
+ "scripts": {
23
+ "build": "yarn clean && tsc -b",
24
+ "build:dev": "tsc -b --watch",
25
+ "clean": "rm -rf ./dest .tsbuildinfo",
26
+ "formatting": "run -T prettier --check ./src && run -T eslint ./src",
27
+ "formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src",
28
+ "start:dev": "tsc-watch -p tsconfig.json --onSuccess 'yarn start'",
29
+ "start": "node ./dest/index.js",
30
+ "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}"
31
+ },
32
+ "inherits": [
33
+ "../package.common.json"
34
+ ],
35
+ "dependencies": {
36
+ "@aztec/blob-lib": "0.0.0-test.0",
37
+ "@aztec/foundation": "0.0.0-test.0",
38
+ "@aztec/l1-artifacts": "0.0.0-test.0",
39
+ "@viem/anvil": "^0.0.10",
40
+ "dotenv": "^16.0.3",
41
+ "tslib": "^2.4.0",
42
+ "viem": "2.22.8",
43
+ "zod": "^3.23.8"
44
+ },
45
+ "devDependencies": {
46
+ "@jest/globals": "^29.5.0",
47
+ "@types/jest": "^29.5.0",
48
+ "@types/node": "^18.14.6",
49
+ "@viem/anvil": "^0.0.10",
50
+ "get-port": "^7.1.0",
51
+ "jest": "^29.5.0",
52
+ "lodash.omit": "^4.5.0",
53
+ "ts-node": "^10.9.1",
54
+ "typescript": "^5.0.4"
55
+ },
56
+ "files": [
57
+ "dest",
58
+ "src",
59
+ "!*.test.*"
60
+ ],
61
+ "types": "./dest/index.d.ts",
62
+ "jest": {
63
+ "moduleNameMapper": {
64
+ "^(\\.{1,2}/.*)\\.[cm]?js$": "$1"
65
+ },
66
+ "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$",
67
+ "rootDir": "./src",
68
+ "transform": {
69
+ "^.+\\.tsx?$": [
70
+ "@swc/jest",
71
+ {
72
+ "jsc": {
73
+ "parser": {
74
+ "syntax": "typescript",
75
+ "decorators": true
76
+ },
77
+ "transform": {
78
+ "decoratorVersion": "2022-03"
79
+ }
80
+ }
81
+ }
82
+ ]
83
+ },
84
+ "extensionsToTreatAsEsm": [
85
+ ".ts"
86
+ ],
87
+ "reporters": [
88
+ "default"
89
+ ],
90
+ "testTimeout": 120000,
91
+ "setupFiles": [
92
+ "../../foundation/src/jest/setup.mjs"
93
+ ]
94
+ },
95
+ "engines": {
96
+ "node": ">=18"
97
+ }
98
+ }
package/src/chain.ts ADDED
@@ -0,0 +1,71 @@
1
+ import type { Chain } from 'viem';
2
+ import { foundry } from 'viem/chains';
3
+
4
+ import { AZTEC_TEST_CHAIN_ID } from './constants.js';
5
+
6
+ /**
7
+ * Interface containing the connection and chain properties to interact with a blockchain.
8
+ */
9
+ export interface EthereumChain {
10
+ /**
11
+ * An instance of the viem chain data.
12
+ */
13
+ chainInfo: Chain;
14
+
15
+ /**
16
+ * The list of actual urls to be used.
17
+ */
18
+ rpcUrls: string[];
19
+ }
20
+
21
+ /**
22
+ * Helper function to create an instance of Aztec Chain from an rpc url and chain id.
23
+ * @param rpcUrls - The rpc url of the chain or a chain identifier (e.g. 'http://localhost:8545')
24
+ * @param chainId - The chain id of the chain or a chain identifier (e.g. 1337)
25
+ */
26
+ export function createEthereumChain(rpcUrls: string[], _chainId: number | string): EthereumChain {
27
+ let chainId: number;
28
+ if (typeof _chainId === 'string') {
29
+ chainId = +_chainId;
30
+ } else {
31
+ chainId = _chainId;
32
+ }
33
+ if (chainId) {
34
+ return {
35
+ chainInfo: {
36
+ id: chainId,
37
+ name: 'Ethereum',
38
+ rpcUrls: {
39
+ default: {
40
+ http: rpcUrls,
41
+ },
42
+ },
43
+ nativeCurrency: {
44
+ decimals: 18,
45
+ name: 'Ether',
46
+ symbol: 'ETH',
47
+ },
48
+ },
49
+ rpcUrls,
50
+ };
51
+ } else {
52
+ return {
53
+ chainInfo: foundry,
54
+ rpcUrls,
55
+ };
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Helper function to determine if a chain id is an instance of Anvil
61
+ */
62
+ export function isAnvilTestChain(_chainId: number | string): boolean {
63
+ let chainId: number;
64
+ if (typeof _chainId === 'string') {
65
+ chainId = +_chainId;
66
+ } else {
67
+ chainId = _chainId;
68
+ }
69
+ const testChains = [foundry.id, AZTEC_TEST_CHAIN_ID];
70
+ return testChains.includes(chainId);
71
+ }
package/src/client.ts ADDED
@@ -0,0 +1,58 @@
1
+ import type { Logger } from '@aztec/foundation/log';
2
+ import { retryUntil } from '@aztec/foundation/retry';
3
+
4
+ import { createPublicClient, fallback, http } from 'viem';
5
+
6
+ import { createEthereumChain } from './chain.js';
7
+ import type { ViemPublicClient } from './types.js';
8
+
9
+ type Config = {
10
+ /** The RPC Url of the ethereum host. */
11
+ l1RpcUrls: string[];
12
+ /** The chain ID of the ethereum host. */
13
+ l1ChainId: number;
14
+ /** The polling interval viem uses in ms */
15
+ viemPollingIntervalMS?: number;
16
+ };
17
+
18
+ // TODO: Use these methods to abstract the creation of viem clients.
19
+
20
+ /** Returns a viem public client given the L1 config. */
21
+ export function getPublicClient(config: Config): ViemPublicClient {
22
+ const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
23
+ return createPublicClient({
24
+ chain: chain.chainInfo,
25
+ transport: fallback(config.l1RpcUrls.map(url => http(url))),
26
+ pollingInterval: config.viemPollingIntervalMS,
27
+ });
28
+ }
29
+
30
+ /** Returns a viem public client after waiting for the L1 RPC node to become available. */
31
+ export async function waitForPublicClient(config: Config, logger?: Logger): Promise<ViemPublicClient> {
32
+ const client = getPublicClient(config);
33
+ await waitForRpc(client, config, logger);
34
+ return client;
35
+ }
36
+
37
+ async function waitForRpc(client: ViemPublicClient, config: Config, logger?: Logger) {
38
+ const l1ChainId = await retryUntil(
39
+ async () => {
40
+ let chainId = 0;
41
+ try {
42
+ chainId = await client.getChainId();
43
+ } catch (err) {
44
+ logger?.warn(`Failed to connect to Ethereum node at ${config.l1RpcUrls.join(', ')}. Retrying...`);
45
+ }
46
+ return chainId;
47
+ },
48
+ `L1 RPC url at ${config.l1RpcUrls.join(', ')}`,
49
+ 600,
50
+ 1,
51
+ );
52
+
53
+ if (l1ChainId !== config.l1ChainId) {
54
+ throw new Error(
55
+ `Ethereum node at ${config.l1RpcUrls.join(', ')} has chain ID ${l1ChainId} but expected ${config.l1ChainId}`,
56
+ );
57
+ }
58
+ }
package/src/config.ts ADDED
@@ -0,0 +1,103 @@
1
+ import {
2
+ type ConfigMappingsType,
3
+ bigintConfigHelper,
4
+ getConfigFromMappings,
5
+ numberConfigHelper,
6
+ } from '@aztec/foundation/config';
7
+
8
+ import { type L1TxUtilsConfig, l1TxUtilsConfigMappings } from './l1_tx_utils.js';
9
+
10
+ export type L1ContractsConfig = {
11
+ /** How many seconds an L1 slot lasts. */
12
+ ethereumSlotDuration: number;
13
+ /** How many seconds an L2 slots lasts (must be multiple of ethereum slot duration). */
14
+ aztecSlotDuration: number;
15
+ /** How many L2 slots an epoch lasts. */
16
+ aztecEpochDuration: number;
17
+ /** The target validator committee size. */
18
+ aztecTargetCommitteeSize: number;
19
+ /** The number of L2 slots that we can wait for a proof of an epoch to be produced. */
20
+ aztecProofSubmissionWindow: number;
21
+ /** The minimum stake for a validator. */
22
+ minimumStake: bigint;
23
+ /** The slashing quorum */
24
+ slashingQuorum: number;
25
+ /** The slashing round size */
26
+ slashingRoundSize: number;
27
+ /** Governance proposing quorum */
28
+ governanceProposerQuorum: number;
29
+ /** Governance proposing round size */
30
+ governanceProposerRoundSize: number;
31
+ } & L1TxUtilsConfig;
32
+
33
+ export const DefaultL1ContractsConfig = {
34
+ ethereumSlotDuration: 12,
35
+ aztecSlotDuration: 24,
36
+ aztecEpochDuration: 16,
37
+ aztecTargetCommitteeSize: 48,
38
+ aztecProofSubmissionWindow: 31, // you have a full epoch to submit a proof after the epoch to prove ends
39
+ minimumStake: BigInt(100e18),
40
+ slashingQuorum: 6,
41
+ slashingRoundSize: 10,
42
+ governanceProposerQuorum: 6,
43
+ governanceProposerRoundSize: 10,
44
+ } satisfies L1ContractsConfig;
45
+
46
+ export const l1ContractsConfigMappings: ConfigMappingsType<L1ContractsConfig> = {
47
+ ethereumSlotDuration: {
48
+ env: 'ETHEREUM_SLOT_DURATION',
49
+ description: 'How many seconds an L1 slot lasts.',
50
+ ...numberConfigHelper(DefaultL1ContractsConfig.ethereumSlotDuration),
51
+ },
52
+ aztecSlotDuration: {
53
+ env: 'AZTEC_SLOT_DURATION',
54
+ description: 'How many seconds an L2 slots lasts (must be multiple of ethereum slot duration).',
55
+ ...numberConfigHelper(DefaultL1ContractsConfig.aztecSlotDuration),
56
+ },
57
+ aztecEpochDuration: {
58
+ env: 'AZTEC_EPOCH_DURATION',
59
+ description: `How many L2 slots an epoch lasts (maximum AZTEC_MAX_EPOCH_DURATION).`,
60
+ ...numberConfigHelper(DefaultL1ContractsConfig.aztecEpochDuration),
61
+ },
62
+ aztecTargetCommitteeSize: {
63
+ env: 'AZTEC_TARGET_COMMITTEE_SIZE',
64
+ description: 'The target validator committee size.',
65
+ ...numberConfigHelper(DefaultL1ContractsConfig.aztecTargetCommitteeSize),
66
+ },
67
+ aztecProofSubmissionWindow: {
68
+ env: 'AZTEC_PROOF_SUBMISSION_WINDOW',
69
+ description:
70
+ 'The number of L2 slots that a proof for an epoch can be submitted in, starting from the beginning of the epoch.',
71
+ ...numberConfigHelper(DefaultL1ContractsConfig.aztecProofSubmissionWindow),
72
+ },
73
+ minimumStake: {
74
+ env: 'AZTEC_MINIMUM_STAKE',
75
+ description: 'The minimum stake for a validator.',
76
+ ...bigintConfigHelper(DefaultL1ContractsConfig.minimumStake),
77
+ },
78
+ slashingQuorum: {
79
+ env: 'AZTEC_SLASHING_QUORUM',
80
+ description: 'The slashing quorum',
81
+ ...numberConfigHelper(DefaultL1ContractsConfig.slashingQuorum),
82
+ },
83
+ slashingRoundSize: {
84
+ env: 'AZTEC_SLASHING_ROUND_SIZE',
85
+ description: 'The slashing round size',
86
+ ...numberConfigHelper(DefaultL1ContractsConfig.slashingRoundSize),
87
+ },
88
+ governanceProposerQuorum: {
89
+ env: 'AZTEC_GOVERNANCE_PROPOSER_QUORUM',
90
+ description: 'The governance proposing quorum',
91
+ ...numberConfigHelper(DefaultL1ContractsConfig.governanceProposerQuorum),
92
+ },
93
+ governanceProposerRoundSize: {
94
+ env: 'AZTEC_GOVERNANCE_PROPOSER_ROUND_SIZE',
95
+ description: 'The governance proposing round size',
96
+ ...numberConfigHelper(DefaultL1ContractsConfig.governanceProposerRoundSize),
97
+ },
98
+ ...l1TxUtilsConfigMappings,
99
+ };
100
+
101
+ export function getL1ContractsConfigEnvVars(): L1ContractsConfig {
102
+ return getConfigFromMappings(l1ContractsConfigMappings);
103
+ }
@@ -0,0 +1,4 @@
1
+ import type { Hex } from 'viem';
2
+
3
+ export const NULL_KEY: Hex = `0x${'0000000000000000000000000000000000000000000000000000000000000000'}`;
4
+ export const AZTEC_TEST_CHAIN_ID = 677692;
@@ -0,0 +1,19 @@
1
+ import { EmpireBaseAbi } from '@aztec/l1-artifacts/EmpireBaseAbi';
2
+
3
+ import { type Hex, encodeFunctionData } from 'viem';
4
+
5
+ import type { L1TxRequest } from '../l1_tx_utils.js';
6
+
7
+ export interface IEmpireBase {
8
+ getRoundInfo(rollupAddress: Hex, round: bigint): Promise<{ lastVote: bigint; leader: Hex; executed: boolean }>;
9
+ computeRound(slot: bigint): Promise<bigint>;
10
+ createVoteRequest(payload: Hex): L1TxRequest;
11
+ }
12
+
13
+ export function encodeVote(payload: Hex): Hex {
14
+ return encodeFunctionData({
15
+ abi: EmpireBaseAbi,
16
+ functionName: 'vote',
17
+ args: [payload],
18
+ });
19
+ }
@@ -0,0 +1,43 @@
1
+ import { EthAddress } from '@aztec/foundation/eth-address';
2
+ import { TestERC20Abi as FeeJuiceAbi } from '@aztec/l1-artifacts';
3
+
4
+ import { type GetContractReturnType, type Hex, getContract } from 'viem';
5
+
6
+ import type { L1Clients } from '../types.js';
7
+
8
+ export class FeeJuiceContract {
9
+ private readonly publicFeeJuice: GetContractReturnType<typeof FeeJuiceAbi, L1Clients['publicClient']>;
10
+ private readonly walletFeeJuice: GetContractReturnType<typeof FeeJuiceAbi, L1Clients['walletClient']> | undefined;
11
+
12
+ constructor(
13
+ address: Hex,
14
+ public readonly publicClient: L1Clients['publicClient'],
15
+ public readonly walletClient: L1Clients['walletClient'] | undefined,
16
+ ) {
17
+ this.publicFeeJuice = getContract({ address, abi: FeeJuiceAbi, client: publicClient });
18
+ this.walletFeeJuice = walletClient ? getContract({ address, abi: FeeJuiceAbi, client: walletClient }) : undefined;
19
+ }
20
+
21
+ public get address() {
22
+ return EthAddress.fromString(this.publicFeeJuice.address);
23
+ }
24
+
25
+ private assertWalletFeeJuice() {
26
+ if (!this.walletFeeJuice) {
27
+ throw new Error('Wallet client is required for this operation');
28
+ }
29
+ return this.walletFeeJuice;
30
+ }
31
+
32
+ public async mint(to: Hex, amount: bigint) {
33
+ const walletFeeJuice = this.assertWalletFeeJuice();
34
+ const tx = await walletFeeJuice.write.mint([to, amount]);
35
+ await this.publicClient.waitForTransactionReceipt({ hash: tx });
36
+ }
37
+
38
+ public async approve(spender: Hex, amount: bigint) {
39
+ const walletFeeJuice = this.assertWalletFeeJuice();
40
+ const tx = await walletFeeJuice.write.approve([spender, amount]);
41
+ await this.publicClient.waitForTransactionReceipt({ hash: tx });
42
+ }
43
+ }