Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions docs/commands/stands.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
`./bin/run.js stands`
=====================
# `./bin/run.js stands`

A collection of ready-made environments for testing.

* [`./bin/run.js stands pectra`](#binrunjs-stands-pectra)
* [`./bin/run.js stands pectra-tw`](#binrunjs-stands-pectra-tw)
- [`./bin/run.js stands pectra`](#binrunjs-stands-pectra)
- [`./bin/run.js stands pectra-tw`](#binrunjs-stands-pectra-tw)
- [`./bin/run.js stands pectra-vaults`](#binrunjs-stands-pectra-vaults)

> [!NOTE]
> After adding new stand, you need to re-build the project.
>
> ```sh
> yarn build:all
> ```

## `./bin/run.js stands pectra`

Expand Down
48 changes: 41 additions & 7 deletions packages/cl-client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,22 @@ export const BeaconConfigSchema = z.object({

export type BeaconConfigResponse = z.infer<typeof BeaconConfigSchema>;

export const BeaconFinalizedCheckpointSchema = z.object({
data: z.object({
finalized: z.object({
epoch: z.string(),
}),
}),
});

export const BeaconHeadCheckpointSchema = z.object({
data: z.object({
current_justified: z.object({
epoch: z.string(),
}),
}),
});

export class BeaconClient {
private baseUrl: string;

Expand All @@ -54,39 +70,57 @@ export class BeaconClient {
public async getBlock(blockId: string): Promise<BeaconBlockResponse> {
return this.fetchAndValidate(
`${this.baseUrl}/eth/v1/beacon/blocks/${blockId}`,
BeaconBlockSchema
BeaconBlockSchema,
);
}

public async getConfig(): Promise<BeaconConfigResponse> {
return this.fetchAndValidate(
`${this.baseUrl}/eth/v1/config/spec`,
BeaconConfigSchema
BeaconConfigSchema,
);
}

public async getFinalizedEpoch(): Promise<number> {
const response = await this.fetchAndValidate(
`${this.baseUrl}/eth/v1/beacon/states/finalized/finality_checkpoints`,
BeaconFinalizedCheckpointSchema,
);
return Number.parseInt(response.data.finalized.epoch, 10);
}

public async getGenesis(): Promise<BeaconGenesisResponse> {
return this.fetchAndValidate(
`${this.baseUrl}/eth/v1/beacon/genesis`,
BeaconGenesisSchema
BeaconGenesisSchema,
);
}

public async getHeadEpoch(): Promise<number> {
const response = await this.fetchAndValidate(
`${this.baseUrl}/eth/v1/beacon/states/head/finality_checkpoints`,
BeaconHeadCheckpointSchema,
);
return Number.parseInt(response.data.current_justified.epoch, 10);
}

public async getValidators(stateId: string): Promise<BeaconValidatorsResponse> {
public async getValidators(
stateId: string,
): Promise<BeaconValidatorsResponse> {
return this.fetchAndValidate(
`${this.baseUrl}/eth/v1/beacon/states/${stateId}/validators`,
BeaconValidatorsSchema
BeaconValidatorsSchema,
);
}

private async fetchAndValidate<T>(
url: string,
schema: z.Schema<T>
schema: z.Schema<T>,
): Promise<T> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(
`Error fetching ${url}: ${response.status} ${response.statusText}`
`Error fetching ${url}: ${response.status} ${response.statusText}`,
);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/command/src/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ async function executeCommandWithLogging<T>(
export class DevNetCommand extends BaseCommand {
static baseFlags = {
network: string({
default: DEFAULT_NETWORK_NAME,
default: process.env.DEFAULT_NETWORK_NAME ?? DEFAULT_NETWORK_NAME,
description: "Name of the network",
required: false,
}),
Expand Down
13 changes: 8 additions & 5 deletions packages/services/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ const lidoCore = new DevNetServiceConfig({
GAS_MAX_FEE: "100",
GAS_PRIORITY_FEE: "1",
NETWORK: "local-devnet",
NETWORK_STATE_DEFAULTS_FILE:
"scripts/scratch/deployed-testnet-defaults.json",
NETWORK_STATE_DEFAULTS_FILE: "scripts/defaults/testnet-defaults.json",
NETWORK_STATE_FILE: `deployed-local-devnet.json`,
SLOTS_PER_EPOCH: "32",
},
Expand Down Expand Up @@ -110,13 +109,17 @@ const kapi = new DevNetServiceConfig({
});

const oracle = new DevNetServiceConfig({
// TODO: revert when have stable branch
repository: {
url: "git@github.com:lidofinance/lido-oracle.git",
branch: "feat/oracle-v5-devnet-config",
branch: "feat/vaults-pectra",
},
workspace: "workspaces/oracle-v5",
workspace: "workspaces/oracle-vaults",
name: "oracle" as const,
constants: {},
constants: {
HASH_CONSENSUS_AO_EPOCHS_PER_FRAME: 8,
HASH_CONSENSUS_VEBO_EPOCHS_PER_FRAME: 8,
},
labels: {},
});

Expand Down
20 changes: 17 additions & 3 deletions src/commands/csm/activate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,34 @@ export const ActivateCSM = command.cli({
description:
"Activates the csm by deploying smart contracts and configuring the environment based on the current network state.",
params: {},
async handler({ dre, dre: { logger } }) {
const { lidoCLI } = dre.services;
async handler({ dre, dre: { logger, network } }) {
const { lidoCLI, oracle } = dre.services;

const { elPublic } = await dre.state.getChain();
const csmState = await dre.state.getCSM();
const clClient = await network.getCLClient();

await dre.network.waitEL();

const {
HASH_CONSENSUS_AO_EPOCHS_PER_FRAME,
HASH_CONSENSUS_VEBO_EPOCHS_PER_FRAME,
} = oracle.config.constants;

const epochPerFrame = Math.max(
HASH_CONSENSUS_AO_EPOCHS_PER_FRAME,
HASH_CONSENSUS_VEBO_EPOCHS_PER_FRAME,
);

const currentEpoch = await clClient.getHeadEpoch();
const initialEpoch = epochPerFrame + currentEpoch + 2;

const env: CSMActivateENV = {
CS_ACCOUNTING_ADDRESS: csmState.accounting,
CS_MODULE_ADDRESS: csmState.module,
CS_ORACLE_HASH_CONSENSUS_ADDRESS: csmState.hashConsensus,
// TODO: calculate it
CS_ORACLE_INITIAL_EPOCH: "60",
CS_ORACLE_INITIAL_EPOCH: initialEpoch.toString(),
EL_API_PROVIDER: elPublic,
};

Expand Down
17 changes: 13 additions & 4 deletions src/commands/kapi/up.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
import { command } from "@devnet/command";
import { Params, command } from "@devnet/command";

export const KapiUp = command.cli({
description: "Start Kapi",
params: {},
async handler({ dre: { state, network, services } }) {
params: {
csm: Params.boolean({
description: "Use CSM module.",
default: false,
}),
},
async handler({ params, dre: { state, network, services } }) {
const { elPrivate } = await state.getChain();

const { kapi } = services;

const { locator, stakingRouter, curatedModule } = await state.getLido();
const { module: csmModule } = await state.getCSM();

let csmModule = "";
if (params.csm) {
csmModule = (await state.getCSM()).module;
}

const env = {
...kapi.config.constants,
Expand Down
23 changes: 19 additions & 4 deletions src/commands/lido-core/activate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ export const ActivateLidoProtocol = command.cli({
async handler({ dre }) {
const {
state,
services: { lidoCLI },
services: { lidoCLI, oracle },
network,
} = dre;

const { elPublic } = await state.getChain();
const { deployer, oracles, councils } = await state.getNamedWallet();
const clClient = await network.getCLClient();

await dre.network.waitEL();

Expand All @@ -23,12 +25,25 @@ export const ActivateLidoProtocol = command.cli({
EL_API_PROVIDER: elPublic,
};

const {
HASH_CONSENSUS_AO_EPOCHS_PER_FRAME,
HASH_CONSENSUS_VEBO_EPOCHS_PER_FRAME,
} = oracle.config.constants;

const epochPerFrame = Math.max(
HASH_CONSENSUS_AO_EPOCHS_PER_FRAME,
HASH_CONSENSUS_VEBO_EPOCHS_PER_FRAME,
);

const currentEpoch = await clClient.getHeadEpoch();
const initialEpoch = epochPerFrame + currentEpoch;

const activateCoreSh = lidoCLI.sh({ env: deployEnv });
// TODO: calc oracles-initial-epoch (time voting + 1 epoch for core and +50 for CSM)
await activateCoreSh`./run.sh devnet setup

await activateCoreSh`./run.sh devnet setup
--oracles-members ${oracles.map(({ publicKey }) => publicKey).join(",")}
--oracles-quorum ${oracles.length - 1}
--oracles-initial-epoch 60
--oracles-initial-epoch ${initialEpoch}
--dsm-guardians ${councils.map(({ publicKey }) => publicKey).join(",")}
--dsm-quorum ${councils.length}
--roles-beneficiary ${deployer.publicKey}`;
Expand Down
1 change: 1 addition & 0 deletions src/commands/lido-core/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export const DeployLidoContracts = command.cli({
SLOTS_PER_EPOCH: constants.SLOTS_PER_EPOCH,
};

await lidoCore.sh({ env: deployEnv })`bash -c yarn install`; // Fix for hardhat not found error
await lidoCore.sh({ env: deployEnv })`bash -c scripts/dao-deploy.sh`;

await dre.runCommand(LidoCoreUpdateState, {});
Expand Down
60 changes: 54 additions & 6 deletions src/commands/lido-core/prepare-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,49 @@ export const PrepareLidoCore = command.cli({
vesting: Params.string({
description: "Vesting LDO amount",
default: "820000000000000000000000",
required: false
required: false,
}),
voteDuration: Params.integer({
description: "Voting duration",
default: 60,
required: false
required: false,
}),
objectionPhaseDuration: Params.integer({
description: "Objection phase duration",
default: 5,
required: false
required: false,
}),
},
async handler({
dre,
params: { voteDuration, objectionPhaseDuration, vesting },
}) {
const { state, services } = dre;
const { lidoCore } = services;
const { lidoCore, oracle } = services;
const { constants } = lidoCore.config;
const { deployer, secondDeployer } = await state.getNamedWallet();

const filePath = constants.NETWORK_STATE_DEFAULTS_FILE;
const networkStateDefaults = await lidoCore.readJson(filePath);

const { vestingParams, daoInitialSettings } = networkStateDefaults;
const {
vestingParams,
daoInitialSettings,
oracleDaemonConfig,
hashConsensusForAccountingOracle,
hashConsensusForValidatorsExitBusOracle,
} = networkStateDefaults;
assert(vestingParams?.holders, "Missing vestingParams.holders");
assert(daoInitialSettings?.voting, "Missing daoInitialSettings.voting");
assert(oracleDaemonConfig, "Missing oracleDaemonConfig");
assert(
hashConsensusForAccountingOracle,
"Missing hashConsensusForAccountingOracle",
);
assert(
hashConsensusForValidatorsExitBusOracle,
"Missing hashConsensusForValidatorsExitBusOracle",
);

Object.assign(vestingParams.holders, {
[deployer.publicKey]: vesting,
Expand All @@ -45,6 +60,39 @@ export const PrepareLidoCore = command.cli({
objectionPhaseDuration,
});

Object.assign(oracleDaemonConfig, {
deployParameters: {
NORMALIZED_CL_REWARD_PER_EPOCH: 64,
NORMALIZED_CL_REWARD_MISTAKE_RATE_BP: 1000,
REBASE_CHECK_NEAREST_EPOCH_DISTANCE: 1,
REBASE_CHECK_DISTANT_EPOCH_DISTANCE: 2,
VALIDATOR_DELAYED_TIMEOUT_IN_SLOTS: 7200,
VALIDATOR_DELINQUENT_TIMEOUT_IN_SLOTS: 28_800,
NODE_OPERATOR_NETWORK_PENETRATION_THRESHOLD_BP: 100,
PREDICTION_DURATION_IN_SLOTS: 50_400,
FINALIZATION_MAX_NEGATIVE_REBASE_EPOCH_SHIFT: 1350,
},
});

const {
HASH_CONSENSUS_AO_EPOCHS_PER_FRAME,
HASH_CONSENSUS_VEBO_EPOCHS_PER_FRAME,
} = oracle.config.constants;

Object.assign(hashConsensusForAccountingOracle, {
deployParameters: {
fastLaneLengthSlots: 10,
epochsPerFrame: HASH_CONSENSUS_AO_EPOCHS_PER_FRAME,
},
});

Object.assign(hashConsensusForValidatorsExitBusOracle, {
deployParameters: {
fastLaneLengthSlots: 10,
epochsPerFrame: HASH_CONSENSUS_VEBO_EPOCHS_PER_FRAME,
},
});

await lidoCore.writeJson(filePath, networkStateDefaults, true);
},
});
17 changes: 13 additions & 4 deletions src/commands/oracles/up.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { command } from "@devnet/command";
import { Params, command } from "@devnet/command";

export const OracleUp = command.cli({
description: "Start Oracle(s)",
params: {},
async handler({ dre: { state, network, services } }) {
params: {
csm: Params.boolean({
description: "Use CSM module.",
default: false,
}),
},
async handler({ params, dre: { state, network, services } }) {
const { oracle } = services;

const { elPrivate, clPrivate } = await state.getChain();
Expand All @@ -12,9 +17,13 @@ export const OracleUp = command.cli({
// const name = state.getOrError("network.name");

const { locator } = await state.getLido();
const { module: csmModule } = await state.getCSM();
const { oracle1, oracle2, oracle3 } = await state.getNamedWallet();

let csmModule = "";
if (params.csm) {
csmModule = (await state.getCSM()).module;
}

const env = {
CHAIN_ID: "32382",
EXECUTION_CLIENT_URI: elPrivate,
Expand Down
Loading