Skip to content

Commit

Permalink
Merge branch 'repovation' into feat/locator-update
Browse files Browse the repository at this point in the history
# Conflicts:
#	test/0.4.24/contracts/LidoLocator__MutableMock.sol
  • Loading branch information
tamtamchik committed Aug 26, 2024
2 parents c426a86 + 2fc9848 commit 66dcdc5
Show file tree
Hide file tree
Showing 187 changed files with 4,570 additions and 4,722 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:

services:
hardhat-node:
image: feofanov/hardhat-node:2.22.8
image: feofanov/hardhat-node:2.22.9
ports:
- 8545:8545
env:
Expand Down
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
foundry
contracts/**/*.sol

.gitignore
.prettierignore

eslint.config.mjs

package-lock.json
13 changes: 12 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,16 @@
"singleQuote": false,
"printWidth": 120,
"tabWidth": 2,
"quoteProps": "consistent"
"quoteProps": "consistent",
"plugins": ["prettier-plugin-solidity"],
"overrides": [
{
"files": "*.sol",
"options": {
"parser": "solidity-parse",
"tabWidth": 4,
"useTabs": false
}
}
]
}
103 changes: 57 additions & 46 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,99 +128,93 @@ This repository features a Hardhat-Foundry dual setup:
- Hardhat gives much more flexibility when writing complex tests;
- The Foundry's fuzzing capabilities enable better coverage of edge cases.
#### Tracing
#### Hardhat
Hardhat tests are all located in `/tests` at the root of the project.
Each subdirectory name corresponds to the version of the contract being tested, mirroring the `/contracts` directory
structure. Integration, regression, and other non-unit tests are placed into corresponding subdirectories,
e.g. `/tests/integration/`, ` /tests/regression`, etc.
##### Tracing
`hardhat-tracer` is used to trace contract calls and state changes during tests.
During Hardhat tests, the `hardhat-tracer` is used to trace contract calls and state changes.
Full-scale transaction tracing is disabled by default because it can significantly slow down the tests.
> [!NOTE]
> Tracing is ONLY supported in Hardhat unit tests and integration tests using Hardhat mainnet fork (see below).
To enable tracing, you need to wrap the code you want to trace with the `Tracer.enable()` and `Tracer.disable()`
functions:
functions and run the tests with the appropriate command postfix, e.g. `yarn test:trace`.
```typescript
import { Tracer } from 'test/suite';
import { Tracer } from "test/suite";
describe('MyContract', () => {
it('should do something', async () => {
describe("MyContract", () => {
it("should do something", async () => {
Tracer.enable();
// code to trace
Tracer.disable();
});
});
```
Then run the tests with the following commands:
##### Unit tests
```bash
yarn test:trace # Run all tests with trace logging (calls only)
yarn test:fulltrace # Run all tests with full trace logging (calls and storage ops)
yarn test:integration:trace # Run all integration tests with trace logging
yarn test:integration:fulltrace # Run all integration tests with full trace logging
```
Unit tests are located in `/tests` at the root of the project.
These tests are designed to verify the behavior of individual contracts and functions.
Naming conventions follow utilizes the `*.test.ts` postfix, e.g. `myContract.test.ts`.
> [!NOTE]
> Tracing is not supported in Foundry tests and integration tests other than Hardhat mainnet fork tests.
If you require mocks or wrappers for external contracts, please place them in the appropriate subdirectories, such as
`/tests/0.4.24/contracts` and adhere to the naming conventions:
#### Hardhat
- Use the `Mock` postfix for self-established contracts that simulate the behavior of the target contract. For example,
`MyContract_Mock.sol` or `MyContract_MockForAnotherContract.sol`.
- Use the `Harness` postfix for a wrapper that exposes private functions of a contract and may include test-specific
actions. For example, `MyContract_Wrapper.sol` or `MyContract_WrapperForAnotherContract.sol`.
Hardhat tests are all located in `/tests` at the root of the project.
Each subdirectory name corresponds to the version of the contract being tested, mirroring the `/contracts` directory
structure. Integration, regression, and other non-unit tests are placed into corresponding subdirectories,
e.g. `/tests/integration/`, ` /tests/regression`, etc.
You can run unit tests in multiple ways:
```bash
yarn test # Run all tests in parallel
yarn test:sequential # Run all tests sequentially
yarn test:trace # Run all tests with trace logging (see Tracing section)
yarn test:trace # Run all tests with trace logging (calls only)
yarn test:fulltrace # Run all tests with full trace logging (calls and storage ops)
yarn test:watch # Run all tests in watch mode
```
#### Foundry
Foundry's Solidity tests are used only for fuzzing library contracts or functions performing complex calculations
or byte juggling. Solidity tests are located under ` / tests` and in the appropriate subdirectories. Naming conventions
follow the Foundry's [documentation](https://book.getfoundry.sh/tutorials/best-practices#general-test-guidance):
- for tests, postfix `.t.sol` is used (e.g., `MyContract.t.sol`)
- for scripts, postfix `.s.sol` is used (e.g., `MyScript.s.sol`)
- for helpers, postfix `.h.sol` is used (e.g., `MyHelper.h.sol`).
Following the convention of distinguishing Hardhat test files from Foundry-related files is essential to ensure the
proper execution of Hardhat tests.
```bash
yarn test:foundry # Run all Foundry tests
```
#### Integration tests
##### Integration tests
Integration tests are located in `/tests/integration` at the root of the project.
These tests are used to verify the interaction between different contracts and their behavior in a real-world scenario.
Naming conventions follow the `*.int.ts` postfix, e.g. `myScenario.int.ts`.
You can run integration tests in multiple ways, but for all of them, you need to have a `.env` file in the root of
the project (you can use `.env.example` as a template).
##### Hardhat Mainnet Fork
###### Hardhat Mainnet Fork
This is the most common way to run integration tests. It uses the Hardhat mainnet fork to simulate the mainnet
environment. Requires `HARDHAT_FORKING_URL` and `HARDHAT_FORKING_BLOCK_NUMBER` (optional) to be set in the `.env` file
along with `MAINNET_*` env variables (see `.env.example`).
along with some `MAINNET_*` env variables (see `.env.example`).
```bash
yarn test:integration # Run all integration tests
yarn test:integration:trace # Run all integration tests with trace logging (see Tracing section)
yarn test:integration # Run all integration tests
yarn test:integration:trace # Run all integration tests with trace logging (calls only)
yarn test:integration:fulltrace # Run all integration tests with full trace logging (calls and storage ops)
```
##### Local setup
###### Local setup
This method is used to run integration tests against a local scratch deployment (
see [scratch-deploy.md](./docs/scratch-deploy.md)).
Requires `LOCAL_*` env variables to be set and a local deployment to be running on port `8555`.
Requires a local deployment to be running on port `8555` and `deployed-local.json` with the deployed addresses
(automatically generated during the scratch deployment).
```bash
yarn test:integration:local
```
##### Any fork setup
###### Any fork setup
This method is used to run integration tests against any fork. Requires `MAINNET_*` env variables to be set in the
`.env` file and a fork to be running on port `8545`.
Expand All @@ -229,6 +223,23 @@ This method is used to run integration tests against any fork. Requires `MAINNET
yarn test:integration:fork
```
#### Foundry tests
Foundry's Solidity tests are used only for fuzzing library contracts or functions performing complex calculations
or byte juggling. Solidity tests are located under ` / tests` and in the appropriate subdirectories. Naming conventions
follow the Foundry's [documentation](https://book.getfoundry.sh/tutorials/best-practices#general-test-guidance):
- for tests, postfix `.t.sol` is used (e.g., `MyContract.t.sol`)
- for scripts, postfix `.s.sol` is used (e.g., `MyScript.s.sol`)
- for helpers, postfix `.h.sol` is used (e.g., `MyHelper.h.sol`).
Following the convention of distinguishing Hardhat test files from Foundry-related files is essential to ensure the
proper execution of Hardhat tests.
```bash
yarn test:foundry # Run all Foundry tests
```
#### Coverage
The project uses the `hardhat-coverage` plugin to generate coverage reports.
Expand Down
2 changes: 1 addition & 1 deletion lib/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ async function getDeployTxParams(deployer: string) {
maxFeePerGas: ethers.parseUnits(String(GAS_MAX_FEE), "gwei"),
};
} else {
throw new Error("Must specify gas ENV vars: \"GAS_PRIORITY_FEE\" and \"GAS_MAX_FEE\" in gwei (like just \"3\")");
throw new Error('Must specify gas ENV vars: "GAS_PRIORITY_FEE" and "GAS_MAX_FEE" in gwei (like just "3")');
}
}

