@aztec/end-to-end 0.0.1-commit.72dcdcda8 → 0.0.1-commit.7689c7f

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 (181) hide show
  1. package/README.md +27 -0
  2. package/dest/bench/client_flows/benchmark.d.ts +15 -1
  3. package/dest/bench/client_flows/benchmark.d.ts.map +1 -1
  4. package/dest/bench/client_flows/benchmark.js +17 -0
  5. package/dest/bench/client_flows/client_flows_benchmark.d.ts +2 -2
  6. package/dest/bench/client_flows/client_flows_benchmark.d.ts.map +1 -1
  7. package/dest/bench/client_flows/client_flows_benchmark.js +32 -36
  8. package/dest/bench/utils.d.ts +1 -1
  9. package/dest/bench/utils.d.ts.map +1 -1
  10. package/dest/bench/utils.js +8 -3
  11. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts +5 -4
  12. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts.map +1 -1
  13. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.js +28 -19
  14. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.d.ts +15 -4
  15. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.d.ts.map +1 -1
  16. package/dest/e2e_cross_chain_messaging/cross_chain_messaging_test.js +39 -7
  17. package/dest/e2e_deploy_contract/deploy_test.d.ts +3 -3
  18. package/dest/e2e_deploy_contract/deploy_test.d.ts.map +1 -1
  19. package/dest/e2e_deploy_contract/deploy_test.js +2 -1
  20. package/dest/e2e_epochs/epochs_test.d.ts +25 -2
  21. package/dest/e2e_epochs/epochs_test.d.ts.map +1 -1
  22. package/dest/e2e_epochs/epochs_test.js +88 -14
  23. package/dest/e2e_fees/fees_test.d.ts +5 -2
  24. package/dest/e2e_fees/fees_test.d.ts.map +1 -1
  25. package/dest/e2e_fees/fees_test.js +44 -11
  26. package/dest/e2e_nested_contract/nested_contract_test.d.ts +3 -3
  27. package/dest/e2e_nested_contract/nested_contract_test.d.ts.map +1 -1
  28. package/dest/e2e_nested_contract/nested_contract_test.js +6 -7
  29. package/dest/e2e_p2p/inactivity_slash_test.d.ts +1 -1
  30. package/dest/e2e_p2p/inactivity_slash_test.d.ts.map +1 -1
  31. package/dest/e2e_p2p/inactivity_slash_test.js +5 -3
  32. package/dest/e2e_p2p/p2p_network.d.ts +13 -11
  33. package/dest/e2e_p2p/p2p_network.d.ts.map +1 -1
  34. package/dest/e2e_p2p/p2p_network.js +65 -43
  35. package/dest/e2e_p2p/reqresp/utils.d.ts +1 -1
  36. package/dest/e2e_p2p/reqresp/utils.d.ts.map +1 -1
  37. package/dest/e2e_p2p/reqresp/utils.js +42 -13
  38. package/dest/e2e_p2p/shared.d.ts +25 -7
  39. package/dest/e2e_p2p/shared.d.ts.map +1 -1
  40. package/dest/e2e_p2p/shared.js +73 -50
  41. package/dest/e2e_token_contract/token_contract_test.d.ts +3 -3
  42. package/dest/e2e_token_contract/token_contract_test.d.ts.map +1 -1
  43. package/dest/e2e_token_contract/token_contract_test.js +15 -13
  44. package/dest/fixtures/authwit_proxy.d.ts +3 -3
  45. package/dest/fixtures/authwit_proxy.d.ts.map +1 -1
  46. package/dest/fixtures/authwit_proxy.js +4 -0
  47. package/dest/fixtures/e2e_prover_test.d.ts +6 -5
  48. package/dest/fixtures/e2e_prover_test.d.ts.map +1 -1
  49. package/dest/fixtures/e2e_prover_test.js +13 -17
  50. package/dest/fixtures/fixtures.d.ts +73 -1
  51. package/dest/fixtures/fixtures.d.ts.map +1 -1
  52. package/dest/fixtures/fixtures.js +68 -0
  53. package/dest/fixtures/get_bb_config.d.ts +1 -1
  54. package/dest/fixtures/get_bb_config.d.ts.map +1 -1
  55. package/dest/fixtures/get_bb_config.js +5 -5
  56. package/dest/fixtures/ha_setup.d.ts +2 -2
  57. package/dest/fixtures/ha_setup.d.ts.map +1 -1
  58. package/dest/fixtures/ha_setup.js +1 -1
  59. package/dest/fixtures/schnorr_hardcoded_account_contract.d.ts +25 -0
  60. package/dest/fixtures/schnorr_hardcoded_account_contract.d.ts.map +1 -0
  61. package/dest/fixtures/schnorr_hardcoded_account_contract.js +37 -0
  62. package/dest/fixtures/setup.d.ts +43 -11
  63. package/dest/fixtures/setup.d.ts.map +1 -1
  64. package/dest/fixtures/setup.js +138 -29
  65. package/dest/fixtures/setup_p2p_test.d.ts +10 -6
  66. package/dest/fixtures/setup_p2p_test.d.ts.map +1 -1
  67. package/dest/fixtures/setup_p2p_test.js +8 -8
  68. package/dest/fixtures/token_utils.d.ts +1 -1
  69. package/dest/fixtures/token_utils.d.ts.map +1 -1
  70. package/dest/fixtures/token_utils.js +2 -5
  71. package/dest/fixtures/utils.d.ts +2 -2
  72. package/dest/fixtures/utils.d.ts.map +1 -1
  73. package/dest/fixtures/utils.js +1 -1
  74. package/dest/forward-compatibility/wallet_rpc_client.d.ts +7 -0
  75. package/dest/forward-compatibility/wallet_rpc_client.d.ts.map +1 -0
  76. package/dest/forward-compatibility/wallet_rpc_client.js +15 -0
  77. package/dest/forward-compatibility/wallet_service.d.ts +3 -0
  78. package/dest/forward-compatibility/wallet_service.d.ts.map +1 -0
  79. package/dest/forward-compatibility/wallet_service.js +109 -0
  80. package/dest/install_legacy_contracts.d.cts +10 -0
  81. package/dest/install_legacy_contracts.d.cts.map +1 -0
  82. package/dest/legacy-jest-resolver.d.cts +3 -0
  83. package/dest/legacy-jest-resolver.d.cts.map +1 -0
  84. package/dest/shared/cross_chain_test_harness.d.ts +4 -2
  85. package/dest/shared/cross_chain_test_harness.d.ts.map +1 -1
  86. package/dest/shared/cross_chain_test_harness.js +22 -18
  87. package/dest/shared/gas_portal_test_harness.d.ts +8 -5
  88. package/dest/shared/gas_portal_test_harness.d.ts.map +1 -1
  89. package/dest/shared/gas_portal_test_harness.js +19 -10
  90. package/dest/shared/submit-transactions.d.ts +1 -1
  91. package/dest/shared/submit-transactions.d.ts.map +1 -1
  92. package/dest/shared/submit-transactions.js +1 -1
  93. package/dest/shared/uniswap_l1_l2.d.ts +1 -1
  94. package/dest/shared/uniswap_l1_l2.d.ts.map +1 -1
  95. package/dest/shared/uniswap_l1_l2.js +60 -39
  96. package/dest/shared/wait_for_l1_to_l2_message.d.ts +13 -0
  97. package/dest/shared/wait_for_l1_to_l2_message.d.ts.map +1 -0
  98. package/dest/shared/wait_for_l1_to_l2_message.js +10 -0
  99. package/dest/simulators/lending_simulator.d.ts +10 -3
  100. package/dest/simulators/lending_simulator.d.ts.map +1 -1
  101. package/dest/simulators/lending_simulator.js +26 -14
  102. package/dest/simulators/token_simulator.d.ts +1 -1
  103. package/dest/simulators/token_simulator.d.ts.map +1 -1
  104. package/dest/simulators/token_simulator.js +2 -2
  105. package/dest/spartan/setup_test_wallets.d.ts +4 -2
  106. package/dest/spartan/setup_test_wallets.d.ts.map +1 -1
  107. package/dest/spartan/setup_test_wallets.js +72 -39
  108. package/dest/spartan/tx_metrics.d.ts +18 -4
  109. package/dest/spartan/tx_metrics.d.ts.map +1 -1
  110. package/dest/spartan/tx_metrics.js +74 -21
  111. package/dest/spartan/utils/config.d.ts +9 -32
  112. package/dest/spartan/utils/config.d.ts.map +1 -1
  113. package/dest/spartan/utils/config.js +1 -0
  114. package/dest/spartan/utils/index.d.ts +2 -1
  115. package/dest/spartan/utils/index.d.ts.map +1 -1
  116. package/dest/spartan/utils/index.js +2 -0
  117. package/dest/spartan/utils/nodes.js +2 -2
  118. package/dest/spartan/utils/pod_logs.d.ts +25 -0
  119. package/dest/spartan/utils/pod_logs.d.ts.map +1 -0
  120. package/dest/spartan/utils/pod_logs.js +74 -0
  121. package/dest/test-wallet/test_wallet.d.ts +25 -23
  122. package/dest/test-wallet/test_wallet.d.ts.map +1 -1
  123. package/dest/test-wallet/test_wallet.js +146 -87
  124. package/dest/test-wallet/utils.d.ts +3 -3
  125. package/dest/test-wallet/utils.d.ts.map +1 -1
  126. package/dest/test-wallet/utils.js +2 -7
  127. package/dest/test-wallet/wallet_worker_script.js +2 -2
  128. package/dest/test-wallet/worker_wallet.d.ts +5 -4
  129. package/dest/test-wallet/worker_wallet.d.ts.map +1 -1
  130. package/dest/test-wallet/worker_wallet.js +5 -1
  131. package/dest/test-wallet/worker_wallet_schema.d.ts +110 -224
  132. package/dest/test-wallet/worker_wallet_schema.d.ts.map +1 -1
  133. package/dest/test-wallet/worker_wallet_schema.js +14 -2
  134. package/package.json +46 -42
  135. package/src/bench/client_flows/benchmark.ts +19 -0
  136. package/src/bench/client_flows/client_flows_benchmark.ts +59 -41
  137. package/src/bench/utils.ts +9 -3
  138. package/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +42 -24
  139. package/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts +49 -11
  140. package/src/e2e_deploy_contract/deploy_test.ts +3 -2
  141. package/src/e2e_epochs/epochs_test.ts +99 -11
  142. package/src/e2e_fees/fees_test.ts +49 -9
  143. package/src/e2e_nested_contract/nested_contract_test.ts +9 -5
  144. package/src/e2e_p2p/inactivity_slash_test.ts +5 -3
  145. package/src/e2e_p2p/p2p_network.ts +82 -60
  146. package/src/e2e_p2p/reqresp/utils.ts +51 -12
  147. package/src/e2e_p2p/shared.ts +92 -64
  148. package/src/e2e_token_contract/token_contract_test.ts +28 -10
  149. package/src/fixtures/authwit_proxy.ts +4 -0
  150. package/src/fixtures/e2e_prover_test.ts +22 -19
  151. package/src/fixtures/fixtures.ts +86 -0
  152. package/src/fixtures/get_bb_config.ts +7 -6
  153. package/src/fixtures/ha_setup.ts +4 -2
  154. package/src/fixtures/schnorr_hardcoded_account_contract.ts +49 -0
  155. package/src/fixtures/setup.ts +175 -33
  156. package/src/fixtures/setup_p2p_test.ts +13 -9
  157. package/src/fixtures/token_utils.ts +1 -2
  158. package/src/fixtures/utils.ts +2 -0
  159. package/src/forward-compatibility/wallet_rpc_client.ts +14 -0
  160. package/src/forward-compatibility/wallet_service.ts +104 -0
  161. package/src/guides/up_quick_start.sh +0 -2
  162. package/src/install_legacy_contracts.cjs +75 -0
  163. package/src/legacy-jest-resolver.cjs +112 -0
  164. package/src/shared/cross_chain_test_harness.ts +27 -13
  165. package/src/shared/gas_portal_test_harness.ts +21 -11
  166. package/src/shared/submit-transactions.ts +1 -4
  167. package/src/shared/uniswap_l1_l2.ts +77 -50
  168. package/src/shared/wait_for_l1_to_l2_message.ts +23 -0
  169. package/src/simulators/lending_simulator.ts +32 -14
  170. package/src/simulators/token_simulator.ts +6 -2
  171. package/src/spartan/setup_test_wallets.ts +91 -37
  172. package/src/spartan/tx_metrics.ts +82 -24
  173. package/src/spartan/utils/config.ts +1 -0
  174. package/src/spartan/utils/index.ts +3 -0
  175. package/src/spartan/utils/nodes.ts +2 -2
  176. package/src/spartan/utils/pod_logs.ts +99 -0
  177. package/src/test-wallet/test_wallet.ts +174 -104
  178. package/src/test-wallet/utils.ts +3 -7
  179. package/src/test-wallet/wallet_worker_script.ts +2 -2
  180. package/src/test-wallet/worker_wallet.ts +9 -4
  181. package/src/test-wallet/worker_wallet_schema.ts +2 -2
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env -S node --no-warnings
2
+ /**
3
+ * Standalone entrypoint that spins up a local Aztec network (L1 + node) and exposes a {@link NodeEmbeddedWallet} over
4
+ * JSON-RPC.
5
+ *
6
+ * Intended for forward-compatibility testing: an **old** release image runs this script so that **new** tests can send
7
+ * new artifacts to old runtime code (loadContractArtifact, ACIR simulator, class-ID computation, entrypoint encoding,
8
+ * etc.).
9
+ */
10
+ import { getSchnorrAccountContractAddress } from '@aztec/accounts/schnorr';
11
+ import { getInitialTestAccountsData } from '@aztec/accounts/testing';
12
+ import { createLocalNetwork } from '@aztec/aztec';
13
+ import { Fr } from '@aztec/aztec.js/fields';
14
+ import { WalletSchema } from '@aztec/aztec.js/wallet';
15
+ import { GrumpkinScalar } from '@aztec/foundation/curves/grumpkin';
16
+ import { createNamespacedSafeJsonRpcServer, startHttpRpcServer } from '@aztec/foundation/json-rpc/server';
17
+ import { createLogger } from '@aztec/foundation/log';
18
+ import { AztecNodeApiSchema } from '@aztec/stdlib/interfaces/client';
19
+ import { EmbeddedWallet } from '@aztec/wallets/embedded';
20
+
21
+ const logger = createLogger('wallet-service');
22
+
23
+ const { ETHEREUM_HOSTS = 'http://localhost:8545', NODE_PORT = '8080', WALLET_PORT = '8081' } = process.env;
24
+
25
+ async function main() {
26
+ const l1RpcUrls = ETHEREUM_HOSTS.split(',').map(url => url.trim());
27
+
28
+ // Some tests (e.g. AMM) need 4 accounts but only 3 are funded via genesis. Generate deterministic keys for a 4th
29
+ // account so we can compute its address before network startup and include it in genesis funding. We cannot do this
30
+ // in the test because Wallet interface does not expose account creation functionality (only TestWallet exposes that
31
+ // but that's not used in forward compatibility testing).
32
+ const extraAccountSecret = Fr.fromHexString('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef');
33
+ const extraAccountSalt = Fr.ZERO;
34
+ const extraAccountSigningKey = GrumpkinScalar.random();
35
+ const extraAccountAddress = await getSchnorrAccountContractAddress(
36
+ extraAccountSecret,
37
+ extraAccountSalt,
38
+ extraAccountSigningKey,
39
+ );
40
+
41
+ logger.info('Starting wallet service...', { l1RpcUrls });
42
+
43
+ // createLocalNetwork deploys L1 contracts, starts the node, and optionally deploys funded test accounts (when
44
+ // TEST_ACCOUNTS=true via env). We are not proving anything just like is done when local network is started by
45
+ // the `aztecStart` function. The extra account address is passed via prefundAddresses so it gets fee juice at genesis.
46
+ const { node, stop: stopNetwork } = await createLocalNetwork(
47
+ { l1RpcUrls, realProofs: false, prefundAddresses: [extraAccountAddress.toString()] },
48
+ logger.info,
49
+ );
50
+
51
+ // Create an ephemeral embedded wallet backed by the local node.
52
+ const wallet = await EmbeddedWallet.create(node, { ephemeral: true });
53
+
54
+ // Re-register the initial test accounts so they are available via wallet.getAccounts(). createLocalNetwork deploys
55
+ // them onchain but uses a temporary wallet that is then stopped.
56
+ //
57
+ // We use the non-lazy import path (@aztec/accounts/testing, not /lazy) to avoid the dynamic JSON import that is
58
+ // incompatible with Node.js import attribute enforcement.
59
+ const testAccountsData = await getInitialTestAccountsData();
60
+ const accounts = await Promise.all(
61
+ testAccountsData.map(({ secret, salt, signingKey }) => wallet.createSchnorrAccount(secret, salt, signingKey)),
62
+ );
63
+
64
+ // Register and deploy the 4th account.
65
+ const extraAccount = await wallet.createSchnorrAccount(extraAccountSecret, extraAccountSalt, extraAccountSigningKey);
66
+ const deployMethod = await extraAccount.getDeployMethod();
67
+ await deployMethod.send({ from: accounts[0].address });
68
+
69
+ logger.info('Embedded wallet created', {
70
+ accounts: [...accounts, extraAccount].map(a => a.address.toString()),
71
+ });
72
+
73
+ // Contract artifacts are large, so allow generous body sizes for RPC requests.
74
+ const rpcOptions = { maxBodySizeBytes: '50mb' };
75
+
76
+ // Serve node RPC
77
+ const nodeRpcServer = createNamespacedSafeJsonRpcServer({ node: [node, AztecNodeApiSchema] }, rpcOptions);
78
+ const nodeHttpServer = await startHttpRpcServer(nodeRpcServer, { port: NODE_PORT });
79
+ logger.info(`Node JSON-RPC server listening on port ${nodeHttpServer.port}`);
80
+
81
+ // Serve wallet RPC
82
+ const walletRpcServer = createNamespacedSafeJsonRpcServer({ wallet: [wallet, WalletSchema] }, rpcOptions);
83
+ const walletHttpServer = await startHttpRpcServer(walletRpcServer, { port: WALLET_PORT });
84
+ logger.info(`Wallet JSON-RPC server listening on port ${walletHttpServer.port}`);
85
+
86
+ const shutdown = async () => {
87
+ logger.info('Shutting down...');
88
+ nodeHttpServer.close();
89
+ walletHttpServer.close();
90
+ await wallet.stop();
91
+ await stopNetwork();
92
+ process.exit(0);
93
+ };
94
+
95
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
96
+ process.once('SIGINT', shutdown);
97
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
98
+ process.once('SIGTERM', shutdown);
99
+ }
100
+
101
+ main().catch(err => {
102
+ logger.error('Wallet service failed to start', err);
103
+ process.exit(1);
104
+ });
@@ -18,10 +18,8 @@ aztec-wallet() {
18
18
 
19
19
  aztec-wallet import-test-accounts
20
20
 
21
- # docs:start:declare-accounts
22
21
  aztec-wallet create-account -a alice -f test0
23
22
  aztec-wallet create-account -a bob -f test0
24
- # docs:end:declare-accounts
25
23
 
26
24
  aztec-wallet bridge-fee-juice 1000000000000000000000 accounts:alice --mint --no-wait
27
25
 
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ // Installs pinned legacy @aztec/* contract-artifact packages into .legacy-contracts/<version>/.
3
+ //
4
+ // Called from two places:
5
+ // - bootstrap.sh ci-compat-e2e: pre-populates the cache on the host before hermetic test
6
+ // containers launch. The containers run with --net=none (see ci3/docker_isolate), so on-demand
7
+ // installs from inside them fail with EAI_AGAIN.
8
+ // - legacy-jest-resolver.cjs: on-demand install for local dev, where jest runs with network.
9
+ //
10
+ // Idempotent: no-op when all packages are already present.
11
+ /* eslint-disable @typescript-eslint/no-require-imports */
12
+
13
+ const path = require('path');
14
+ const fs = require('fs');
15
+ const { execFileSync } = require('child_process');
16
+
17
+ const REDIRECTED = ['@aztec/noir-contracts.js', '@aztec/noir-test-contracts.js', '@aztec/accounts'];
18
+
19
+ const e2eRoot = path.resolve(__dirname, '..');
20
+
21
+ function cacheRoot(version) {
22
+ return path.join(e2eRoot, '.legacy-contracts', version);
23
+ }
24
+
25
+ function pkgJsonPath(version, pkg) {
26
+ return path.join(cacheRoot(version), 'node_modules', pkg, 'package.json');
27
+ }
28
+
29
+ function installLegacyContracts(version) {
30
+ if (!version) {
31
+ throw new Error('installLegacyContracts: version is required');
32
+ }
33
+ if (REDIRECTED.every(pkg => fs.existsSync(pkgJsonPath(version, pkg)))) {
34
+ return;
35
+ }
36
+
37
+ const cacheDir = cacheRoot(version);
38
+ fs.mkdirSync(cacheDir, { recursive: true });
39
+
40
+ // Seed a standalone package.json so `npm install --prefix` treats cacheRoot as its own project. Without this, npm
41
+ // walks up and finds the yarn-project workspace root, which breaks on `workspace:` protocol deps and risks
42
+ // clobbering the monorepo's node_modules.
43
+ const seed = path.join(cacheDir, 'package.json');
44
+ if (!fs.existsSync(seed)) {
45
+ fs.writeFileSync(seed, JSON.stringify({ name: 'legacy-contracts-cache', private: true }));
46
+ }
47
+
48
+ const specs = REDIRECTED.map(pkg => `${pkg}@${version}`);
49
+ process.stderr.write(`[legacy-contracts] installing ${specs.join(' ')} into ${cacheDir}\n`);
50
+ // --prefix: install into cacheRoot instead of cwd, so the cache is isolated from the monorepo.
51
+ // --no-save: don't write the installed packages back to the seeded package.json.
52
+ // --ignore-scripts: skip lifecycle scripts (preinstall/postinstall) of the legacy packages and their transitive
53
+ // deps; we only want the files on disk, not to run any build steps.
54
+ // --legacy-peer-deps: tolerate peer-dependency mismatches between the pinned legacy @aztec/* graph and whatever
55
+ // current versions npm would otherwise try to reconcile.
56
+ execFileSync(
57
+ 'npm',
58
+ ['install', '--prefix', cacheDir, '--no-save', '--ignore-scripts', '--legacy-peer-deps', ...specs],
59
+ { stdio: 'inherit' },
60
+ );
61
+
62
+ // Verify versions on disk match the requested version.
63
+ for (const pkg of REDIRECTED) {
64
+ const onDisk = JSON.parse(fs.readFileSync(pkgJsonPath(version, pkg), 'utf8')).version;
65
+ if (onDisk !== version) {
66
+ throw new Error(`[legacy-contracts] ${pkg} on disk is ${onDisk}, expected ${version}`);
67
+ }
68
+ }
69
+ }
70
+
71
+ module.exports = { installLegacyContracts, REDIRECTED, cacheRoot };
72
+
73
+ if (require.main === module) {
74
+ installLegacyContracts(process.argv[2]);
75
+ }
@@ -0,0 +1,112 @@
1
+ // Custom Jest resolver. When CONTRACT_ARTIFACTS_VERSION is set, redirects *only* JSON artifact files under
2
+ // @aztec/noir-contracts.js/artifacts/, @aztec/noir-test-contracts.js/artifacts/, and @aztec/accounts/artifacts/ to a local cache of the pinned
3
+ // legacy versions. TypeScript wrapper classes (e.g. Token.ts) continue to load from the current workspace and use the
4
+ // current @aztec/aztec.js — only the artifact JSON (the deployed-contract ABI / bytecode / notes surface) is swapped.
5
+ //
6
+ // Why JSON-only: the JSON artifact is the actual interchange surface a "deployed contract" exposes. The TS wrapper is
7
+ // generated client-side ergonomics that's tightly coupled to the current @aztec/aztec.js API. Redirecting the wrapper
8
+ // would couple this test to a moving aztec.js surface and break at import time on unrelated breaking changes; we want
9
+ // to fail only on actual artifact-compat regressions.
10
+ //
11
+ // Cache population lives in install_legacy_contracts.cjs — invoked lazily here for local dev, and eagerly
12
+ // by bootstrap.sh ci-compat-e2e before hermetic test containers (which run with --net=none) launch.
13
+ //
14
+ // Missing artifacts: legacy version directories are immutable, so an artifact missing from the cache means the
15
+ // contract was added after the pinned release — there's nothing to compat-test. Rather than failing or silently
16
+ // falling back to the workspace artifact (which would turn the compat run into a regular e2e run that always
17
+ // passes), we log the miss and exit the process cleanly with code 0. The test never runs, but the per-test CI
18
+ // log captures the explanatory line so the reason is auditable. This keeps the change scoped to this resolver,
19
+ // avoiding a new exit-code contract in the shared ci3 test runner.
20
+ //
21
+ // Activated by env var; passthrough otherwise.
22
+ /* eslint-disable @typescript-eslint/no-require-imports */
23
+
24
+ const path = require('path');
25
+ const fs = require('fs');
26
+ const { installLegacyContracts, REDIRECTED, cacheRoot } = require('./install_legacy_contracts.cjs');
27
+
28
+ const version = process.env.CONTRACT_ARTIFACTS_VERSION;
29
+ const cacheDir = version ? cacheRoot(version) : null;
30
+
31
+ function pkgJsonPath(name) {
32
+ return path.join(cacheDir, 'node_modules', name, 'package.json');
33
+ }
34
+
35
+ // Kept in a separate module (not inlined) because bootstrap.sh ci-compat-e2e also calls it directly
36
+ // via `node .../install_legacy_contracts.cjs <version>` to pre-populate the cache on the host before
37
+ // hermetic --net=none test containers launch. Inlining here would force us to duplicate the logic
38
+ // in bash or re-run jest just to trigger the install.
39
+ if (version) {
40
+ installLegacyContracts(version);
41
+ }
42
+
43
+ let bannerPrinted = false;
44
+ const seen = new Set();
45
+
46
+ function printBannerOnce() {
47
+ if (bannerPrinted || !version) {
48
+ return;
49
+ }
50
+ bannerPrinted = true;
51
+ const lines = ['='.repeat(60), `[legacy-contracts][jest] CONTRACT_ARTIFACTS_VERSION=${version}`];
52
+ for (const p of REDIRECTED) {
53
+ const v = JSON.parse(fs.readFileSync(pkgJsonPath(p), 'utf8')).version;
54
+ if (v !== version) {
55
+ throw new Error(`[legacy-contracts] ${p} on disk is ${v}, expected ${version}`);
56
+ }
57
+ lines.push(`[legacy-contracts][jest] redirecting ${p}/artifacts/*.json -> .legacy-contracts/${version}/...`);
58
+ }
59
+ lines.push('='.repeat(60));
60
+ process.stderr.write(lines.join('\n') + '\n');
61
+ }
62
+
63
+ // Match a resolved absolute path against the workspace artifacts dirs and return the legacy cache equivalent, or null
64
+ // if it's not an artifact path we should redirect.
65
+ function legacyArtifactPath(resolved) {
66
+ if (!resolved.endsWith('.json')) {
67
+ return null;
68
+ }
69
+ for (const pkg of REDIRECTED) {
70
+ // pkg = '@aztec/noir-contracts.js' -> match '/noir-contracts.js/artifacts/'
71
+ const dirName = pkg.split('/')[1];
72
+ const marker = `/${dirName}/artifacts/`;
73
+ const idx = resolved.indexOf(marker);
74
+ if (idx === -1) {
75
+ continue;
76
+ }
77
+ const basename = resolved.slice(idx + marker.length);
78
+ return path.join(cacheDir, 'node_modules', pkg, 'artifacts', basename);
79
+ }
80
+ return null;
81
+ }
82
+
83
+ module.exports = function legacyResolver(request, options) {
84
+ // Always run the default resolver first. We only inspect (and possibly rewrite) the *result*; this catches both
85
+ // bare-specifier imports of `@aztec/noir-contracts.js/artifacts/foo.json` and the relative `../artifacts/foo.json`
86
+ // imports inside the workspace TS wrapper classes — both resolve to the same workspace artifact path that we then
87
+ // redirect.
88
+ const resolved = options.defaultResolver(request, options);
89
+ if (!version) {
90
+ return resolved;
91
+ }
92
+ printBannerOnce();
93
+ const legacy = legacyArtifactPath(resolved);
94
+ if (!legacy) {
95
+ return resolved;
96
+ }
97
+ if (!fs.existsSync(legacy)) {
98
+ // Contract was added after this historical release, there is nothing to compat-test for it. Exit the process
99
+ // cleanly with code 0 so the test runner reports the run as passed.
100
+ fs.writeSync(
101
+ 2,
102
+ `[legacy-contracts][jest] artifact ${path.basename(legacy)} not in legacy cache @${version}; ` +
103
+ `assumed added after this release. No compat coverage applicable for this version, treating as passed.\n`,
104
+ );
105
+ process.exit(0);
106
+ }
107
+ if (!seen.has(resolved)) {
108
+ seen.add(resolved);
109
+ process.stderr.write(`[legacy-contracts][jest] redirected ${path.basename(legacy)} -> ${legacy}\n`);
110
+ }
111
+ return legacy;
112
+ };
@@ -13,11 +13,11 @@ import type { AztecNode } from '@aztec/aztec.js/node';
13
13
  import type { SiblingPath } from '@aztec/aztec.js/trees';
14
14
  import type { TxReceipt } from '@aztec/aztec.js/tx';
15
15
  import type { Wallet } from '@aztec/aztec.js/wallet';
16
+ import { OutboxContract } from '@aztec/ethereum/contracts';
16
17
  import { deployL1Contract } from '@aztec/ethereum/deploy-l1-contract';
17
18
  import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
18
19
  import type { ExtendedViemWalletClient } from '@aztec/ethereum/types';
19
20
  import { EpochNumber } from '@aztec/foundation/branded-types';
20
- import { retryUntil } from '@aztec/foundation/retry';
21
21
  import { TestERC20Abi, TokenPortalAbi, TokenPortalBytecode } from '@aztec/l1-artifacts';
22
22
  import { TokenContract } from '@aztec/noir-contracts.js/Token';
23
23
  import { TokenBridgeContract } from '@aztec/noir-contracts.js/TokenBridge';
@@ -25,6 +25,7 @@ import { TokenBridgeContract } from '@aztec/noir-contracts.js/TokenBridge';
25
25
  import { type Hex, getContract } from 'viem';
26
26
 
27
27
  import { mintTokensToPrivate } from '../fixtures/token_utils.js';
28
+ import { waitForL1ToL2MessageSeen } from './wait_for_l1_to_l2_message.js';
28
29
 
29
30
  /**
30
31
  * Deploy L1 token and portal, initialize portal, deploy a non native l2 token contract, its L2 bridge contract and attach is to the portal.
@@ -73,22 +74,26 @@ export async function deployAndInitializeTokenAndBridgeContracts(
73
74
  });
74
75
 
75
76
  // deploy l2 token
76
- const token = await TokenContract.deploy(wallet, owner, 'TokenName', 'TokenSymbol', 18).send({ from: owner });
77
+ const { contract: token } = await TokenContract.deploy(wallet, owner, 'TokenName', 'TokenSymbol', 18).send({
78
+ from: owner,
79
+ });
77
80
 
78
81
  // deploy l2 token bridge and attach to the portal
79
- const bridge = await TokenBridgeContract.deploy(wallet, token.address, tokenPortalAddress).send({ from: owner });
82
+ const { contract: bridge } = await TokenBridgeContract.deploy(wallet, token.address, tokenPortalAddress).send({
83
+ from: owner,
84
+ });
80
85
 
81
- if ((await token.methods.get_admin().simulate({ from: owner })) !== owner.toBigInt()) {
86
+ if ((await token.methods.get_admin().simulate({ from: owner })).result !== owner.toBigInt()) {
82
87
  throw new Error(`Token admin is not ${owner}`);
83
88
  }
84
89
 
85
- if (!(await bridge.methods.get_config().simulate({ from: owner })).token.equals(token.address)) {
90
+ if (!(await bridge.methods.get_config().simulate({ from: owner })).result.token.equals(token.address)) {
86
91
  throw new Error(`Bridge token is not ${token.address}`);
87
92
  }
88
93
 
89
94
  // make the bridge a minter on the token:
90
95
  await token.methods.set_minter(bridge.address, true).send({ from: owner });
91
- if ((await token.methods.is_minter(bridge.address).simulate({ from: owner })) === 1n) {
96
+ if ((await token.methods.is_minter(bridge.address).simulate({ from: owner })).result === 1n) {
92
97
  throw new Error(`Bridge is not a minter`);
93
98
  }
94
99
 
@@ -162,6 +167,7 @@ export class CrossChainTestHarness {
162
167
 
163
168
  private readonly l1TokenManager: L1TokenManager;
164
169
  private readonly l1TokenPortalManager: L1TokenPortalManager;
170
+ public readonly outboxContract: OutboxContract;
165
171
 
166
172
  constructor(
167
173
  /** Aztec node instance. */
@@ -202,6 +208,7 @@ export class CrossChainTestHarness {
202
208
  this.logger,
203
209
  );
204
210
  this.l1TokenManager = this.l1TokenPortalManager.getTokenManager();
211
+ this.outboxContract = new OutboxContract(this.l1Client, this.l1ContractAddresses.outboxAddress);
205
212
  }
