Skip to content
Open
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
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./node_modules
20 changes: 20 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM node:18-alpine

COPY . /usr/src/app

WORKDIR /usr/src/app

RUN apk add --update --no-cache python3 && ln -sf python3 /usr/bin/python

RUN apk add git

RUN apk add make g++ py3-pip

RUN yarn global add ts-node

RUN yarn install

RUN yarn add --save-dev "@nomicfoundation/hardhat-network-helpers@^1.0.0" "@nomicfoundation/hardhat-chai-matchers@^1.0.0" "@nomiclabs/hardhat-etherscan@^3.0.0" "@typechain/ethers-v5@^10.1.0" "@typechain/hardhat@^6.1.2" "chai@^4.2.0" "hardhat-gas-reporter@^1.0.8" "solidity-coverage@^0.8.1" "typechain@^8.1.0"


ENTRYPOINT ["sleep", "999999"]
66 changes: 57 additions & 9 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import { NetworkUserConfig } from 'hardhat/types';

import 'dotenv/config';

import '@nomicfoundation/hardhat-toolbox-viem';
import '@nomicfoundation/hardhat-chai-matchers';
import '@openzeppelin/hardhat-upgrades';

import 'hardhat-abi-exporter';
import 'hardhat-contract-sizer';
import 'solidity-coverage';

import 'hardhat-storage-layout';