Expand Down
10 changes: 7 additions & 3 deletions lib/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,15 @@ const parseLogEntry = (entry: Log, interfaces: Interface[]): LogDescription | nu
return null;
};

export function findEventsWithInterfaces(receipt: ContractTransactionReceipt, eventName: string, interfaces: Interface[]): LogDescription[] {
export function findEventsWithInterfaces(
receipt: ContractTransactionReceipt,
eventName: string,
interfaces: Interface[],
): LogDescription[] {
const events: LogDescription[] = [];
const notParsedLogs: Log[] = [];

receipt.logs.forEach(entry => {
receipt.logs.forEach((entry) => {
const logDescription = parseLogEntry(entry, interfaces);
if (logDescription) {
events.push(logDescription);
Expand All @@ -59,7 +63,7 @@ export function findEventsWithInterfaces(receipt: ContractTransactionReceipt, ev
// log.warning("The following logs could not be parsed:", notParsedLogs);
}

return events.filter(e => e.name === eventName);
return events.filter((e) => e.name === eventName);
}

export function findEvents(receipt: ContractTransactionReceipt, eventName: string) {
Expand Down
5 changes: 3 additions & 2 deletions lib/protocol/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const getSigner = async (signer: Signer, balance = ether("100"), signers: Protoc

export const getProtocolContext = async (): Promise<ProtocolContext> => {
const { contracts, signers } = await discover();
const interfaces = Object.values(contracts).map(contract => contract.interface);
const interfaces = Object.values(contracts).map((contract) => contract.interface);

// By default, all flags are "on"
const flags = {
Expand All @@ -30,7 +30,8 @@ export const getProtocolContext = async (): Promise<ProtocolContext> => {
interfaces,
flags,
getSigner: async (signer: Signer, balance?: bigint) => getSigner(signer, balance, signers),
getEvents: (receipt: ContractTransactionReceipt, eventName: string) => findEventsWithInterfaces(receipt, eventName, interfaces),
getEvents: (receipt: ContractTransactionReceipt, eventName: string) =>
findEventsWithInterfaces(receipt, eventName, interfaces),
} as ProtocolContext;

await provision(context);
Expand Down
30 changes: 15 additions & 15 deletions lib/protocol/discover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,51 +64,51 @@ const getFoundationContracts = async (locator: LoadedContract<LidoLocator>, conf
(await batch({
accountingOracle: loadContract(
"AccountingOracle",
config.get("accountingOracle") || await locator.accountingOracle(),
config.get("accountingOracle") || (await locator.accountingOracle()),
),
depositSecurityModule: loadContract(
"DepositSecurityModule",
config.get("depositSecurityModule") || await locator.depositSecurityModule(),
config.get("depositSecurityModule") || (await locator.depositSecurityModule()),
),
elRewardsVault: loadContract(
"LidoExecutionLayerRewardsVault",
config.get("elRewardsVault") || await locator.elRewardsVault(),
config.get("elRewardsVault") || (await locator.elRewardsVault()),
),
legacyOracle: loadContract("LegacyOracle", config.get("legacyOracle") || await locator.legacyOracle()),
lido: loadContract("Lido", config.get("lido") || await locator.lido()),
legacyOracle: loadContract("LegacyOracle", config.get("legacyOracle") || (await locator.legacyOracle())),
lido: loadContract("Lido", config.get("lido") || (await locator.lido())),
oracleReportSanityChecker: loadContract(
"OracleReportSanityChecker",
config.get("oracleReportSanityChecker") || await locator.oracleReportSanityChecker(),
config.get("oracleReportSanityChecker") || (await locator.oracleReportSanityChecker()),
),
burner: loadContract("Burner", config.get("burner") || await locator.burner()),
stakingRouter: loadContract("StakingRouter", config.get("stakingRouter") || await locator.stakingRouter()),
burner: loadContract("Burner", config.get("burner") || (await locator.burner())),
stakingRouter: loadContract("StakingRouter", config.get("stakingRouter") || (await locator.stakingRouter())),
validatorsExitBusOracle: loadContract(
"ValidatorsExitBusOracle",
config.get("validatorsExitBusOracle") || await locator.validatorsExitBusOracle(),
config.get("validatorsExitBusOracle") || (await locator.validatorsExitBusOracle()),
),
withdrawalQueue: loadContract(
"WithdrawalQueueERC721",
config.get("withdrawalQueue") || await locator.withdrawalQueue(),
config.get("withdrawalQueue") || (await locator.withdrawalQueue()),
),
withdrawalVault: loadContract(
"WithdrawalVault",
config.get("withdrawalVault") || await locator.withdrawalVault(),
config.get("withdrawalVault") || (await locator.withdrawalVault()),
),
oracleDaemonConfig: loadContract(
"OracleDaemonConfig",
config.get("oracleDaemonConfig") || await locator.oracleDaemonConfig(),
config.get("oracleDaemonConfig") || (await locator.oracleDaemonConfig()),
),
})) as CoreContracts;

/**
* Load Aragon contracts required for protocol.
*/
const getAragonContracts = async (lido: LoadedContract<Lido>, config: ProtocolNetworkConfig) => {
const kernelAddress = config.get("kernel") || await lido.kernel();
const kernelAddress = config.get("kernel") || (await lido.kernel());
const kernel = await loadContract("Kernel", kernelAddress);
return (await batch({
kernel: new Promise((resolve) => resolve(kernel)), // Avoiding double loading
acl: loadContract("ACL", config.get("acl") || await kernel.acl()),
acl: loadContract("ACL", config.get("acl") || (await kernel.acl())),
})) as AragonContracts;
};

Expand All @@ -127,7 +127,7 @@ const getStakingModules = async (stakingRouter: LoadedContract<StakingRouter>, c
* Load HashConsensus contract for accounting oracle.
*/
const getHashConsensus = async (accountingOracle: LoadedContract<AccountingOracle>, config: ProtocolNetworkConfig) => {
const hashConsensusAddress = config.get("hashConsensus") || await accountingOracle.getConsensusContract();
const hashConsensusAddress = config.get("hashConsensus") || (await accountingOracle.getConsensusContract());
return (await batch({
hashConsensus: loadContract("HashConsensus", hashConsensusAddress),
})) as HashConsensusContracts;
Expand Down
29 changes: 12 additions & 17 deletions lib/protocol/helpers/accounting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,19 +385,17 @@ export const handleOracleReport = async (
"El Rewards Vault Balance": ethers.formatEther(elRewardsVaultBalance),
});

const handleReportTx = await lido
.connect(accountingOracleAccount)
.handleOracleReport(
reportTimestamp,
1n * 24n * 60n * 60n, // 1 day
beaconValidators,
clBalance,
withdrawalVaultBalance,
elRewardsVaultBalance,
sharesRequestedToBurn,
[],
0n,
);
const handleReportTx = await lido.connect(accountingOracleAccount).handleOracleReport(
reportTimestamp,
1n * 24n * 60n * 60n, // 1 day
beaconValidators,
clBalance,
withdrawalVaultBalance,
elRewardsVaultBalance,
sharesRequestedToBurn,
[],
0n,
);

await trace("lido.handleOracleReport", handleReportTx);
} catch (error) {
Expand Down Expand Up @@ -621,10 +619,7 @@ export const submitReport = async (
/**
* Ensure that the oracle committee has the required number of members.
*/
export const ensureOracleCommitteeMembers = async (
ctx: ProtocolContext,
minMembersCount = MIN_MEMBERS_COUNT,
) => {
export const ensureOracleCommitteeMembers = async (ctx: ProtocolContext, minMembersCount = MIN_MEMBERS_COUNT) => {
const { hashConsensus } = ctx.contracts;

const members = await hashConsensus.getFastLaneMembers();
Expand Down
Loading

0 comments on commit 66dcdc5

Please sign in to comment.