Skip to content

Commit

Permalink
[Update]: deployment tests and update task and documentation (#282)
Browse files Browse the repository at this point in the history
* fix: updated documentations
  • Loading branch information
Mohsen-T authored Dec 5, 2023
1 parent 7564dfe commit 4471f05
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 167 deletions.
25 changes: 23 additions & 2 deletions docs/local-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,19 @@ Open `.openzeppelin/<network>.json` file and find `[impls.<hash>.address]` value
You will find 2 `[impls.<hash>]` entries, one for `SSVNetwork` and another for `SSVNetworkViews`.
Run this verification process for both.

You can take it from the output of the `deploy-all.ts` script.
You can take it from the output of the `npx hardhat --network <network> deploy:all` command.

To verify an implementation contract (SSVNetwork, SSVNetworkViews or any module), run this:
To verify a proxy contract (SSVNetwork, SSVNetworkViews), run this:

```sh
npx hardhat verify --network <network> <proxy-address>
```

By verifying a contract using its proxy address, the verification process for both the proxy and the implementation contracts is conducted seamlessly.
The proxy contract is automatically linked to the implementation contract.
As a result, users will be able to view interfaces of both the proxy and the implementation contracts on the Etherscan website's contract page, ensuring comprehensive visibility and transparency.

To verify a module contract (SSVClusters, SSVOperators, SSVDAO, SSVViews), run this:

```sh
npx hardhat verify --network <network> <implementation-address>
Expand All @@ -114,3 +124,14 @@ https://goerli.etherscan.io/address/0x227...#code
```

After this action, you can go to the proxy contract in Etherscan and start interacting with it.

### How to resolve issues during the verification

- Error: no such file or directory, open ‘…/artifacts/build-info/XXXX...XXXX.json’

This issue can be resolved by executing the following commands.

```sh
npx hardhat clean
npx hardhat compile
```
3 changes: 3 additions & 0 deletions docs/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ Example:
npx hardhat --network goerli_testnet upgrade:proxy --proxy-address 0x1234... --contract SSVNetworkV2 --init-function initializev2 param1 param2
```

It is crucial to verify the upgraded contract using its proxy address.
This ensures that users can interact with the correct, upgraded implementation on Etherscan.

### Update a module

Sometimes you only need to perform changes in the logic of a function of a module, add a private function or do something that doesn't affect other components in the architecture. Then you can use the task to update a module.
Expand Down
275 changes: 129 additions & 146 deletions tasks/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { task, subtask, types } from "hardhat/config";
import { SSVModules } from "./config";
import { task, subtask, types } from 'hardhat/config';
import { SSVModules } from './config';

/**
@title Hardhat task to deploy all required contracts for SSVNetwork.
Expand All @@ -15,32 +15,28 @@ The deployer account used will be the first one returned by ethers.getSigners().
Therefore, it should be appropriately configured in your Hardhat network configuration.
This task assumes that the SSVModules enum and deployment tasks for individual contracts have been properly defined.
*/
task("deploy:all", "Deploy SSVNetwork, SSVNetworkViews and module contracts")
.setAction(async ({ }, hre) => {
const [deployer] = await ethers.getSigners();
console.log(`Deploying contracts with the account:${deployer.address}`);

const ssvTokenAddress = await hre.run("deploy:mock-token");
const operatorsModAddress = await hre.run("deploy:module", { module: SSVModules[SSVModules.SSVOperators] });
const clustersModAddress = await hre.run("deploy:module", { module: SSVModules[SSVModules.SSVClusters] });
const daoModAddress = await hre.run("deploy:module", { module: SSVModules[SSVModules.SSVDAO] });
const viewsModAddress = await hre.run("deploy:module", { module: SSVModules[SSVModules.SSVViews] });

const { ssvNetworkProxyAddress: ssvNetworkAddress } = await hre.run("deploy:ssv-network",
{
operatorsModAddress,
clustersModAddress,
daoModAddress,
viewsModAddress,
ssvTokenAddress
});

await hre.run("deploy:ssv-network-views",
{
ssvNetworkAddress
});
});

task('deploy:all', 'Deploy SSVNetwork, SSVNetworkViews and module contracts').setAction(async ({}, hre) => {
const [deployer] = await ethers.getSigners();
console.log(`Deploying contracts with the account:${deployer.address}`);

const ssvTokenAddress = await hre.run('deploy:mock-token');
const operatorsModAddress = await hre.run('deploy:module', { module: SSVModules[SSVModules.SSVOperators] });
const clustersModAddress = await hre.run('deploy:module', { module: SSVModules[SSVModules.SSVClusters] });
const daoModAddress = await hre.run('deploy:module', { module: SSVModules[SSVModules.SSVDAO] });
const viewsModAddress = await hre.run('deploy:module', { module: SSVModules[SSVModules.SSVViews] });

const { ssvNetworkProxyAddress: ssvNetworkAddress } = await hre.run('deploy:ssv-network', {
operatorsModAddress,
clustersModAddress,
daoModAddress,
viewsModAddress,
ssvTokenAddress,
});

await hre.run('deploy:ssv-network-views', {
ssvNetworkAddress,
});
});

/**
@title Hardhat task to deploy a main implementation contract for SSVNetwork or SSVNetworkViews.
Expand All @@ -56,13 +52,11 @@ The deployer account used will be the first one returned by ethers.getSigners().
Therefore, it should be appropriately configured in your Hardhat network configuration.
This task uses the "deploy:impl" subtask for the actual deployment.
*/
task("deploy:main-impl", "Deploys SSVNetwork / SSVNetworkViews implementation contract")
.addParam("contract", "New contract implemetation", null, types.string)
.setAction(async ({ contract }, hre) => {

await hre.run("deploy:impl", { contract });
});

task('deploy:main-impl', 'Deploys SSVNetwork / SSVNetworkViews implementation contract')
.addParam('contract', 'New contract implemetation', null, types.string)
.setAction(async ({ contract }, hre) => {
await hre.run('deploy:impl', { contract });
});

/**
@title Hardhat subtask to deploy an SSV module contract.
Expand All @@ -76,48 +70,45 @@ The deployer account used will be the first one returned by ethers.getSigners().
Therefore, it should be appropriately configured in your Hardhat network configuration.
This subtask uses the "deploy:impl" subtask for the actual deployment.
*/
subtask("deploy:module", "Deploys a new module contract")
.addParam("module", "SSV Module", null, types.string)
.setAction(async ({ module }, hre) => {
const moduleValues = Object.values(SSVModules);
if (!moduleValues.includes(module)) {
throw new Error(`Invalid SSVModule: ${module}. Expected one of: ${moduleValues.join(", ")}`);
}

const moduleAddress = await hre.run("deploy:impl", { contract: module });
return moduleAddress;
});
subtask('deploy:module', 'Deploys a new module contract')
.addParam('module', 'SSV Module', null, types.string)
.setAction(async ({ module }, hre) => {
const moduleValues = Object.values(SSVModules);
if (!moduleValues.includes(module)) {
throw new Error(`Invalid SSVModule: ${module}. Expected one of: ${moduleValues.join(', ')}`);
}

task("deploy:token", "Deploys SSV Token")
.setAction(async ({ }, hre) => {
console.log('Deploying SSV Network Token');
const moduleAddress = await hre.run('deploy:impl', { module });
return moduleAddress;
});

const ssvTokenFactory = await ethers.getContractFactory('SSVToken');
const ssvToken = await ssvTokenFactory.deploy();
await ssvToken.deployed();
task('deploy:token', 'Deploys SSV Token').setAction(async ({}, hre) => {
console.log('Deploying SSV Network Token');

console.log(`SSV Network Token deployed to: ${ssvToken.address}`);
const ssvTokenFactory = await ethers.getContractFactory('SSVToken');
const ssvToken = await ssvTokenFactory.deploy();
await ssvToken.deployed();

});
console.log(`SSV Network Token deployed to: ${ssvToken.address}`);
});

/**
* @title Hardhat subtask to deploy or fetch an SSV Token contract.
* The ssvToken parameter in the hardhat's network section, specifies the address of the SSV Token contract.
* If not provided, it will deploy a new MockToken contract.
* @returns {string} The address of the deployed or fetched SSV Token contract.
*/
subtask("deploy:mock-token", "Deploys / fetch SSV Token")
.setAction(async ({ }, hre) => {
const tokenAddress = hre.network.config.ssvToken;
if (tokenAddress) return tokenAddress;

// Local networks, deploy mock token
const ssvTokenFactory = await ethers.getContractFactory('SSVTokenMock');
const ssvToken = await ssvTokenFactory.deploy();
await ssvToken.deployed();

return ssvToken.address;
});
* @title Hardhat subtask to deploy or fetch an SSV Token contract.
* The ssvToken parameter in the hardhat's network section, specifies the address of the SSV Token contract.
* If not provided, it will deploy a new MockToken contract.
* @returns {string} The address of the deployed or fetched SSV Token contract.
*/
subtask('deploy:mock-token', 'Deploys / fetch SSV Token').setAction(async ({}, hre) => {
const tokenAddress = hre.network.config.ssvToken;
if (tokenAddress) return tokenAddress;

// Local networks, deploy mock token
const ssvTokenFactory = await ethers.getContractFactory('SSVTokenMock');
const ssvToken = await ssvTokenFactory.deploy();
await ssvToken.deployed();

return ssvToken.address;
});

/**
@title Hardhat subtask to deploy a new implementation contract.
Expand All @@ -130,19 +121,19 @@ The deployer account used will be the first one returned by ethers.getSigners().
Therefore, it should be appropriately configured in your Hardhat network configuration.
The contract specified should be already compiled and exist in the 'artifacts' directory.
*/
subtask("deploy:impl", "Deploys an implementation contract")
.addParam("contract", "New contract implemetation", null, types.string)
.setAction(async ({ contract }) => {
// Initialize contract
const contractFactory = await ethers.getContractFactory(contract);

// Deploy implemetation contract
const contractImpl = await contractFactory.deploy();
await contractImpl.deployed();
console.log(`${contract} implementation deployed to: ${contractImpl.address}`);

return contractImpl.address;
});
subtask('deploy:impl', 'Deploys an implementation contract')
.addParam('contract', 'New contract implemetation', null, types.string)
.setAction(async ({ contract }) => {
// Initialize contract
const contractFactory = await ethers.getContractFactory(contract);

// Deploy implemetation contract
const contractImpl = await contractFactory.deploy();
await contractImpl.deployed();
console.log(`${contract} implementation deployed to: ${contractImpl.address}`);

return contractImpl.address;
});

/**
@title Hardhat subtask to deploy the SSVNetwork contract.
Expand All @@ -161,50 +152,46 @@ The deployer account used will be the first one returned by ethers.getSigners().
Therefore, it should be appropriately configured in your Hardhat network configuration.
The 'SSVNetwork' contract specified should be already compiled and exist in the 'artifacts' directory.
*/
subtask("deploy:ssv-network", "Deploys SSVNetwork contract")
.addPositionalParam("operatorsModAddress", "Operators module address", null, types.string)
.addPositionalParam("clustersModAddress", "Clusters module address", null, types.string)
.addPositionalParam("daoModAddress", "DAO module address", null, types.string)
.addPositionalParam("viewsModAddress", "Views module address", null, types.string)
.addPositionalParam("ssvTokenAddress", "SSV Token address", null, types.string)
.setAction(async ({
subtask('deploy:ssv-network', 'Deploys SSVNetwork contract')
.addPositionalParam('operatorsModAddress', 'Operators module address', null, types.string)
.addPositionalParam('clustersModAddress', 'Clusters module address', null, types.string)
.addPositionalParam('daoModAddress', 'DAO module address', null, types.string)
.addPositionalParam('viewsModAddress', 'Views module address', null, types.string)
.addPositionalParam('ssvTokenAddress', 'SSV Token address', null, types.string)
.setAction(async ({ operatorsModAddress, clustersModAddress, daoModAddress, viewsModAddress, ssvTokenAddress }) => {
const ssvNetworkFactory = await ethers.getContractFactory('SSVNetwork');

// deploy SSVNetwork
console.log(`Deploying SSVNetwork with ssvToken ${ssvTokenAddress}`);
const ssvNetwork = await upgrades.deployProxy(
ssvNetworkFactory,
[
ssvTokenAddress,
operatorsModAddress,
clustersModAddress,
daoModAddress,
viewsModAddress,
ssvTokenAddress }) => {


const ssvNetworkFactory = await ethers.getContractFactory('SSVNetwork');

// deploy SSVNetwork
console.log(`Deploying SSVNetwork with ssvToken ${ssvTokenAddress}`);
const ssvNetwork = await upgrades.deployProxy(ssvNetworkFactory, [
ssvTokenAddress,
operatorsModAddress,
clustersModAddress,
daoModAddress,
viewsModAddress,
process.env.MINIMUM_BLOCKS_BEFORE_LIQUIDATION,
process.env.MINIMUM_LIQUIDATION_COLLATERAL,
process.env.VALIDATORS_PER_OPERATOR_LIMIT,
process.env.DECLARE_OPERATOR_FEE_PERIOD,
process.env.EXECUTE_OPERATOR_FEE_PERIOD,
process.env.OPERATOR_MAX_FEE_INCREASE
],
{
kind: "uups"
});
await ssvNetwork.deployed();

const ssvNetworkProxyAddress = ssvNetwork.address;
const ssvNetworkImplAddress = await upgrades.erc1967.getImplementationAddress(ssvNetwork.address);

console.log(`SSVNetwork proxy deployed to: ${ssvNetworkProxyAddress}`);
console.log(`SSVNetwork implementation deployed to: ${ssvNetworkImplAddress}`);

return { ssvNetworkProxyAddress, ssvNetworkImplAddress };
});
process.env.MINIMUM_BLOCKS_BEFORE_LIQUIDATION,
process.env.MINIMUM_LIQUIDATION_COLLATERAL,
process.env.VALIDATORS_PER_OPERATOR_LIMIT,
process.env.DECLARE_OPERATOR_FEE_PERIOD,
process.env.EXECUTE_OPERATOR_FEE_PERIOD,
process.env.OPERATOR_MAX_FEE_INCREASE,
],
{
kind: 'uups',
},
);
await ssvNetwork.deployed();

const ssvNetworkProxyAddress = ssvNetwork.address;
const ssvNetworkImplAddress = await upgrades.erc1967.getImplementationAddress(ssvNetwork.address);

console.log(`SSVNetwork proxy deployed to: ${ssvNetworkProxyAddress}`);
console.log(`SSVNetwork implementation deployed to: ${ssvNetworkImplAddress}`);

return { ssvNetworkProxyAddress, ssvNetworkImplAddress };
});

/**
@title Hardhat subtask to deploy the SSVNetworkViews contract.
Expand All @@ -217,26 +204,22 @@ The deployer account used will be the first one returned by ethers.getSigners().
Therefore, it should be appropriately configured in your Hardhat network configuration.
The 'SSVNetworkViews' contract specified should be already compiled and exist in the 'artifacts' directory.
*/
subtask("deploy:ssv-network-views", "Deploys SSVNetworkViews contract")
.addParam("ssvNetworkAddress", "SSVNetwork address", null, types.string)
.setAction(async ({
ssvNetworkAddress }) => {
const ssvNetworkViewsFactory = await ethers.getContractFactory('SSVNetworkViews');

// deploy SSVNetwork
const ssvNetworkViews = await upgrades.deployProxy(ssvNetworkViewsFactory, [
ssvNetworkAddress
],
{
kind: "uups"
});
await ssvNetworkViews.deployed();

const ssvNetworkViewsProxyAddress = ssvNetworkViews.address;
const ssvNetworkViewsImplAddress = await upgrades.erc1967.getImplementationAddress(ssvNetworkViews.address);

console.log(`SSVNetworkViews proxy deployed to: ${ssvNetworkViewsProxyAddress}`);
console.log(`SSVNetworkViews implementation deployed to: ${ssvNetworkViewsImplAddress}`);

return { ssvNetworkViewsProxyAddress, ssvNetworkViewsImplAddress };
subtask('deploy:ssv-network-views', 'Deploys SSVNetworkViews contract')
.addParam('ssvNetworkAddress', 'SSVNetwork address', null, types.string)
.setAction(async ({ ssvNetworkAddress }) => {
const ssvNetworkViewsFactory = await ethers.getContractFactory('SSVNetworkViews');

// deploy SSVNetwork
const ssvNetworkViews = await upgrades.deployProxy(ssvNetworkViewsFactory, [ssvNetworkAddress], {
kind: 'uups',
});
await ssvNetworkViews.deployed();

const ssvNetworkViewsProxyAddress = ssvNetworkViews.address;
const ssvNetworkViewsImplAddress = await upgrades.erc1967.getImplementationAddress(ssvNetworkViews.address);

console.log(`SSVNetworkViews proxy deployed to: ${ssvNetworkViewsProxyAddress}`);
console.log(`SSVNetworkViews implementation deployed to: ${ssvNetworkViewsImplAddress}`);

return { ssvNetworkViewsProxyAddress, ssvNetworkViewsImplAddress };
});
Loading

0 comments on commit 4471f05

Please sign in to comment.