import './tasks/deploy';
import './tasks/update-module';
import './tasks/upgrade';
Expand All @@ -27,16 +28,37 @@ const config: HardhatUserConfig = {
compilers: [
{
version: '0.8.4',
settings: {
// Include storage layout output
outputSelection: {
"*": {
"*": ["storageLayout"]
}
}
}
},
{
version: '0.8.18',
},
{
{
version: '0.8.18',
settings: {
// Include storage layout output
outputSelection: {
"*": {
"*": ["storageLayout"]
}
}
}
},
{
version: '0.8.24',
settings: {
optimizer: {
enabled: true,
runs: 10000,
optimizer: {
enabled: true,
runs: 10000
},
outputSelection: {
"*": {
"*": ["storageLayout"]
}
},
evmVersion: 'cancun',
},
Expand All @@ -54,7 +76,10 @@ const config: HardhatUserConfig = {
},
},
etherscan: {
apiKey: process.env.ETHERSCAN_KEY,
apiKey: {
'holesky': process.env.ETHERSCAN_KEY,
'kurtosis': 'empty',
},
customChains: [
{
network: 'holesky',
Expand All @@ -64,6 +89,14 @@ const config: HardhatUserConfig = {
browserURL: 'https://holesky.etherscan.io',
},
},
{
network: 'kurtosis',
chainId: 3151908,
urls: {
apiURL: `${process.env.BLOCKSCOUT_URL}/api`,
browserURL: process.env.BLOCKSCOUT_URL,
},
}
],
},
contractSizer: {
Expand All @@ -86,6 +119,7 @@ const config: HardhatUserConfig = {
},
};


if (process.env.HOLESKY_ETH_NODE_URL && process.env.HOLESKY_OWNER_PRIVATE_KEY) {
const sharedConfig = {
url: `${process.env.HOLESKY_ETH_NODE_URL}${process.env.NODE_PROVIDER_KEY}`,
Expand Down Expand Up @@ -135,4 +169,18 @@ if (process.env.FORK_TESTING_ENABLED) {
};
}

if (process.env.DEVNET_ETH_NODE_URL) {
config.networks = {
kurtosis: {
chainId: 3151908,
url: `${process.env.DEVNET_ETH_NODE_URL}`,
accounts: [
"39725efee3fb28614de3bacaffe4cc4bd8c436257e2c8bb887c4b5c4be45e76d",
],
gasPrice: +(process.env.GAS_PRICE || ''),
gas: +(process.env.GAS || ''),
} as NetworkUserConfig,
};
}

export default config;
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@
"hardhat": "^2.22.4",
"hardhat-abi-exporter": "^2.10.1",
"hardhat-contract-sizer": "^2.10.0",
"hardhat-storage-layout": "^0.1.7",
"solidity-coverage": "^0.8.12",
"ssv-keys": "github:bloxapp/ssv-keys#v1.0.4"
"ssv-keys": "github:bloxapp/ssv-keys#v1.0.4",
"ssv-scanner": "^1.0.4"
}
}
119 changes: 84 additions & 35 deletions tasks/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { task, subtask, types } from 'hardhat/config';
import { SSVModules } from './config';


/**
@title Hardhat task to deploy all required contracts for SSVNetwork.
This task deploys the main SSVNetwork and SSVNetworkViews contracts, along with their associated modules.
Expand All @@ -15,30 +16,51 @@ 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) => {
// Triggering compilation
await hre.run('compile');
task('deploy:all', 'Deploy SSVNetwork, SSVNetworkViews and module contracts')
.addOptionalParam('machine', 'outputs the machine readable output', false, types.boolean)
.setAction(async (args, hre) => {
if (!args.machine) {
// Triggering compilation if this is manual
await hre.run('compile');
}

const [deployer] = await ethers.getSigners();
console.log(`Deploying contracts with the account:${deployer.address}`);
if (!args.machine){
const [deployer] = await ethers.getSigners();
deployer.address = deployer.address.toLowerCase();
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 ssvTokenAddress = await hre.run('deploy:token', { machine: args.machine });
const operatorsModAddress = await hre.run('deploy:module', { module: SSVModules[SSVModules.SSVOperators], machine: args.machine });
const clustersModAddress = await hre.run('deploy:module', { module: SSVModules[SSVModules.SSVClusters], machine: args.machine });
const daoModAddress = await hre.run('deploy:module', { module: SSVModules[SSVModules.SSVDAO], machine: args.machine });
const viewsModAddress = await hre.run('deploy:module', { module: SSVModules[SSVModules.SSVViews], machine: args.machine });

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

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

if (args.machine) {
const jsonData = {
ssvTokenAddress,
operatorsModAddress,
clustersModAddress,
daoModAddress,
viewsModAddress,
ssvNetworkAddress,
}
console.log(JSON.stringify(jsonData));
}
});

/**
Expand Down Expand Up @@ -92,27 +114,35 @@ 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) => {
.addOptionalParam('machine', 'outputs the machine readable output', false, types.boolean)
.setAction(async ({ module, machine }, 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 });
const moduleAddress = await hre.run('deploy:impl', { contract: module, machine: machine });
return moduleAddress;
});

task('deploy:token', 'Deploys SSV Token').setAction(async ({}, hre) => {
// Triggering compilation
await hre.run('compile');

console.log('Deploying SSV Network Token');
task('deploy:token', 'Deploys SSV Token')
.addOptionalParam('machine', 'outputs the machine readable output', false, types.boolean)
.setAction(async (args, hre) => {
if (!args.machine){
// Triggering compilation if this is manual
await hre.run('compile');
console.log('Deploying SSV Network Token');
}

const ssvTokenFactory = await ethers.getContractFactory('SSVToken');
const ssvToken = await ssvTokenFactory.deploy();
await ssvToken.deployed();
await ssvToken.waitForDeployment();

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

return await ssvToken.getAddress()
});

/**
Expand All @@ -126,9 +156,12 @@ subtask('deploy:mock-token', 'Deploys / fetch SSV Token').setAction(async ({}, h
if (tokenAddress) return tokenAddress;

// Local networks, deploy mock token
const ssvToken = await hre.viem.deployContract('SSVToken');
// const ssvToken = await hre.viem.deployContract('SSVToken');
const ssvTokenFactory = await ethers.getContractFactory('SSVToken');
const ssvToken = await ssvTokenFactory.deployContract();
await ssvToken.waitForDeployment();

return ssvToken.address;
return await ssvToken.getAddress();
});

/**
Expand All @@ -144,15 +177,22 @@ The contract specified should be already compiled and exist in the 'artifacts' d
*/
subtask('deploy:impl', 'Deploys an implementation contract')
.addParam('contract', 'New contract implemetation', null, types.string)
.setAction(async ({ contract }, hre) => {
// Triggering compilation
await hre.run('compile');

.addOptionalParam('machine', 'outputs the machine readable output', false, types.boolean)
.setAction(async (args, hre) => {
if (!args.machine){
// Triggering compilation if this is manual
await hre.run('compile');
}
// Deploy implemetation contract
const contractImpl = await hre.viem.deployContract(contract);
console.log(`${contract} implementation deployed to: ${contractImpl.address}`);
const contractFactory = await ethers.getContractFactory(args.contract);
const contractImpl = await contractFactory.deploy();
await contractImpl.waitForDeployment();

return contractImpl.address;
if (!args.machine){
console.log(`${args.contract} implementation deployed to: ${await contractImpl.getAddress()}`);
}

return await contractImpl.getAddress();
});

/**
Expand All @@ -178,11 +218,15 @@ subtask('deploy:ssv-network', 'Deploys SSVNetwork contract')
.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 }) => {
.addOptionalParam('machine', 'outputs the machine readable output', false, types.boolean)
.setAction(async ({ operatorsModAddress, clustersModAddress, daoModAddress, viewsModAddress, ssvTokenAddress, machine }) => {
const ssvNetworkFactory = await ethers.getContractFactory('SSVNetwork');

// deploy SSVNetwork
console.log(`Deploying SSVNetwork with ssvToken ${ssvTokenAddress}`);
if (!machine){
console.log(`Deploying SSVNetwork with ssvToken ${ssvTokenAddress}`);
}

const ssvNetwork = await upgrades.deployProxy(
ssvNetworkFactory,
[
Expand All @@ -207,8 +251,10 @@ subtask('deploy:ssv-network', 'Deploys SSVNetwork contract')
const ssvNetworkProxyAddress = await ssvNetwork.getAddress();
const ssvNetworkImplAddress = await upgrades.erc1967.getImplementationAddress(ssvNetworkProxyAddress);

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

return { ssvNetworkProxyAddress, ssvNetworkImplAddress };
});
Expand All @@ -226,7 +272,8 @@ The 'SSVNetworkViews' contract specified should be already compiled and exist in
*/
subtask('deploy:ssv-network-views', 'Deploys SSVNetworkViews contract')
.addParam('ssvNetworkAddress', 'SSVNetwork address', null, types.string)
.setAction(async ({ ssvNetworkAddress }) => {
.addOptionalParam('machine', 'outputs the machine readable output', false, types.boolean)
.setAction(async ({ ssvNetworkAddress, machine }) => {
const ssvNetworkViewsFactory = await ethers.getContractFactory('SSVNetworkViews');

// deploy SSVNetwork
Expand All @@ -238,8 +285,10 @@ subtask('deploy:ssv-network-views', 'Deploys SSVNetworkViews contract')
const ssvNetworkViewsProxyAddress = await ssvNetworkViews.getAddress();
const ssvNetworkViewsImplAddress = await upgrades.erc1967.getImplementationAddress(ssvNetworkViewsProxyAddress);

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

return { ssvNetworkViewsProxyAddress, ssvNetworkViewsImplAddress };
});
Loading