@aztec/ethereum 0.65.2 → 0.66.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.
- package/dest/constants.d.ts +1 -0
- package/dest/constants.d.ts.map +1 -1
- package/dest/constants.js +2 -1
- package/dest/contracts/rollup.d.ts +5 -0
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +16 -1
- package/dest/deploy_l1_contracts.d.ts +5 -1
- package/dest/deploy_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_l1_contracts.js +109 -41
- package/dest/eth_cheat_codes.d.ts +155 -0
- package/dest/eth_cheat_codes.d.ts.map +1 -0
- package/dest/eth_cheat_codes.js +291 -0
- package/dest/index.d.ts +4 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +5 -2
- package/dest/l1_contract_addresses.d.ts +4 -1
- package/dest/l1_contract_addresses.d.ts.map +1 -1
- package/dest/l1_contract_addresses.js +8 -1
- package/dest/l1_reader.d.ts +1 -0
- package/dest/l1_reader.d.ts.map +1 -1
- package/dest/l1_reader.js +5 -2
- package/dest/l1_tx_utils.d.ts +100 -0
- package/dest/l1_tx_utils.d.ts.map +1 -0
- package/dest/l1_tx_utils.js +253 -0
- package/dest/utils.js +2 -2
- package/package.json +5 -3
- package/src/constants.ts +1 -0
- package/src/contracts/rollup.ts +20 -0
- package/src/deploy_l1_contracts.ts +127 -44
- package/src/eth_cheat_codes.ts +316 -0
- package/src/index.ts +4 -1
- package/src/l1_contract_addresses.ts +7 -0
- package/src/l1_reader.ts +5 -1
- package/src/l1_tx_utils.ts +400 -0
- package/src/utils.ts +1 -1
|
@@ -5,6 +5,8 @@ import { type DebugLogger } from '@aztec/foundation/log';
|
|
|
5
5
|
import {
|
|
6
6
|
CoinIssuerAbi,
|
|
7
7
|
CoinIssuerBytecode,
|
|
8
|
+
ExtRollupLibAbi,
|
|
9
|
+
ExtRollupLibBytecode,
|
|
8
10
|
FeeJuicePortalAbi,
|
|
9
11
|
FeeJuicePortalBytecode,
|
|
10
12
|
GovernanceAbi,
|
|
@@ -13,6 +15,8 @@ import {
|
|
|
13
15
|
GovernanceProposerBytecode,
|
|
14
16
|
InboxAbi,
|
|
15
17
|
InboxBytecode,
|
|
18
|
+
LeonidasLibAbi,
|
|
19
|
+
LeonidasLibBytecode,
|
|
16
20
|
OutboxAbi,
|
|
17
21
|
OutboxBytecode,
|
|
18
22
|
RegistryAbi,
|
|
@@ -22,12 +26,8 @@ import {
|
|
|
22
26
|
RollupAbi,
|
|
23
27
|
RollupBytecode,
|
|
24
28
|
RollupLinkReferences,
|
|
25
|
-
SampleLibAbi,
|
|
26
|
-
SampleLibBytecode,
|
|
27
29
|
TestERC20Abi,
|
|
28
30
|
TestERC20Bytecode,
|
|
29
|
-
TxsDecoderAbi,
|
|
30
|
-
TxsDecoderBytecode,
|
|
31
31
|
} from '@aztec/l1-artifacts';
|
|
32
32
|
|
|
33
33
|
import type { Abi, Narrow } from 'abitype';
|
|
@@ -53,8 +53,10 @@ import { type HDAccount, type PrivateKeyAccount, mnemonicToAccount, privateKeyTo
|
|
|
53
53
|
import { foundry } from 'viem/chains';
|
|
54
54
|
|
|
55
55
|
import { type L1ContractsConfig } from './config.js';
|
|
56
|
+
import { MINIMUM_STAKE } from './constants.js';
|
|
56
57
|
import { isAnvilTestChain } from './ethereum_chain.js';
|
|
57
58
|
import { type L1ContractAddresses } from './l1_contract_addresses.js';
|
|
59
|
+
import { L1TxUtils } from './l1_tx_utils.js';
|
|
58
60
|
|
|
59
61
|
/**
|
|
60
62
|
* Return type of the deployL1Contract function.
|
|
@@ -126,10 +128,14 @@ export interface L1ContractArtifactsForDeployment {
|
|
|
126
128
|
* Rollup contract artifacts
|
|
127
129
|
*/
|
|
128
130
|
rollup: ContractArtifacts;
|
|
131
|
+
/**
|
|
132
|
+
* The token to stake.
|
|
133
|
+
*/
|
|
134
|
+
stakingAsset: ContractArtifacts;
|
|
129
135
|
/**
|
|
130
136
|
* The token to pay for gas. This will be bridged to L2 via the feeJuicePortal below
|
|
131
137
|
*/
|
|
132
|
-
|
|
138
|
+
feeAsset: ContractArtifacts;
|
|
133
139
|
/**
|
|
134
140
|
* Fee juice portal contract artifacts. Optional for now as gas is not strictly enforced
|
|
135
141
|
*/
|
|
@@ -171,18 +177,22 @@ export const l1Artifacts: L1ContractArtifactsForDeployment = {
|
|
|
171
177
|
libraries: {
|
|
172
178
|
linkReferences: RollupLinkReferences,
|
|
173
179
|
libraryCode: {
|
|
174
|
-
|
|
175
|
-
contractAbi:
|
|
176
|
-
contractBytecode:
|
|
180
|
+
LeonidasLib: {
|
|
181
|
+
contractAbi: LeonidasLibAbi,
|
|
182
|
+
contractBytecode: LeonidasLibBytecode,
|
|
177
183
|
},
|
|
178
|
-
|
|
179
|
-
contractAbi:
|
|
180
|
-
contractBytecode:
|
|
184
|
+
ExtRollupLib: {
|
|
185
|
+
contractAbi: ExtRollupLibAbi,
|
|
186
|
+
contractBytecode: ExtRollupLibBytecode,
|
|
181
187
|
},
|
|
182
188
|
},
|
|
183
189
|
},
|
|
184
190
|
},
|
|
185
|
-
|
|
191
|
+
stakingAsset: {
|
|
192
|
+
contractAbi: TestERC20Abi,
|
|
193
|
+
contractBytecode: TestERC20Bytecode,
|
|
194
|
+
},
|
|
195
|
+
feeAsset: {
|
|
186
196
|
contractAbi: TestERC20Abi,
|
|
187
197
|
contractBytecode: TestERC20Bytecode,
|
|
188
198
|
},
|
|
@@ -255,6 +265,7 @@ export function createL1Clients(
|
|
|
255
265
|
const publicClient = createPublicClient({
|
|
256
266
|
chain,
|
|
257
267
|
transport: http(rpcUrl),
|
|
268
|
+
pollingInterval: 100,
|
|
258
269
|
});
|
|
259
270
|
|
|
260
271
|
return { walletClient, publicClient };
|
|
@@ -306,8 +317,19 @@ export const deployL1Contracts = async (
|
|
|
306
317
|
const registryAddress = await govDeployer.deploy(l1Artifacts.registry, [account.address.toString()]);
|
|
307
318
|
logger.info(`Deployed Registry at ${registryAddress}`);
|
|
308
319
|
|
|
309
|
-
const
|
|
310
|
-
|
|
320
|
+
const feeAssetAddress = await govDeployer.deploy(l1Artifacts.feeAsset, [
|
|
321
|
+
'FeeJuice',
|
|
322
|
+
'FEE',
|
|
323
|
+
account.address.toString(),
|
|
324
|
+
]);
|
|
325
|
+
logger.info(`Deployed Fee Juice at ${feeAssetAddress}`);
|
|
326
|
+
|
|
327
|
+
const stakingAssetAddress = await govDeployer.deploy(l1Artifacts.stakingAsset, [
|
|
328
|
+
'Staking',
|
|
329
|
+
'STK',
|
|
330
|
+
account.address.toString(),
|
|
331
|
+
]);
|
|
332
|
+
logger.info(`Deployed Staking Asset at ${stakingAssetAddress}`);
|
|
311
333
|
|
|
312
334
|
// @todo #8084
|
|
313
335
|
// @note These numbers are just chosen to make testing simple.
|
|
@@ -320,26 +342,29 @@ export const deployL1Contracts = async (
|
|
|
320
342
|
]);
|
|
321
343
|
logger.info(`Deployed GovernanceProposer at ${governanceProposerAddress}`);
|
|
322
344
|
|
|
345
|
+
// @note @LHerskind the assets are expected to be the same at some point, but for better
|
|
346
|
+
// configurability they are different for now.
|
|
323
347
|
const governanceAddress = await govDeployer.deploy(l1Artifacts.governance, [
|
|
324
|
-
|
|
348
|
+
feeAssetAddress.toString(),
|
|
325
349
|
governanceProposerAddress.toString(),
|
|
326
350
|
]);
|
|
327
351
|
logger.info(`Deployed Governance at ${governanceAddress}`);
|
|
328
352
|
|
|
329
353
|
const coinIssuerAddress = await govDeployer.deploy(l1Artifacts.coinIssuer, [
|
|
330
|
-
|
|
354
|
+
feeAssetAddress.toString(),
|
|
331
355
|
1n * 10n ** 18n, // @todo #8084
|
|
332
356
|
governanceAddress.toString(),
|
|
333
357
|
]);
|
|
334
358
|
logger.info(`Deployed CoinIssuer at ${coinIssuerAddress}`);
|
|
335
359
|
|
|
336
360
|
const rewardDistributorAddress = await govDeployer.deploy(l1Artifacts.rewardDistributor, [
|
|
337
|
-
|
|
361
|
+
feeAssetAddress.toString(),
|
|
338
362
|
registryAddress.toString(),
|
|
339
363
|
governanceAddress.toString(),
|
|
340
364
|
]);
|
|
341
365
|
logger.info(`Deployed RewardDistributor at ${rewardDistributorAddress}`);
|
|
342
366
|
|
|
367
|
+
logger.verbose(`Waiting for governance contracts to be deployed`);
|
|
343
368
|
await govDeployer.waitForDeployments();
|
|
344
369
|
logger.info(`All governance contracts deployed`);
|
|
345
370
|
|
|
@@ -347,27 +372,29 @@ export const deployL1Contracts = async (
|
|
|
347
372
|
|
|
348
373
|
const feeJuicePortalAddress = await deployer.deploy(l1Artifacts.feeJuicePortal, [
|
|
349
374
|
registryAddress.toString(),
|
|
350
|
-
|
|
375
|
+
feeAssetAddress.toString(),
|
|
351
376
|
args.l2FeeJuiceAddress.toString(),
|
|
352
377
|
]);
|
|
353
378
|
logger.info(`Deployed Fee Juice Portal at ${feeJuicePortalAddress}`);
|
|
354
379
|
|
|
355
|
-
const
|
|
380
|
+
const rollupConfigArgs = {
|
|
356
381
|
aztecSlotDuration: args.aztecSlotDuration,
|
|
357
382
|
aztecEpochDuration: args.aztecEpochDuration,
|
|
358
383
|
targetCommitteeSize: args.aztecTargetCommitteeSize,
|
|
359
384
|
aztecEpochProofClaimWindowInL2Slots: args.aztecEpochProofClaimWindowInL2Slots,
|
|
385
|
+
minimumStake: MINIMUM_STAKE,
|
|
360
386
|
};
|
|
361
|
-
const
|
|
387
|
+
const rollupArgs = [
|
|
362
388
|
feeJuicePortalAddress.toString(),
|
|
363
389
|
rewardDistributorAddress.toString(),
|
|
390
|
+
stakingAssetAddress.toString(),
|
|
364
391
|
args.vkTreeRoot.toString(),
|
|
365
392
|
args.protocolContractTreeRoot.toString(),
|
|
366
393
|
account.address.toString(),
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
logger.info(`Deployed Rollup at ${rollupAddress}`,
|
|
394
|
+
rollupConfigArgs,
|
|
395
|
+
];
|
|
396
|
+
const rollupAddress = await deployer.deploy(l1Artifacts.rollup, rollupArgs);
|
|
397
|
+
logger.info(`Deployed Rollup at ${rollupAddress}`, rollupConfigArgs);
|
|
371
398
|
|
|
372
399
|
await deployer.waitForDeployments();
|
|
373
400
|
logger.info(`All core contracts deployed`);
|
|
@@ -378,9 +405,15 @@ export const deployL1Contracts = async (
|
|
|
378
405
|
client: walletClient,
|
|
379
406
|
});
|
|
380
407
|
|
|
381
|
-
const
|
|
382
|
-
address:
|
|
383
|
-
abi: l1Artifacts.
|
|
408
|
+
const feeAsset = getContract({
|
|
409
|
+
address: feeAssetAddress.toString(),
|
|
410
|
+
abi: l1Artifacts.feeAsset.contractAbi,
|
|
411
|
+
client: walletClient,
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
const stakingAsset = getContract({
|
|
415
|
+
address: stakingAssetAddress.toString(),
|
|
416
|
+
abi: l1Artifacts.stakingAsset.contractAbi,
|
|
384
417
|
client: walletClient,
|
|
385
418
|
});
|
|
386
419
|
|
|
@@ -393,12 +426,40 @@ export const deployL1Contracts = async (
|
|
|
393
426
|
// Transaction hashes to await
|
|
394
427
|
const txHashes: Hex[] = [];
|
|
395
428
|
|
|
429
|
+
{
|
|
430
|
+
const txHash = await feeAsset.write.setFreeForAll([true], {} as any);
|
|
431
|
+
logger.info(`Fee asset set to free for all in ${txHash}`);
|
|
432
|
+
txHashes.push(txHash);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (args.initialValidators && args.initialValidators.length > 0) {
|
|
436
|
+
// Mint tokens, approve them, use cheat code to initialise validator set without setting up the epoch.
|
|
437
|
+
const stakeNeeded = MINIMUM_STAKE * BigInt(args.initialValidators.length);
|
|
438
|
+
await Promise.all(
|
|
439
|
+
[
|
|
440
|
+
await stakingAsset.write.mint([walletClient.account.address, stakeNeeded], {} as any),
|
|
441
|
+
await stakingAsset.write.approve([rollupAddress.toString(), stakeNeeded], {} as any),
|
|
442
|
+
].map(txHash => publicClient.waitForTransactionReceipt({ hash: txHash })),
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
const initiateValidatorSetTxHash = await rollup.write.cheat__InitialiseValidatorSet([
|
|
446
|
+
args.initialValidators.map(v => ({
|
|
447
|
+
attester: v.toString(),
|
|
448
|
+
proposer: v.toString(),
|
|
449
|
+
withdrawer: v.toString(),
|
|
450
|
+
amount: MINIMUM_STAKE,
|
|
451
|
+
})),
|
|
452
|
+
]);
|
|
453
|
+
txHashes.push(initiateValidatorSetTxHash);
|
|
454
|
+
logger.info(`Initialized validator set (${args.initialValidators.join(', ')}) in tx ${initiateValidatorSetTxHash}`);
|
|
455
|
+
}
|
|
456
|
+
|
|
396
457
|
// @note This value MUST match what is in `constants.nr`. It is currently specified here instead of just importing
|
|
397
458
|
// because there is circular dependency hell. This is a temporary solution. #3342
|
|
398
459
|
// @todo #8084
|
|
399
460
|
// fund the portal contract with Fee Juice
|
|
400
|
-
const FEE_JUICE_INITIAL_MINT =
|
|
401
|
-
const mintTxHash = await
|
|
461
|
+
const FEE_JUICE_INITIAL_MINT = 200000000000000000000n;
|
|
462
|
+
const mintTxHash = await feeAsset.write.mint([feeJuicePortalAddress.toString(), FEE_JUICE_INITIAL_MINT], {} as any);
|
|
402
463
|
|
|
403
464
|
// @note This is used to ensure we fully wait for the transaction when running against a real chain
|
|
404
465
|
// otherwise we execute subsequent transactions too soon
|
|
@@ -414,7 +475,7 @@ export const deployL1Contracts = async (
|
|
|
414
475
|
}
|
|
415
476
|
|
|
416
477
|
logger.info(
|
|
417
|
-
`Initialized Fee Juice Portal at ${feeJuicePortalAddress} to bridge between L1 ${
|
|
478
|
+
`Initialized Fee Juice Portal at ${feeJuicePortalAddress} to bridge between L1 ${feeAssetAddress} to L2 ${args.l2FeeJuiceAddress}`,
|
|
418
479
|
);
|
|
419
480
|
|
|
420
481
|
if (isAnvilTestChain(chain.id)) {
|
|
@@ -492,7 +553,8 @@ export const deployL1Contracts = async (
|
|
|
492
553
|
registryAddress,
|
|
493
554
|
inboxAddress,
|
|
494
555
|
outboxAddress,
|
|
495
|
-
feeJuiceAddress,
|
|
556
|
+
feeJuiceAddress: feeAssetAddress,
|
|
557
|
+
stakingAssetAddress,
|
|
496
558
|
feeJuicePortalAddress,
|
|
497
559
|
coinIssuerAddress,
|
|
498
560
|
rewardDistributorAddress,
|
|
@@ -607,10 +669,21 @@ export async function deployL1Contract(
|
|
|
607
669
|
logger?: DebugLogger,
|
|
608
670
|
): Promise<{ address: EthAddress; txHash: Hex | undefined }> {
|
|
609
671
|
let txHash: Hex | undefined = undefined;
|
|
610
|
-
let
|
|
672
|
+
let resultingAddress: Hex | null | undefined = undefined;
|
|
673
|
+
|
|
674
|
+
const l1TxUtils = new L1TxUtils(publicClient, walletClient, logger);
|
|
611
675
|
|
|
612
676
|
if (libraries) {
|
|
613
|
-
//
|
|
677
|
+
// Note that this does NOT work well for linked libraries having linked libraries.
|
|
678
|
+
|
|
679
|
+
// Verify that all link references have corresponding code
|
|
680
|
+
for (const linkRef in libraries.linkReferences) {
|
|
681
|
+
for (const contractName in libraries.linkReferences[linkRef]) {
|
|
682
|
+
if (!libraries.libraryCode[contractName]) {
|
|
683
|
+
throw new Error(`Missing library code for ${contractName}`);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
614
687
|
|
|
615
688
|
const replacements: Record<string, EthAddress> = {};
|
|
616
689
|
|
|
@@ -659,21 +732,31 @@ export async function deployL1Contract(
|
|
|
659
732
|
const salt = padHex(maybeSalt, { size: 32 });
|
|
660
733
|
const deployer: Hex = '0x4e59b44847b379578588920cA78FbF26c0B4956C';
|
|
661
734
|
const calldata = encodeDeployData({ abi, bytecode, args });
|
|
662
|
-
|
|
663
|
-
const existing = await publicClient.getBytecode({ address });
|
|
735
|
+
resultingAddress = getContractAddress({ from: deployer, salt, bytecode: calldata, opcode: 'CREATE2' });
|
|
736
|
+
const existing = await publicClient.getBytecode({ address: resultingAddress });
|
|
664
737
|
|
|
665
738
|
if (existing === undefined || existing === '0x') {
|
|
666
|
-
|
|
667
|
-
|
|
739
|
+
const res = await l1TxUtils.sendTransaction({
|
|
740
|
+
to: deployer,
|
|
741
|
+
data: concatHex([salt, calldata]),
|
|
742
|
+
});
|
|
743
|
+
txHash = res.txHash;
|
|
744
|
+
|
|
745
|
+
logger?.verbose(`Deployed contract with salt ${salt} to address ${resultingAddress} in tx ${txHash}.`);
|
|
668
746
|
} else {
|
|
669
|
-
logger?.verbose(`Skipping existing deployment of contract with salt ${salt} to address ${
|
|
747
|
+
logger?.verbose(`Skipping existing deployment of contract with salt ${salt} to address ${resultingAddress}`);
|
|
670
748
|
}
|
|
671
749
|
} else {
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
const receipt = await
|
|
675
|
-
|
|
676
|
-
|
|
750
|
+
// Regular deployment path
|
|
751
|
+
const deployData = encodeDeployData({ abi, bytecode, args });
|
|
752
|
+
const receipt = await l1TxUtils.sendAndMonitorTransaction({
|
|
753
|
+
to: null,
|
|
754
|
+
data: deployData,
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
txHash = receipt.transactionHash;
|
|
758
|
+
resultingAddress = receipt.contractAddress;
|
|
759
|
+
if (!resultingAddress) {
|
|
677
760
|
throw new Error(
|
|
678
761
|
`No contract address found in receipt: ${JSON.stringify(receipt, (_, val) =>
|
|
679
762
|
typeof val === 'bigint' ? String(val) : val,
|
|
@@ -682,6 +765,6 @@ export async function deployL1Contract(
|
|
|
682
765
|
}
|
|
683
766
|
}
|
|
684
767
|
|
|
685
|
-
return { address: EthAddress.fromString(
|
|
768
|
+
return { address: EthAddress.fromString(resultingAddress!), txHash };
|
|
686
769
|
}
|
|
687
770
|
// docs:end:deployL1Contract
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { toBigIntBE, toHex } from '@aztec/foundation/bigint-buffer';
|
|
2
|
+
import { keccak256 } from '@aztec/foundation/crypto';
|
|
3
|
+
import { type EthAddress } from '@aztec/foundation/eth-address';
|
|
4
|
+
import { createDebugLogger } from '@aztec/foundation/log';
|
|
5
|
+
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import { type Hex } from 'viem';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A class that provides utility functions for interacting with ethereum (L1).
|
|
11
|
+
*/
|
|
12
|
+
export class EthCheatCodes {
|
|
13
|
+
constructor(
|
|
14
|
+
/**
|
|
15
|
+
* The RPC URL to use for interacting with the chain
|
|
16
|
+
*/
|
|
17
|
+
public rpcUrl: string,
|
|
18
|
+
/**
|
|
19
|
+
* The logger to use for the eth cheatcodes
|
|
20
|
+
*/
|
|
21
|
+
public logger = createDebugLogger('aztec:cheat_codes:eth'),
|
|
22
|
+
) {}
|
|
23
|
+
|
|
24
|
+
async rpcCall(method: string, params: any[]) {
|
|
25
|
+
const paramsString = JSON.stringify(params);
|
|
26
|
+
const content = {
|
|
27
|
+
body: `{"jsonrpc":"2.0", "method": "${method}", "params": ${paramsString}, "id": 1}`,
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: { 'Content-Type': 'application/json' },
|
|
30
|
+
};
|
|
31
|
+
return await (await fetch(this.rpcUrl, content)).json();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get the auto mine status of the underlying chain
|
|
36
|
+
* @returns True if automine is on, false otherwise
|
|
37
|
+
*/
|
|
38
|
+
public async isAutoMining(): Promise<boolean> {
|
|
39
|
+
try {
|
|
40
|
+
const res = await this.rpcCall('anvil_getAutomine', []);
|
|
41
|
+
return res.result;
|
|
42
|
+
} catch (err) {
|
|
43
|
+
this.logger.error(`Calling "anvil_getAutomine" failed with:`, err);
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get the current blocknumber
|
|
50
|
+
* @returns The current block number
|
|
51
|
+
*/
|
|
52
|
+
public async blockNumber(): Promise<number> {
|
|
53
|
+
const res = await this.rpcCall('eth_blockNumber', []);
|
|
54
|
+
return parseInt(res.result, 16);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get the current chainId
|
|
59
|
+
* @returns The current chainId
|
|
60
|
+
*/
|
|
61
|
+
public async chainId(): Promise<number> {
|
|
62
|
+
const res = await this.rpcCall('eth_chainId', []);
|
|
63
|
+
return parseInt(res.result, 16);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get the current timestamp
|
|
68
|
+
* @returns The current timestamp
|
|
69
|
+
*/
|
|
70
|
+
public async timestamp(): Promise<number> {
|
|
71
|
+
const res = await this.rpcCall('eth_getBlockByNumber', ['latest', true]);
|
|
72
|
+
return parseInt(res.result.timestamp, 16);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Advance the chain by a number of blocks
|
|
77
|
+
* @param numberOfBlocks - The number of blocks to mine
|
|
78
|
+
*/
|
|
79
|
+
public async mine(numberOfBlocks = 1): Promise<void> {
|
|
80
|
+
const res = await this.rpcCall('hardhat_mine', [numberOfBlocks]);
|
|
81
|
+
if (res.error) {
|
|
82
|
+
throw new Error(`Error mining: ${res.error.message}`);
|
|
83
|
+
}
|
|
84
|
+
this.logger.verbose(`Mined ${numberOfBlocks} L1 blocks`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Mines a single block with evm_mine
|
|
89
|
+
*/
|
|
90
|
+
public async evmMine(): Promise<void> {
|
|
91
|
+
const res = await this.rpcCall('evm_mine', []);
|
|
92
|
+
if (res.error) {
|
|
93
|
+
throw new Error(`Error mining: ${res.error.message}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Set the balance of an account
|
|
99
|
+
* @param account - The account to set the balance for
|
|
100
|
+
* @param balance - The balance to set
|
|
101
|
+
*/
|
|
102
|
+
public async setBalance(account: EthAddress, balance: bigint): Promise<void> {
|
|
103
|
+
const res = await this.rpcCall('anvil_setBalance', [account.toString(), toHex(balance)]);
|
|
104
|
+
if (res.error) {
|
|
105
|
+
throw new Error(`Error setting balance for ${account}: ${res.error.message}`);
|
|
106
|
+
}
|
|
107
|
+
this.logger.verbose(`Set balance for ${account} to ${balance}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Set the interval between blocks (block time)
|
|
112
|
+
* @param interval - The interval to use between blocks
|
|
113
|
+
*/
|
|
114
|
+
public async setBlockInterval(interval: number): Promise<void> {
|
|
115
|
+
const res = await this.rpcCall('anvil_setBlockTimestampInterval', [interval]);
|
|
116
|
+
if (res.error) {
|
|
117
|
+
throw new Error(`Error setting block interval: ${res.error.message}`);
|
|
118
|
+
}
|
|
119
|
+
this.logger.verbose(`Set L1 block interval to ${interval}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Set the next block base fee per gas
|
|
124
|
+
* @param baseFee - The base fee to set
|
|
125
|
+
*/
|
|
126
|
+
public async setNextBlockBaseFeePerGas(baseFee: bigint): Promise<void> {
|
|
127
|
+
const res = await this.rpcCall('anvil_setNextBlockBaseFeePerGas', [baseFee.toString()]);
|
|
128
|
+
if (res.error) {
|
|
129
|
+
throw new Error(`Error setting next block base fee per gas: ${res.error.message}`);
|
|
130
|
+
}
|
|
131
|
+
this.logger.verbose(`Set L1 next block base fee per gas to ${baseFee}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Set the interval between blocks (block time)
|
|
136
|
+
* @param seconds - The interval to use between blocks
|
|
137
|
+
*/
|
|
138
|
+
public async setIntervalMining(seconds: number): Promise<void> {
|
|
139
|
+
const res = await this.rpcCall('anvil_setIntervalMining', [seconds]);
|
|
140
|
+
if (res.error) {
|
|
141
|
+
throw new Error(`Error setting interval mining: ${res.error.message}`);
|
|
142
|
+
}
|
|
143
|
+
this.logger.verbose(`Set L1 interval mining to ${seconds} seconds`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Set the automine status of the underlying anvil chain
|
|
148
|
+
* @param automine - The automine status to set
|
|
149
|
+
*/
|
|
150
|
+
public async setAutomine(automine: boolean): Promise<void> {
|
|
151
|
+
const res = await this.rpcCall('anvil_setAutomine', [automine]);
|
|
152
|
+
if (res.error) {
|
|
153
|
+
throw new Error(`Error setting automine: ${res.error.message}`);
|
|
154
|
+
}
|
|
155
|
+
this.logger.verbose(`Set L1 automine to ${automine}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Drop a transaction from the mempool
|
|
160
|
+
* @param txHash - The transaction hash
|
|
161
|
+
*/
|
|
162
|
+
public async dropTransaction(txHash: Hex): Promise<void> {
|
|
163
|
+
const res = await this.rpcCall('anvil_dropTransaction', [txHash]);
|
|
164
|
+
if (res.error) {
|
|
165
|
+
throw new Error(`Error dropping transaction: ${res.error.message}`);
|
|
166
|
+
}
|
|
167
|
+
this.logger.verbose(`Dropped transaction ${txHash}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Set the next block timestamp
|
|
172
|
+
* @param timestamp - The timestamp to set the next block to
|
|
173
|
+
*/
|
|
174
|
+
public async setNextBlockTimestamp(timestamp: number): Promise<void> {
|
|
175
|
+
const res = await this.rpcCall('evm_setNextBlockTimestamp', [timestamp]);
|
|
176
|
+
if (res.error) {
|
|
177
|
+
throw new Error(`Error setting next block timestamp: ${res.error.message}`);
|
|
178
|
+
}
|
|
179
|
+
this.logger.verbose(`Set L1 next block timestamp to ${timestamp}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Set the next block timestamp and mines the block
|
|
184
|
+
* @param timestamp - The timestamp to set the next block to
|
|
185
|
+
*/
|
|
186
|
+
public async warp(timestamp: number | bigint): Promise<void> {
|
|
187
|
+
const res = await this.rpcCall('evm_setNextBlockTimestamp', [Number(timestamp)]);
|
|
188
|
+
if (res.error) {
|
|
189
|
+
throw new Error(`Error warping: ${res.error.message}`);
|
|
190
|
+
}
|
|
191
|
+
await this.mine();
|
|
192
|
+
this.logger.verbose(`Warped L1 timestamp to ${timestamp}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Dumps the current chain state to a file.
|
|
197
|
+
* @param fileName - The file name to dump state into
|
|
198
|
+
*/
|
|
199
|
+
public async dumpChainState(fileName: string): Promise<void> {
|
|
200
|
+
const res = await this.rpcCall('hardhat_dumpState', []);
|
|
201
|
+
if (res.error) {
|
|
202
|
+
throw new Error(`Error dumping state: ${res.error.message}`);
|
|
203
|
+
}
|
|
204
|
+
const jsonContent = JSON.stringify(res.result);
|
|
205
|
+
fs.writeFileSync(`${fileName}.json`, jsonContent, 'utf8');
|
|
206
|
+
this.logger.verbose(`Dumped state to ${fileName}`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Loads the chain state from a file.
|
|
211
|
+
* @param fileName - The file name to load state from
|
|
212
|
+
*/
|
|
213
|
+
public async loadChainState(fileName: string): Promise<void> {
|
|
214
|
+
const data = JSON.parse(fs.readFileSync(`${fileName}.json`, 'utf8'));
|
|
215
|
+
const res = await this.rpcCall('hardhat_loadState', [data]);
|
|
216
|
+
if (res.error) {
|
|
217
|
+
throw new Error(`Error loading state: ${res.error.message}`);
|
|
218
|
+
}
|
|
219
|
+
this.logger.verbose(`Loaded state from ${fileName}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Load the value at a storage slot of a contract address on eth
|
|
224
|
+
* @param contract - The contract address
|
|
225
|
+
* @param slot - The storage slot
|
|
226
|
+
* @returns - The value at the storage slot
|
|
227
|
+
*/
|
|
228
|
+
public async load(contract: EthAddress, slot: bigint): Promise<bigint> {
|
|
229
|
+
const res = await this.rpcCall('eth_getStorageAt', [contract.toString(), toHex(slot), 'latest']);
|
|
230
|
+
return BigInt(res.result);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Set the value at a storage slot of a contract address on eth
|
|
235
|
+
* @param contract - The contract address
|
|
236
|
+
* @param slot - The storage slot
|
|
237
|
+
* @param value - The value to set the storage slot to
|
|
238
|
+
*/
|
|
239
|
+
public async store(contract: EthAddress, slot: bigint, value: bigint): Promise<void> {
|
|
240
|
+
// for the rpc call, we need to change value to be a 32 byte hex string.
|
|
241
|
+
const res = await this.rpcCall('hardhat_setStorageAt', [contract.toString(), toHex(slot), toHex(value, true)]);
|
|
242
|
+
if (res.error) {
|
|
243
|
+
throw new Error(`Error setting storage for contract ${contract} at ${slot}: ${res.error.message}`);
|
|
244
|
+
}
|
|
245
|
+
this.logger.verbose(`Set L1 storage for contract ${contract} at ${slot} to ${value}`);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Computes the slot value for a given map and key.
|
|
250
|
+
* @param baseSlot - The base slot of the map (specified in Aztec.nr contract)
|
|
251
|
+
* @param key - The key to lookup in the map
|
|
252
|
+
* @returns The storage slot of the value in the map
|
|
253
|
+
*/
|
|
254
|
+
public keccak256(baseSlot: bigint, key: bigint): bigint {
|
|
255
|
+
// abi encode (removing the 0x) - concat key and baseSlot (both padded to 32 bytes)
|
|
256
|
+
const abiEncoded = toHex(key, true).substring(2) + toHex(baseSlot, true).substring(2);
|
|
257
|
+
return toBigIntBE(keccak256(Buffer.from(abiEncoded, 'hex')));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Send transactions impersonating an externally owned account or contract.
|
|
262
|
+
* @param who - The address to impersonate
|
|
263
|
+
*/
|
|
264
|
+
public async startImpersonating(who: EthAddress | Hex): Promise<void> {
|
|
265
|
+
const res = await this.rpcCall('hardhat_impersonateAccount', [who.toString()]);
|
|
266
|
+
if (res.error) {
|
|
267
|
+
throw new Error(`Error impersonating ${who}: ${res.error.message}`);
|
|
268
|
+
}
|
|
269
|
+
this.logger.verbose(`Impersonating ${who}`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Stop impersonating an account that you are currently impersonating.
|
|
274
|
+
* @param who - The address to stop impersonating
|
|
275
|
+
*/
|
|
276
|
+
public async stopImpersonating(who: EthAddress | Hex): Promise<void> {
|
|
277
|
+
const res = await this.rpcCall('hardhat_stopImpersonatingAccount', [who.toString()]);
|
|
278
|
+
if (res.error) {
|
|
279
|
+
throw new Error(`Error when stopping the impersonation of ${who}: ${res.error.message}`);
|
|
280
|
+
}
|
|
281
|
+
this.logger.verbose(`Stopped impersonating ${who}`);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Set the bytecode for a contract
|
|
286
|
+
* @param contract - The contract address
|
|
287
|
+
* @param bytecode - The bytecode to set
|
|
288
|
+
*/
|
|
289
|
+
public async etch(contract: EthAddress, bytecode: `0x${string}`): Promise<void> {
|
|
290
|
+
const res = await this.rpcCall('hardhat_setCode', [contract.toString(), bytecode]);
|
|
291
|
+
if (res.error) {
|
|
292
|
+
throw new Error(`Error setting bytecode for ${contract}: ${res.error.message}`);
|
|
293
|
+
}
|
|
294
|
+
this.logger.verbose(`Set bytecode for ${contract} to ${bytecode}`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Get the bytecode for a contract
|
|
299
|
+
* @param contract - The contract address
|
|
300
|
+
* @returns The bytecode for the contract
|
|
301
|
+
*/
|
|
302
|
+
public async getBytecode(contract: EthAddress): Promise<`0x${string}`> {
|
|
303
|
+
const res = await this.rpcCall('eth_getCode', [contract.toString(), 'latest']);
|
|
304
|
+
return res.result;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Get the raw transaction object for a given transaction hash
|
|
309
|
+
* @param txHash - The transaction hash
|
|
310
|
+
* @returns The raw transaction
|
|
311
|
+
*/
|
|
312
|
+
public async getRawTransaction(txHash: Hex): Promise<`0x${string}`> {
|
|
313
|
+
const res = await this.rpcCall('debug_getRawTransaction', [txHash]);
|
|
314
|
+
return res.result;
|
|
315
|
+
}
|
|
316
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
export * from './constants.js';
|
|
2
2
|
export * from './deploy_l1_contracts.js';
|
|
3
|
+
export * from './ethereum_chain.js';
|
|
4
|
+
export * from './eth_cheat_codes.js';
|
|
5
|
+
export * from './l1_tx_utils.js';
|
|
3
6
|
export * from './l1_contract_addresses.js';
|
|
4
7
|
export * from './l1_reader.js';
|
|
5
|
-
export * from './ethereum_chain.js';
|
|
6
8
|
export * from './utils.js';
|
|
7
9
|
export * from './config.js';
|
|
8
10
|
export * from './types.js';
|
|
11
|
+
export * from './contracts/index.js';
|
|
@@ -20,6 +20,7 @@ export const L1ContractsNames = [
|
|
|
20
20
|
'rewardDistributorAddress',
|
|
21
21
|
'governanceProposerAddress',
|
|
22
22
|
'governanceAddress',
|
|
23
|
+
'stakingAssetAddress',
|
|
23
24
|
] as const;
|
|
24
25
|
|
|
25
26
|
/** Provides the directory of current L1 contract addresses */
|
|
@@ -33,6 +34,7 @@ export const L1ContractAddressesSchema = z.object({
|
|
|
33
34
|
inboxAddress: schemas.EthAddress,
|
|
34
35
|
outboxAddress: schemas.EthAddress,
|
|
35
36
|
feeJuiceAddress: schemas.EthAddress,
|
|
37
|
+
stakingAssetAddress: schemas.EthAddress,
|
|
36
38
|
feeJuicePortalAddress: schemas.EthAddress,
|
|
37
39
|
coinIssuerAddress: schemas.EthAddress,
|
|
38
40
|
rewardDistributorAddress: schemas.EthAddress,
|
|
@@ -68,6 +70,11 @@ export const l1ContractAddressesMapping: ConfigMappingsType<L1ContractAddresses>
|
|
|
68
70
|
description: 'The deployed L1 Fee Juice contract address.',
|
|
69
71
|
parseEnv,
|
|
70
72
|
},
|
|
73
|
+
stakingAssetAddress: {
|
|
74
|
+
env: 'STAKING_ASSET_CONTRACT_ADDRESS',
|
|
75
|
+
description: 'The deployed L1 staking asset contract address.',
|
|
76
|
+
parseEnv,
|
|
77
|
+
},
|
|
71
78
|
feeJuicePortalAddress: {
|
|
72
79
|
env: 'FEE_JUICE_PORTAL_CONTRACT_ADDRESS',
|
|
73
80
|
description: 'The deployed L1 Fee Juice portal contract address.',
|