206
213
 
207
214
  async mintTokensOnL1(amount: bigint) {
@@ -269,7 +276,7 @@ export class CrossChainTestHarness {
269
276
  authwitNonce: Fr = Fr.ZERO,
270
277
  authWitness: AuthWitness,
271
278
  ): Promise<TxReceipt> {
272
- const withdrawReceipt = await this.l2Bridge.methods
279
+ const { receipt: withdrawReceipt } = await this.l2Bridge.methods
273
280
  .exit_to_l1_private(this.l2Token.address, this.ethAccount, withdrawAmount, EthAddress.ZERO, authwitNonce)
274
281
  .send({ authWitnesses: [authWitness], from: this.ownerAddress });
275
282
 
@@ -277,7 +284,7 @@ export class CrossChainTestHarness {
277
284
  }
278
285
 
279
286
  async withdrawPublicFromAztecToL1(withdrawAmount: bigint, authwitNonce: Fr = Fr.ZERO): Promise<TxReceipt> {
280
- const withdrawReceipt = await this.l2Bridge.methods
287
+ const { receipt: withdrawReceipt } = await this.l2Bridge.methods
281
288
  .exit_to_l1_public(this.ethAccount, withdrawAmount, EthAddress.ZERO, authwitNonce)
282
289
  .send({ from: this.ownerAddress });
283
290
 
@@ -285,7 +292,7 @@ export class CrossChainTestHarness {
285
292
  }
286
293
 
287
294
  async getL2PrivateBalanceOf(owner: AztecAddress) {
288
- return await this.l2Token.methods.balance_of_private(owner).simulate({ from: owner });
295
+ return (await this.l2Token.methods.balance_of_private(owner).simulate({ from: owner })).result;
289
296
  }
290
297
 
291
298
  async expectPrivateBalanceOnL2(owner: AztecAddress, expectedBalance: bigint) {
@@ -295,7 +302,7 @@ export class CrossChainTestHarness {
295
302
  }
296
303
 
297
304
  async getL2PublicBalanceOf(owner: AztecAddress) {
298
- return await this.l2Token.methods.balance_of_public(owner).simulate({ from: this.ownerAddress });
305
+ return (await this.l2Token.methods.balance_of_public(owner).simulate({ from: this.ownerAddress })).result;
299
306
  }
300
307
 
301
308
  async expectPublicBalanceOnL2(owner: AztecAddress, expectedBalance: bigint) {
@@ -315,10 +322,18 @@ export class CrossChainTestHarness {
315
322
  withdrawFundsFromBridgeOnL1(
316
323
  amount: bigint,
317
324
  epochNumber: EpochNumber,
325
+ numCheckpointsInEpoch: number,
318
326
  messageIndex: bigint,
319
327
  siblingPath: SiblingPath<number>,
320
328
  ) {
321
- return this.l1TokenPortalManager.withdrawFunds(amount, this.ethAccount, epochNumber, messageIndex, siblingPath);
329
+ return this.l1TokenPortalManager.withdrawFunds(
330
+ amount,
331
+ this.ethAccount,
332
+ epochNumber,
333
+ numCheckpointsInEpoch,
334
+ messageIndex,
335
+ siblingPath,
336
+ );
322
337
  }
323
338
 
324
339
  async transferToPrivateOnL2(shieldAmount: bigint) {
@@ -342,8 +357,7 @@ export class CrossChainTestHarness {
342
357
  */
343
358
  async makeMessageConsumable(msgHash: Fr | Hex) {
344
359
  const frMsgHash = typeof msgHash === 'string' ? Fr.fromHexString(msgHash) : msgHash;
345
- // We poll isL1ToL2MessageSynced endpoint until the message is available
346
- await retryUntil(async () => await this.aztecNode.isL1ToL2MessageSynced(frMsgHash), 'message sync', 10);
360
+ await waitForL1ToL2MessageSeen(this.aztecNode, frMsgHash, { timeoutSeconds: 10 });
347
361
 
348
362
  await this.mintTokensPublicOnL2(0n);
349
363
  await this.mintTokensPublicOnL2(0n);
@@ -8,7 +8,12 @@ import type { ExtendedViemWalletClient } from '@aztec/ethereum/types';
8
8
  import { retryUntil } from '@aztec/foundation/retry';
9
9
  import { FeeJuiceContract } from '@aztec/noir-contracts.js/FeeJuice';
10
10
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
11
- import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
11
+ import type { AztecNodeAdmin, AztecNodeDebug } from '@aztec/stdlib/interfaces/client';
12
+
13
+ import { waitForL1ToL2MessageSeen } from './wait_for_l1_to_l2_message.js';
14
+
15
+ /** Aztec node that may expose the debug mining API in local e2e setups. */
16
+ type MiningAztecNode = AztecNode & Partial<AztecNodeDebug>;
12
17
 
13
18
  export interface IGasBridgingTestHarness {
14
19
  getL1FeeJuiceBalance(address: EthAddress): Promise<bigint>;
@@ -19,7 +24,7 @@ export interface IGasBridgingTestHarness {
19
24
  }
20
25
 
21
26
  export interface FeeJuicePortalTestingHarnessFactoryConfig {
22
- aztecNode: AztecNode;
27
+ aztecNode: MiningAztecNode;
23
28
  aztecNodeAdmin?: AztecNodeAdmin;
24
29
  l1Client: ExtendedViemWalletClient;
25
30
  wallet: Wallet;
@@ -75,7 +80,7 @@ export class GasBridgingTestHarness implements IGasBridgingTestHarness {
75
80
 
76
81
  constructor(
77
82
  /** Aztec node */
78
- public aztecNode: AztecNode,
83
+ public aztecNode: MiningAztecNode,
79
84
  /** Aztec node admin interface */
80
85
  public aztecNodeAdmin: AztecNodeAdmin | undefined,
81
86
  /** Wallet. */
@@ -131,7 +136,7 @@ export class GasBridgingTestHarness implements IGasBridgingTestHarness {
131
136
  }
132
137
 
133
138
  async getL2PublicBalanceOf(owner: AztecAddress) {
134
- return await this.feeJuice.methods.balance_of_public(owner).simulate({ from: owner });
139
+ return (await this.feeJuice.methods.balance_of_public(owner).simulate({ from: owner })).result;
135
140
  }
136
141
 
137
142
  async expectPublicBalanceOnL2(owner: AztecAddress, expectedBalance: bigint) {
@@ -144,8 +149,7 @@ export class GasBridgingTestHarness implements IGasBridgingTestHarness {
144
149
  await this.mintTokensOnL1();
145
150
  const claim = await this.sendTokensToPortalPublic(bridgeAmount, owner);
146
151
 
147
- const isSynced = async () => await this.aztecNode.isL1ToL2MessageSynced(Fr.fromHexString(claim.messageHash));
148
- await retryUntil(isSynced, `message ${claim.messageHash} sync`, 24, 1);
152
+ await waitForL1ToL2MessageSeen(this.aztecNode, Fr.fromHexString(claim.messageHash), { timeoutSeconds: 24 });
149
153
 
150
154
  // Progress by 2 L2 blocks so that the l1ToL2Message added above will be available to use on L2.
151
155
  await this.advanceL2Block();
@@ -163,6 +167,11 @@ export class GasBridgingTestHarness implements IGasBridgingTestHarness {
163
167
  }
164
168
 
165
169
  private async advanceL2Block() {
170
+ if (this.aztecNode.mineBlock) {
171
+ await this.aztecNode.mineBlock();
172
+ return;
173
+ }
174
+
166
175
  const initialBlockNumber = await this.aztecNode.getBlockNumber();
167
176
 
168
177
  let minTxsPerBlock = undefined;
@@ -171,11 +180,12 @@ export class GasBridgingTestHarness implements IGasBridgingTestHarness {
171
180
  await this.aztecNodeAdmin.setConfig({ minTxsPerBlock: 0 }); // Set to 0 to ensure we can advance the block
172
181
  }
173
182
 
174
- await retryUntil(async () => (await this.aztecNode.getBlockNumber()) >= initialBlockNumber + 1);
175
-
176
- if (this.aztecNodeAdmin && minTxsPerBlock !== undefined) {
177
- await this.aztecNodeAdmin.setConfig({ minTxsPerBlock });
183
+ try {
184
+ await retryUntil(async () => (await this.aztecNode.getBlockNumber()) >= initialBlockNumber + 1);
185
+ } finally {
186
+ if (this.aztecNodeAdmin && minTxsPerBlock !== undefined) {
187
+ await this.aztecNodeAdmin.setConfig({ minTxsPerBlock });
188
+ }
178
189
  }
179
190
  }
180
191
  }
181
- // docs:end:cross_chain_test_harness
@@ -19,10 +19,7 @@ export const submitTxsTo = async (
19
19
  times(numTxs, async () => {
20
20
  const accountManager = await wallet.createSchnorrAccount(Fr.random(), Fr.random(), GrumpkinScalar.random());
21
21
  const deployMethod = await accountManager.getDeployMethod();
22
- const txHash = await deployMethod.send({
23
- from: submitter,
24
- wait: NO_WAIT,
25
- });
22
+ const { txHash } = await deployMethod.send({ from: submitter, wait: NO_WAIT });
26
23
 
27
24
  logger.info(`Tx sent with hash ${txHash}`);
28
25
  const receipt: TxReceipt = await wallet.getTxReceipt(txHash);