diff --git a/docs/docs/dev_docs/testing/cheat_codes.md b/docs/docs/dev_docs/testing/cheat_codes.md index e69de29bb2d..4a09ac7f811 100644 --- a/docs/docs/dev_docs/testing/cheat_codes.md +++ b/docs/docs/dev_docs/testing/cheat_codes.md @@ -0,0 +1,466 @@ +--- +title: Cheat Codes +--- + +## Introduction + +To help with testing, the sandbox is shipped with a set of cheatcodes. + +Cheatcodes allow you to change the time of the Aztec block, load certain state or more easily manipulate Ethereum instead of having to write dedicated RPC calls to anvil or hardhat. + +:::info Prerequisites +If you aren't familiar with [Anvil](https://book.getfoundry.sh/anvil/), we recommend reading up on that since Aztec Sandbox uses Anvil as the local Ethereum instance. +::: + +### Aims + +The guide will cover how to manipulate the state of the: +- Ethereum blockchain; +- Aztec network. + +### Dependencies + +For this guide, the following Aztec packages are used: +- @aztec/aztec.js + +### Initialisation +```ts +import { createAztecRpcClient, CheatCodes } from '@aztec/aztec.js'; +const aztecRpcUrl = 'http://localhost:8080'; +const aztecRpcClient = createAztecRpcClient(aztecRpcUrl); +const cc = await CheatCodes.create(aztecRpcUrl, aztecRpcClient); +``` + +There are two properties of the CheatCodes class - `eth` and `aztec` for cheatcodes relating to the Ethereum blockchain (L1) and the Aztec network (L2) respectively. + +## Ethereum related cheatcodes +These are cheatcodes exposed from anvil/hardhat conveniently wrapped for ease of use in the Sandbox. + +### Interface +```ts +// Fetch current block number of Ethereum +public async blockNumber(): Promise + +// Fetch chain ID of the local Ethereum instance +public async chainId(): Promise + +// Fetch current timestamp on Ethereum +public async timestamp(): Promise + +// Mine a given number of blocks on Ethereum. Mines 1 block by default +public async mine(numberOfBlocks = 1): Promise + +// Set the timestamp for the next block on Ethereum. +public async setNextBlockTimestamp(timestamp: number): Promise + +// Dumps the current Ethereum chain state to a given file. +public async dumpChainState(fileName: string): Promise + +// Loads the Ethereum chain state from a file. You may use `dumpChainState()` to save the state of the Ethereum chain to a file and later load it. +public async loadChainState(fileName: string): Promise + +// Load the value at a storage slot of a contract address on Ethereum +public async load(contract: EthAddress, slot: bigint): Promise + +// Set the value at a storage slot of a contract address on Ethereum (e.g. modify a storage variable on your portal contract or even the rollup contract). +public async store(contract: EthAddress, slot: bigint, value: bigint): Promise + +// Computes the slot value for a given map and key on Ethereum. A convenient wrapper to find the appropriate storage slot to load or overwrite the state. +public keccak256(baseSlot: bigint, key: bigint): bigint + +// Let you send transactions on Ethereum impersonating an externally owned or contract, without knowing the private key. +public async startImpersonating(who: EthAddress): Promise + +// Stop impersonating an account on Ethereum that you are currently impersonating. +public async stopImpersonating(who: EthAddress): Promise + +// Set the bytecode for a Ethereum contract +public async etch(contract: EthAddress, bytecode: `0x${string}`): Promise + +// Get the bytecode for a Ethereum contract +public async getBytecode(contract: EthAddress): Promise<`0x${string}`> +``` + +### blockNumber + +#### Function Signature +```ts +public async blockNumber(): Promise +``` + +#### Description +Fetches the current Ethereum block number. + +#### Example +```ts +const blockNumber = await cc.eth.blockNumber() +``` + +### chainId + +#### Function Signature +```ts +public async chainId(): Promise +``` + +#### Description +Fetches the Ethereum chain ID + +#### Example +```ts +const chainId = await cc.eth.chainId() +``` + +### timestamp + +#### Function Signature +```ts +public async timestamp(): Promise +``` + +#### Description +Fetches the current Ethereum timestamp. + +#### Example +```ts +const timestamp = await cc.eth.timestamp() +``` + +### mine + +#### Function Signature +```ts +public async mine(numberOfBlocks = 1): Promise +``` + +#### Description +Mines the specified number of blocks on Ethereum (default 1). + +#### Example +```ts +const blockNum = await cc.eth.blockNumber(); +await cc.eth.mine(10) // mines 10 blocks +const newBlockNum = await cc.eth.blockNumber(); // = blockNum + 10. +``` + +### setNextBlockTimestamp + +#### Function Signature +```ts +public async setNextBlockTimestamp(timestamp: number): Promise +``` + +#### Description +Sets the timestamp (unix format in seconds) for the next mined block on Ethereum. +Time can only be set in the future. +If you set the timestamp to a time in the past, this method will throw an error. + +#### Example +```ts +// // Set next block timestamp to 16 Aug 2023 10:54:30 GMT +await cc.eth.setNextBlockTimestamp(1692183270) +// next transaction you will do will have the timestamp as 1692183270 +``` + +### dumpChainState + +#### Function Signature +```ts +public async dumpChainState(fileName: string): Promise +``` + +#### Description +Dumps the current Ethereum chain state to a file. +Stores a hex string representing the complete state of the chain in a file with the provided path. Can be re-imported into a fresh/restarted instance of Anvil to reattain the same state. +When combined with `loadChainState()` cheatcode, it can be let you easily import the current state of mainnet into the Anvil instance of the sandbox. + +#### Example +```ts +await cc.eth.dumpChainState('chain-state.json') +``` + +### loadChainState + +#### Function Signature +```ts +public async loadChainState(fileName: string): Promise +``` + +#### Description +Loads the Ethereum chain state from a file which contains a hex string representing an Ethereum state. +When given a file previously written to by `cc.eth.dumpChainState()`, it merges the contents into the current chain state. Will overwrite any colliding accounts/storage slots. + +#### Example +```ts +await cc.eth.loadChainState('chain-state.json') +``` + +### load + +#### Function Signature +```ts +public async load(contract: EthAddress, slot: bigint): Promise +``` + +#### Description +Loads the value at a storage slot of a Ethereum contract. + +#### Example +```solidity +contract LeetContract { + uint256 private leet = 1337; // slot 0 +} +``` + +```ts +const leetContractAddress = EthAddress.fromString('0x1234...'); +const value = await cc.eth.load(leetContractAddress, BigInt(0)); +console.log(value); // 1337 +``` + +### store + +#### Function Signature +```ts +public async store(contract: EthAddress, slot: bigint, value: bigint): Promise +``` + +#### Description +Stores the value in storage slot on a Ethereum contract. + +#### Example +```solidity +contract LeetContract { + uint256 private leet = 1337; // slot 0 +} +``` + +```ts +const leetContractAddress = EthAddress.fromString('0x1234...'); +await cc.eth.store(leetContractAddress, BigInt(0), BigInt(1000)); +const value = await cc.eth.load(leetContractAddress, BigInt(0)); +console.log(value); // 1000 +``` + +### keccak256 + +#### Function Signature +```ts +public keccak256(baseSlot: bigint, key: bigint): bigint +``` + +#### Description +Computes the storage slot for a map key. + +#### Example +```solidity +contract LeetContract { + uint256 private leet = 1337; // slot 0 + mapping(address => uint256) public balances; // base slot 1 +} +``` + +```ts +// find the storage slot for key `0xdead` in the balance map. +const address = BigInt('0x000000000000000000000000000000000000dead'); +const slot = cc.eth.keccak256(1n, address); +// store balance of 0xdead as 100 +await cc.eth.store(contractAddress, slot, 100n); +``` + +### startImpersonating + +#### Function Signature +```ts +public async startImpersonating(who: EthAddress): Promise +``` + +#### Description +Start impersonating an Ethereum account. +This allows you to use this address as a sender. + +#### Example +```ts +await cc.eth.startImpersonating(EthAddress.fromString(address)); +``` + +### stopImpersonating + +#### Function Signature +```ts +public async stopImpersonating(who: EthAddress): Promise +``` + +#### Description +Stop impersonating an Ethereum account. +Stops an active impersonation started by startImpersonating. + +#### Example +```ts +await cc.eth.stopImpersonating(EthAddress.fromString(address)) +``` + +### getBytecode + +#### Function Signature +```ts +public async getBytecode(contract: EthAddress): Promise<`0x${string}`> +``` + +#### Description +Get the bytecode for an Ethereum contract. + +#### Example +```ts +const bytecode = await cc.eth.getBytecode(contract) // 0x6080604052348015610010... +``` + +### etch + +#### Function Signature +```ts +public async etch(contract: EthAddress, bytecode: `0x${string}`): Promise +``` + +#### Description +Set the bytecode for an Ethereum contract. + +#### Example +```ts +const bytecode = `0x6080604052348015610010...` +await cc.eth.etch(contract, bytecode) +console.log(await cc.eth.getBytecode(contract)) // 0x6080604052348015610010... +``` + +## Aztec related cheatcodes +These are cheatcodes specific to manipulating the state of Aztec rollup. + +### Interface +```ts +// Get the current aztec block number +public async blockNumber(): Promise + +// Set time of the next execution on aztec. It also modifies time on Ethereum for next execution and stores this time as the last rollup block on the rollup contract. +public async warp(to: number): Promise + +// Loads the value stored at the given slot in the public storage of the given contract. +public async loadPublic(who: AztecAddress, slot: Fr | bigint): Promise + +// Computes the slot value for a given map and key. +public computeSlotInMap(baseSlot: Fr | bigint, key: Fr | bigint): Fr +``` + +### blockNumber + +#### Function Signature +```ts +public async blockNumber(): Promise +``` + +#### Description +Get the current aztec block number. + +#### Example +```ts +const blockNumber = await cc.aztec.blockNumber() +``` + +### warp + +#### Function Signature +```ts +public async warp(to: number): Promise +``` + +#### Description +Sets the time on Ethereum and the time of the next block on Aztec. +Like with the corresponding Ethereum cheatcode, time can only be set in the future, not the past. +Otherwise, it will throw an error. + +#### Example +```ts +const timestamp = await cc.eth.timestamp(); +const newTimestamp = timestamp + 100_000_000; +await cc.aztec.warp(newTimestamp); +// any noir contract calls that make use of current timestamp +// and is executed in the next rollup block will now read `newTimestamp` +``` + +### computeSlotInMap + +#### Function Signature +```ts +public computeSlotInMap(baseSlot: Fr | bigint, key: Fr | bigint): Fr +``` + +#### Description +Compute storage slot for a map key. +The baseSlot is specified in the noir contract. + +#### Example +```rust +struct Storage { + balances: Map>, +} + +impl Storage { + fn init() -> Self { + Storage { + balances: Map::new(1, |slot| PublicState::new(slot, FieldSerialisationMethods)), + } + } +} + +contract Token { + ... +} +``` + +```ts +const slot = cc.aztec.computeSlotInMap(1n, key) +``` + +### loadPublic + +#### Function Signature +```ts +public async loadPublic(who: AztecAddress, slot: Fr | bigint): Promise +``` + +#### Description +Loads the value stored at the given slot in the public storage of the given contract. + +Note: One Field element occupies a storage slot. Hence, structs with multiple field elements will be spread over multiple sequential slots. Using loadPublic will only load a single field of the struct (depending on the size of the attributes within it). + +#### Example +```rust +struct Storage { + balances: Map>, +} + +impl Storage { + fn init() -> Self { + Storage { + balances: Map::new(1, |slot| PublicState::new(slot, FieldSerialisationMethods)), + } + } +} + +contract Token { + ... +} +``` + +```ts +const address = AztecAddress.fromString("0x123...") +const slot = cc.aztec.computeSlotInMap(1n, key) +const value = await cc.aztec.loadPublic(address, slot); +``` +## Participate + +Keep up with the latest discussion and join the conversation in the [Aztec forum](https://discourse.aztec.network). + +You can also use the above link to request more cheatcodes. + +import Disclaimer from "../../misc/common/\_disclaimer.mdx"; + \ No newline at end of file diff --git a/yarn-project/aztec.js/src/utils/cheat_codes.ts b/yarn-project/aztec.js/src/utils/cheat_codes.ts index 07368e789c8..bf296344b90 100644 --- a/yarn-project/aztec.js/src/utils/cheat_codes.ts +++ b/yarn-project/aztec.js/src/utils/cheat_codes.ts @@ -17,35 +17,35 @@ const toFr = (value: Fr | bigint): Fr => { export class CheatCodes { constructor( /** - * The L1 cheat codes. + * The cheat codes for ethereum (L1). */ - public l1: L1CheatCodes, + public eth: EthCheatCodes, /** - * The L2 cheat codes. + * The cheat codes for aztec. */ - public l2: L2CheatCodes, + public aztec: AztecCheatCodes, ) {} static async create(rpcUrl: string, aztecRpc: AztecRPC): Promise { - const l1CheatCodes = new L1CheatCodes(rpcUrl); - const l2CheatCodes = new L2CheatCodes(aztecRpc, await CircuitsWasm.get(), l1CheatCodes); - return new CheatCodes(l1CheatCodes, l2CheatCodes); + const ethCheatCodes = new EthCheatCodes(rpcUrl); + const aztecCheatCodes = new AztecCheatCodes(aztecRpc, await CircuitsWasm.get(), ethCheatCodes); + return new CheatCodes(ethCheatCodes, aztecCheatCodes); } } /** - * A class that provides utility functions for interacting with the L1 chain. + * A class that provides utility functions for interacting with ethereum (L1). */ -export class L1CheatCodes { +export class EthCheatCodes { constructor( /** * The RPC client to use for interacting with the chain */ public rpcUrl: string, /** - * The logger to use for the l1 cheatcodes + * The logger to use for the eth cheatcodes */ - public logger = createDebugLogger('aztec:cheat_codes:l1'), + public logger = createDebugLogger('aztec:cheat_codes:eth'), ) {} async rpcCall(method: string, params: any[]) { @@ -91,7 +91,7 @@ export class L1CheatCodes { * @returns The current chainId */ public async mine(numberOfBlocks = 1): Promise { - const res = await this.rpcCall('anvil_mine', [numberOfBlocks]); + const res = await this.rpcCall('hardhat_mine', [numberOfBlocks]); if (res.error) throw new Error(`Error mining: ${res.error.message}`); this.logger(`Mined ${numberOfBlocks} blocks`); } @@ -101,7 +101,7 @@ export class L1CheatCodes { * @param timestamp - The timestamp to set the next block to */ public async setNextBlockTimestamp(timestamp: number): Promise { - const res = await this.rpcCall('anvil_setNextBlockTimestamp', [timestamp]); + const res = await this.rpcCall('evm_setNextBlockTimestamp', [timestamp]); if (res.error) throw new Error(`Error setting next block timestamp: ${res.error.message}`); this.logger(`Set next block timestamp to ${timestamp}`); } @@ -111,7 +111,7 @@ export class L1CheatCodes { * @param fileName - The file name to dump state into */ public async dumpChainState(fileName: string): Promise { - const res = await this.rpcCall('anvil_dumpState', []); + const res = await this.rpcCall('hardhat_dumpState', []); if (res.error) throw new Error(`Error dumping state: ${res.error.message}`); const jsonContent = JSON.stringify(res.result); fs.writeFileSync(`${fileName}.json`, jsonContent, 'utf8'); @@ -124,13 +124,13 @@ export class L1CheatCodes { */ public async loadChainState(fileName: string): Promise { const data = JSON.parse(fs.readFileSync(`${fileName}.json`, 'utf8')); - const res = await this.rpcCall('anvil_loadState', [data]); + const res = await this.rpcCall('hardhat_loadState', [data]); if (res.error) throw new Error(`Error loading state: ${res.error.message}`); this.logger(`Loaded state from ${fileName}`); } /** - * Load the value at a storage slot of a contract address on L1 + * Load the value at a storage slot of a contract address on eth * @param contract - The contract address * @param slot - The storage slot * @returns - The value at the storage slot @@ -141,14 +141,14 @@ export class L1CheatCodes { } /** - * Set the value at a storage slot of a contract address on L1 + * Set the value at a storage slot of a contract address on eth * @param contract - The contract address * @param slot - The storage slot * @param value - The value to set the storage slot to */ public async store(contract: EthAddress, slot: bigint, value: bigint): Promise { // for the rpc call, we need to change value to be a 32 byte hex string. - const res = await this.rpcCall('anvil_setStorageAt', [contract.toString(), toHex(slot), toHex(value, true)]); + const res = await this.rpcCall('hardhat_setStorageAt', [contract.toString(), toHex(slot), toHex(value, true)]); if (res.error) throw new Error(`Error setting storage for contract ${contract} at ${slot}: ${res.error.message}`); this.logger(`Set storage for contract ${contract} at ${slot} to ${value}`); } @@ -169,9 +169,9 @@ export class L1CheatCodes { * Send transactions impersonating an externally owned account or contract. * @param who - The address to impersonate */ - public async startPrank(who: EthAddress): Promise { - const res = await this.rpcCall('anvil_impersonateAccount', [who.toString()]); - if (res.error) throw new Error(`Error pranking ${who}: ${res.error.message}`); + public async startImpersonating(who: EthAddress): Promise { + const res = await this.rpcCall('hardhat_impersonateAccount', [who.toString()]); + if (res.error) throw new Error(`Error impersonating ${who}: ${res.error.message}`); this.logger(`Impersonating ${who}`); } @@ -179,9 +179,9 @@ export class L1CheatCodes { * Stop impersonating an account that you are currently impersonating. * @param who - The address to stop impersonating */ - public async stopPrank(who: EthAddress): Promise { - const res = await this.rpcCall('anvil_stopImpersonatingAccount', [who.toString()]); - if (res.error) throw new Error(`Error pranking ${who}: ${res.error.message}`); + public async stopImpersonating(who: EthAddress): Promise { + const res = await this.rpcCall('hardhat_stopImpersonatingAccount', [who.toString()]); + if (res.error) throw new Error(`Error when stopping the impersonation of ${who}: ${res.error.message}`); this.logger(`Stopped impersonating ${who}`); } @@ -191,7 +191,7 @@ export class L1CheatCodes { * @param bytecode - The bytecode to set */ public async etch(contract: EthAddress, bytecode: `0x${string}`): Promise { - const res = await this.rpcCall('anvil_setCode', [contract.toString(), bytecode]); + const res = await this.rpcCall('hardhat_setCode', [contract.toString(), bytecode]); if (res.error) throw new Error(`Error setting bytecode for ${contract}: ${res.error.message}`); this.logger(`Set bytecode for ${contract} to ${bytecode}`); } @@ -208,9 +208,9 @@ export class L1CheatCodes { } /** - * A class that provides utility functions for interacting with the L2 chain. + * A class that provides utility functions for interacting with the aztec chain. */ -export class L2CheatCodes { +export class AztecCheatCodes { constructor( /** * The RPC client to use for interacting with the chain @@ -221,13 +221,13 @@ export class L2CheatCodes { */ public wasm: CircuitsWasm, /** - * The L1 cheat codes. + * The eth cheat codes. */ - public l1: L1CheatCodes, + public eth: EthCheatCodes, /** - * The logger to use for the l2 cheatcodes + * The logger to use for the aztec cheatcodes */ - public logger = createDebugLogger('aztec:cheat_codes:l2'), + public logger = createDebugLogger('aztec:cheat_codes:aztec'), ) {} /** @@ -256,16 +256,16 @@ export class L2CheatCodes { } /** - * Set time of the next execution on L2. - * It also modifies time on L1 for next execution and stores this time as the last rollup block on the rollup contract. + * Set time of the next execution on aztec. + * It also modifies time on eth for next execution and stores this time as the last rollup block on the rollup contract. * @param to - The timestamp to set the next block to (must be greater than current time) */ public async warp(to: number): Promise { const rollupContract = (await this.aztecRpc.getNodeInfo()).rollupAddress; - await this.l1.setNextBlockTimestamp(to); + await this.eth.setNextBlockTimestamp(to); // also store this time on the rollup contract (slot 1 tracks `lastBlockTs`). // This is because when the sequencer executes public functions, it uses the timestamp stored in the rollup contract. - await this.l1.store(rollupContract, 1n, BigInt(to)); + await this.eth.store(rollupContract, 1n, BigInt(to)); } /** diff --git a/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts b/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts index 38a9adb19dc..f68071ab996 100644 --- a/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts +++ b/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts @@ -41,34 +41,42 @@ describe('e2e_cheat_codes', () => { describe('L1 only', () => { describe('mine', () => { it(`mine block`, async () => { - const blockNumber = await cc.l1.blockNumber(); - await cc.l1.mine(); - expect(await cc.l1.blockNumber()).toBe(blockNumber + 1); + const blockNumber = await cc.eth.blockNumber(); + await cc.eth.mine(); + expect(await cc.eth.blockNumber()).toBe(blockNumber + 1); }); it.each([10, 42, 99])(`mine blocks`, async increment => { - const blockNumber = await cc.l1.blockNumber(); - await cc.l1.mine(increment); - expect(await cc.l1.blockNumber()).toBe(blockNumber + increment); + const blockNumber = await cc.eth.blockNumber(); + await cc.eth.mine(increment); + expect(await cc.eth.blockNumber()).toBe(blockNumber + increment); }); }); it.each([100, 42, 99])('setNextBlockTimestamp', async increment => { - const blockNumber = await cc.l1.blockNumber(); - const timestamp = await cc.l1.timestamp(); - await cc.l1.setNextBlockTimestamp(timestamp + increment); + const blockNumber = await cc.eth.blockNumber(); + const timestamp = await cc.eth.timestamp(); + await cc.eth.setNextBlockTimestamp(timestamp + increment); - expect(await cc.l1.timestamp()).toBe(timestamp); + expect(await cc.eth.timestamp()).toBe(timestamp); - await cc.l1.mine(); + await cc.eth.mine(); - expect(await cc.l1.blockNumber()).toBe(blockNumber + 1); - expect(await cc.l1.timestamp()).toBe(timestamp + increment); + expect(await cc.eth.blockNumber()).toBe(blockNumber + 1); + expect(await cc.eth.timestamp()).toBe(timestamp + increment); + }); + + it('setNextBlockTimestamp to a past timestamp throws', async () => { + const timestamp = await cc.eth.timestamp(); + const pastTimestamp = timestamp - 1000; + await expect(async () => await cc.eth.setNextBlockTimestamp(pastTimestamp)).rejects.toThrow( + `Error setting next block timestamp: Timestamp error: ${pastTimestamp} is lower than or equal to previous block's timestamp`, + ); }); it('load a value at a particular storage slot', async () => { // check that storage slot 0 is empty as expected - const res = await cc.l1.load(EthAddress.ZERO, 0n); + const res = await cc.eth.load(EthAddress.ZERO, 0n); expect(res).toBe(0n); }); @@ -78,18 +86,18 @@ describe('e2e_cheat_codes', () => { const storageSlot = BigInt('0x' + storageSlotInHex); const valueToSet = 5n; const contractAddress = EthAddress.fromString('0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266'); - await cc.l1.store(contractAddress, storageSlot, valueToSet); - expect(await cc.l1.load(contractAddress, storageSlot)).toBe(valueToSet); + await cc.eth.store(contractAddress, storageSlot, valueToSet); + expect(await cc.eth.load(contractAddress, storageSlot)).toBe(valueToSet); // also test with the keccak value of the slot - can be used to compute storage slots of maps - await cc.l1.store(contractAddress, cc.l1.keccak256(0n, storageSlot), valueToSet); - expect(await cc.l1.load(contractAddress, cc.l1.keccak256(0n, storageSlot))).toBe(valueToSet); + await cc.eth.store(contractAddress, cc.eth.keccak256(0n, storageSlot), valueToSet); + expect(await cc.eth.load(contractAddress, cc.eth.keccak256(0n, storageSlot))).toBe(valueToSet); }, ); it('set bytecode correctly', async () => { const contractAddress = EthAddress.fromString('0x70997970C51812dc3A010C7d01b50e0d17dc79C8'); - await cc.l1.etch(contractAddress, '0x1234'); - expect(await cc.l1.getBytecode(contractAddress)).toBe('0x1234'); + await cc.eth.etch(contractAddress, '0x1234'); + expect(await cc.eth.getBytecode(contractAddress)).toBe('0x1234'); }); it('impersonate', async () => { @@ -105,7 +113,7 @@ describe('e2e_cheat_codes', () => { const beforeBalance = await publicClient.getBalance({ address: randomAddress }); // impersonate random address - await cc.l1.startPrank(EthAddress.fromString(randomAddress)); + await cc.eth.startImpersonating(EthAddress.fromString(randomAddress)); // send funds from random address const amountToSend = parseEther('0.1'); const txHash = await walletClient.sendTransaction({ @@ -118,7 +126,7 @@ describe('e2e_cheat_codes', () => { expect(await publicClient.getBalance({ address: randomAddress })).toBe(beforeBalance - amountToSend - feePaid); // stop impersonating - await cc.l1.stopPrank(EthAddress.fromString(randomAddress)); + await cc.eth.stopImpersonating(EthAddress.fromString(randomAddress)); // making calls from random address should not be successful try { @@ -134,7 +142,7 @@ describe('e2e_cheat_codes', () => { } }); - it('can modify L1 block time', async () => { + it('can modify L2 block time', async () => { // deploy lending contract const tx = LendingContract.deploy(aztecRpcServer).send(); await tx.isMined({ interval: 0.1 }); @@ -142,9 +150,9 @@ describe('e2e_cheat_codes', () => { const contract = await LendingContract.at(receipt.contractAddress!, wallet); // now update time: - const timestamp = await cc.l1.timestamp(); + const timestamp = await cc.eth.timestamp(); const newTimestamp = timestamp + 100_000_000; - await cc.l2.warp(newTimestamp); + await cc.aztec.warp(newTimestamp); // ensure rollup contract is correctly updated const rollup = getContract({ address: getAddress(rollupAddress.toString()), abi: RollupAbi, publicClient }); @@ -159,10 +167,18 @@ describe('e2e_cheat_codes', () => { const lastUpdatedTs = Number((await contract.methods.getTot(0).view())['last_updated_ts']); expect(lastUpdatedTs).toEqual(newTimestamp); // ensure anvil is correctly updated - expect(await cc.l1.timestamp()).toEqual(newTimestamp); + expect(await cc.eth.timestamp()).toEqual(newTimestamp); // ensure rollup contract is correctly updated expect(Number(await rollup.read.lastBlockTs())).toEqual(newTimestamp); expect; }, 50_000); + + it('should throw if setting L2 block time to a past timestamp', async () => { + const timestamp = await cc.eth.timestamp(); + const pastTimestamp = timestamp - 1000; + await expect(async () => await cc.aztec.warp(pastTimestamp)).rejects.toThrow( + `Error setting next block timestamp: Timestamp error: ${pastTimestamp} is lower than or equal to previous block's timestamp`, + ); + }); }); }); diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index 85dff3bf91b..53fa13034ad 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -9,7 +9,7 @@ import { EntrypointCollection, EntrypointWallet, EthAddress, - L1CheatCodes, + EthCheatCodes, Wallet, createAztecRpcClient as createJsonRpcClient, getL1ContractAddresses, @@ -232,8 +232,8 @@ export async function setup( const config = getConfigEnvVars(); if (stateLoad) { - const l1CheatCodes = new L1CheatCodes(config.rpcUrl); - await l1CheatCodes.loadChainState(stateLoad); + const ethCheatCodes = new EthCheatCodes(config.rpcUrl); + await ethCheatCodes.loadChainState(stateLoad); } const logger = getLogger();