Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/wagmi and minipool script #207

Merged
merged 10 commits into from
Jul 17, 2024
31,565 changes: 31,565 additions & 0 deletions index.ts

Large diffs are not rendered by default.

3,180 changes: 1,485 additions & 1,695 deletions package-lock.json

Large diffs are not rendered by default.

44 changes: 30 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
"description": "Protocol for accessing NodeSet's Constellation Ethereum staking network",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"lint:sol": "prettier --write 'contracts/**/*.sol'"
"lint:sol": "prettier --write 'contracts/**/*.sol'",
"codegen": "wagmi generate",
"hardhat": "hardhat",
"prepublish": "npm run codegen"
},
"keywords": [
"nodeset",
Expand All @@ -23,40 +26,53 @@
"@babel/preset-env": "^7.20.2",
"@babel/register": "^7.18.9",
"@babel/runtime": "^7.20.1",
"@chainsafe/bls-keygen": "^0.4.0",
"@chainsafe/lodestar-types": "^0.5.0",
"@chainsafe/ssz": "^0.6.1",
"@noble/curves": "^1.4.2",
"@nomicfoundation/hardhat-toolbox": "^2.0.0",
"@nomiclabs/hardhat-truffle5": "^2.0.7",
"@nomiclabs/hardhat-web3": "^2.0.0",
"@openzeppelin/contracts": "^4.9.5",
"@openzeppelin/contracts-upgradeable": "^4.8.0",
"@openzeppelin/hardhat-upgrades": "^1.22.0",
"@openzeppelin/upgrades": "^2.8.0",
"@types/find-config": "^1.0.2",
"@types/seedrandom": "^3.0.5",
"@uniswap/v3-core": "^1.0.1",
"@uniswap/v3-periphery": "^1.4.3",
"@wagmi/cli": "^2.1.15",
"abdk-libraries-solidity": "^3.2.0",
"axios": "^0.21.1",
"chai": "^4.3.7",
"dotenv": "^16.0.3",
"dotenv": "^16.1.1",
"ethereumjs-util": "^6.2.0",
"find-config": "^1.0.0",
"glob": "^7.1.7",
"hardhat": "^2.12.7",
"hardhat-contract-sizer": "^2.10.0",
"hardhat-gas-reporter": "^1.0.9",
"mocha": "^10.2.0",
"oz-contracts-3-4-0": "npm:@openzeppelin/contracts@^3.4.0",
"oz-contracts-340": "npm:@openzeppelin/contracts@^3.4.0",
"pako": "^1.0.6",
"prettier": "^3.1.1",
"prettier-plugin-solidity": "^1.3.1",
"query-string": "^7.0.1",
"solidity-docgen": "^0.6.0-beta.36",
"web3": "^1.2.8",
"@openzeppelin/contracts-upgradeable": "^4.8.0",
"@openzeppelin/upgrades": "^2.8.0",
"@uniswap/v3-core": "^1.0.1",
"@uniswap/v3-periphery": "^1.4.3",
"abdk-libraries-solidity": "^3.2.0",
"dotenv": "^16.1.1",
"find-config": "^1.0.0",
"oz-contracts-3-4-0": "npm:@openzeppelin/contracts@^3.4.0",
"oz-contracts-340": "npm:@openzeppelin/contracts@^3.4.0",
"rlp": "^3.0.0",
"seedrandom": "^3.0.5"
"seedrandom": "^3.0.5",
"solidity-docgen": "^0.6.0-beta.36",
"web3": "^1.2.8"
},
"devDependencies": {
"@nomicfoundation/hardhat-chai-matchers": "^1.0.6",
"@nomicfoundation/hardhat-network-helpers": "^1.0.11",
"@nomiclabs/hardhat-ethers": "^2.2.3",
"@nomiclabs/hardhat-etherscan": "^3.1.8",
"@typechain/ethers-v5": "^10.2.1",
"@typechain/hardhat": "^6.1.6",
"@types/mocha": "^10.0.7",
"solidity-coverage": "^0.8.12",
"typechain": "^8.3.2"
}
}
Empty file added scripts/reset.ts
Empty file.
85 changes: 2 additions & 83 deletions scripts/sandbox.ts
Original file line number Diff line number Diff line change
@@ -1,87 +1,6 @@
import { ethers, upgrades } from "hardhat";
import { setDefaultParameters } from "../test/rocketpool/_helpers/defaults";
import { deployRocketPool } from "../test/rocketpool/_helpers/deployment";
import { IXRETHOracle } from "../typechain-types";
import { fastDeployProtocol, retryOperation } from "./utils/deployment";
import { setupSandbox } from "./utils/setup_sandbox";


async function main() {
const [deployer, admin] = await ethers.getSigners();

const rocketStorage = await deployRocketPool();
await setDefaultParameters();

upgrades.silenceWarnings()

// deploy weth
const wETH = await retryOperation(async () => {
const WETH = await ethers.getContractFactory("WETH");
const contract = await WETH.deploy();
await contract.deployed();
return contract;
});
console.log("weth address", wETH.address)

// deploy mock uniswap v3 pool
const uniswapV3Pool = await retryOperation(async () => {
const UniswapV3Pool = await ethers.getContractFactory("MockUniswapV3Pool");
const contract = await UniswapV3Pool.deploy();
await contract.deployed();
return contract;
});

const sanctions = await retryOperation(async () => {
const Sanctions = await ethers.getContractFactory("MockSanctions");
const contract = await Sanctions.deploy();
await contract.deployed();
return contract;
});
console.log("sanctions address", sanctions.address);

const { directory } = await fastDeployProtocol(admin, deployer, admin, rocketStorage.address, wETH.address, sanctions.address, uniswapV3Pool.address, admin.address, true);

// set adminServer to be ADMIN_SERVER_ROLE
const adminRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("ADMIN_SERVER_ROLE"));
await retryOperation(async () => {
await directory.connect(admin).grantRole(ethers.utils.arrayify(adminRole), deployer.address);
});

// set adminServer to be ADMIN_ORACLE_ROLE
const adminOracleRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("ADMIN_ORACLE_ROLE"));
await retryOperation(async () => {
await directory.connect(admin).grantRole(ethers.utils.arrayify(adminOracleRole), deployer.address);
});

// set timelock to be TIMELOCK_ROLE
const timelockShortRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("TIMELOCK_SHORT"));
await retryOperation(async () => {
await directory.connect(admin).grantRole(ethers.utils.arrayify(timelockShortRole), deployer.address);
});
console.log("timelock short role set");

// set timelock to be TIMELOCK_ROLE
const timelockMedRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("TIMELOCK_MED"));
await retryOperation(async () => {
await directory.connect(admin).grantRole(ethers.utils.arrayify(timelockMedRole), deployer.address);
});
console.log("timelock med role set");

// set timelock to be TIMELOCK_ROLE
const timelockLongRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("TIMELOCK_LONG"));
await retryOperation(async () => {
await directory.connect(admin).grantRole(ethers.utils.arrayify(timelockLongRole), deployer.address);
});
console.log("timelock long role set");

// set protocolSigner to be PROTOCOL_ROLE
const protocolRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("CORE_PROTOCOL_ROLE"));
await retryOperation(async () => {
await directory.connect(admin).grantRole(ethers.utils.arrayify(protocolRole), deployer.address);
});
console.log("protocol role set");
}

main()
setupSandbox()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
Expand Down
17 changes: 17 additions & 0 deletions scripts/sandboxWithMinipools/helpers/fillRocketPoolDepositPool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { NewOperator, SetupData } from "../../../test/test";
import { increaseEVMTime } from "../../../test/utils/utils";

export const fillDepositPoolAndAssignDeposits = async ([setupData, newOperators]: [SetupData, NewOperator[]]): Promise<[SetupData, NewOperator[]]> => {

const { rocketPool } = setupData;

await rocketPool.rocketDepositPoolContract.deposit({
value: ethers.utils.parseEther('32').mul(newOperators.length),
});

await rocketPool.rocketDepositPoolContract.assignDeposits();

await increaseEVMTime(60 * 60 * 24 * 7 * 32);

return [setupData, newOperators];
}
16 changes: 16 additions & 0 deletions scripts/sandboxWithMinipools/helpers/launchMinipools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { NewOperator, SetupData } from '../../../test/test';

export const launchMinipools = async ([setupData, newOperators]: [SetupData, NewOperator[]]): Promise<[SetupData, NewOperator[]]> => {
console.log('Launching minipools...');
await Promise.all(newOperators.map(getMinipoolLaunchIterator(setupData)));
console.log('Minipool launching complete.');
return [setupData, newOperators];
}

const getMinipoolLaunchIterator = (setupData: SetupData) => async (operator: NewOperator) => {

const { protocol } = setupData;

await protocol.superNode.connect(operator.signer).stake(operator.expectedMinipoolAddress);

}
38 changes: 38 additions & 0 deletions scripts/sandboxWithMinipools/helpers/prepareForMinipoolCreation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { createSigners, getRocketPool, SetupData } from "../../../test/test";
import { SandboxDeployments } from "../../utils/setup_sandbox";

/**
* Prepares for minipool creation by setting up necessary data and configurations.
*
* @param deployments - The sandbox deployed contracts.
* @returns The setup data containing the protocol, rocket pool instance, and signers.
*/

export const prepareForMinipoolCreation = async (deployments: SandboxDeployments) => {
console.log('Preparing for minipool creation...');
const signers = await createSigners();

const { admin, adminServer } = signers;

const rocketPool = await getRocketPool(deployments.directory);

const setupData: SetupData = {
protocol: deployments,
rocketPool,
signers,
};

console.log('Funded the rplWhale');

const rplWhaleBalance = await rocketPool.rplContract.balanceOf(signers.deployer.address);

await rocketPool.rplContract.transfer(signers.rplWhale.address, rplWhaleBalance);

console.log('Granted admin server role to admin server.')
const adminRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes('ADMIN_SERVER_ROLE'));

await deployments.directory.connect(admin).grantRole(ethers.utils.arrayify(adminRole), adminServer.address);

console.log('Prepared for minipool creation.');
return setupData;
}
92 changes: 92 additions & 0 deletions scripts/sandboxWithMinipools/helpers/prepareNewOperators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { deriveEth2ValidatorKeys, deriveKeyFromMaster } from '@chainsafe/bls-keygen';
import { RocketMinipoolFactory } from '../../../test/rocketpool/_utils/artifacts';
import { getSignerPrivateKey, NewOperator, SetupData, Signers } from '../../../test/test';
import { approveHasSignedExitMessageSig, prepareOperatorDistributionContract } from '../../../test/utils/utils';
import { ethers } from 'hardhat';
import { types } from '@chainsafe/lodestar-types/lib/ssz/presets/mainnet';
import { DepositData } from '@chainsafe/lodestar-types';
import { keccak256 } from 'ethereumjs-util';
import { bls12_381 as bls } from '@noble/curves/bls12-381';

export const prepareNewOperators = async (setupData: SetupData): Promise<[SetupData, NewOperator[]]> => {
console.log('Preparing new operators...');

const { signers } = setupData;

const operators = await Promise.all([
makeOperator('operator', setupData, 0),
makeOperator('random', setupData, 1),
makeOperator('random2', setupData, 2),
makeOperator('random3', setupData, 3),
makeOperator('random4', setupData, 4),
makeOperator('random5', setupData, 5),
]);

// TODO: Not sure why the + 1 necessary? Without it we seem to run out of RPL/ETH in the node
await prepareOperatorDistributionContract(setupData, operators.length + 1);

console.log('Prepared new operators.');
return [setupData, operators];
};

const makeOperator = async (signerKey: keyof Signers, setupData: SetupData, salt: number): Promise<NewOperator> => {
const index = 0;
const { signers, protocol: { superNode } } = setupData;
const privateKey = getSignerPrivateKey(signerKey);

const privateKeyArr = ethers.utils.arrayify(privateKey);

const signer = signers[signerKey];
const amount = BigInt(1000000000);

const rocketMinipoolFactory = await RocketMinipoolFactory.deployed();
let expectedMinipoolAddress = (await rocketMinipoolFactory.getExpectedAddress(superNode.address, salt)).substr(2);

console.log('expectedMinipoolAddress: ', expectedMinipoolAddress);

let withdrawalCredentials = '0x010000000000000000000000' + expectedMinipoolAddress;

const pubkey = deriveKeyFromMaster(privateKeyArr, `m/12381/3600/${index}/0/0`);

const validatorKey = deriveEth2ValidatorKeys(pubkey, 1);

console.log('minipool public key: ', ethers.utils.zeroPad(pubkey, 48));
console.log('withdrawal credentials: ', withdrawalCredentials.substring(2));

const depositMessage = Buffer.concat([
Buffer.from(ethers.utils.zeroPad(pubkey, 48)),
Buffer.from(withdrawalCredentials.substring(2), 'hex'),
ethers.utils.zeroPad(ethers.utils.arrayify(ethers.BigNumber.from(amount)), 8),
]);

const signingRoot = keccak256(depositMessage);

const signature = bls.sign(signingRoot, validatorKey.signing);

const depositData: DepositData = {
pubkey: Buffer.from(ethers.utils.zeroPad(pubkey, 48)),
amount,
signature,
withdrawalCredentials: Buffer.from(withdrawalCredentials.substr(2), 'hex'),
};

const depositDataRoot = types.DepositData.hashTreeRoot(depositData);

const exitMessageSignature = await approveHasSignedExitMessageSig(
setupData,
'0x' + expectedMinipoolAddress,
salt
);

return {
signer,
depositData,
depositDataRoot,
expectedMinipoolAddress,
salt,
bondValue: ethers.utils.parseEther('8'),
minimumNodeFee: 0,
timezoneLocation: 'Australia/Brisbane',
exitMessageSignature
};
};
56 changes: 56 additions & 0 deletions scripts/sandboxWithMinipools/helpers/setupEachOperator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { NewOperator, SetupData } from '../../../test/test';
import { whitelistUserServerSig } from "../../../test/utils/utils";

export const setupEachOperator = async ([setupData, newOperators]: [SetupData, NewOperator[]]): Promise<[SetupData, NewOperator[]]> => {
console.log('Setting up the operators...');
await Promise.all(newOperators.map(getOperatorSetupIterator(setupData)));
console.log('Operator setup complete.');
return [setupData, newOperators];
}


const getOperatorSetupIterator = (setupData: SetupData) => async (operator: NewOperator) => {
const {
protocol,
rocketPool,
signers: { adminServer, rplWhale, admin },
} = setupData;

console.log('Setting up operator: ', operator.signer.address);

const whitelistResult = await whitelistUserServerSig(setupData, operator.signer);
console.log('gets whitelistResult');
await protocol.whitelist
.connect(adminServer)
.addOperator(operator.signer.address, whitelistResult.timestamp, whitelistResult.sig);

const amountToBeStaked = ethers.utils.parseUnits('1000', await rocketPool.rplContract.decimals());
console.log('amount of RPL to be staked', amountToBeStaked);

await rocketPool.rplContract.connect(rplWhale).transfer(protocol.depositPool.address, amountToBeStaked);

await protocol.depositPool.connect(admin).stakeRPLFor(protocol.superNode.address, amountToBeStaked);

console.log('transferred and staked RPL', amountToBeStaked);

return protocol.superNode
.connect(operator.signer)
.createMinipool({
bondAmount: operator.bondValue,
depositDataRoot: operator.depositDataRoot,
expectedMinipoolAddress: operator.expectedMinipoolAddress,
salt: operator.salt,
minimumNodeFee: operator.minimumNodeFee,
timezoneLocation: operator.timezoneLocation,
validatorPubkey: operator.depositData.pubkey,
validatorSignature: operator.depositData.signature,

}, operator.exitMessageSignature.timestamp, operator.exitMessageSignature.sig, {
value: ethers.utils.parseEther('1'),
}).then(
(res) => {
console.log('Finished setting up operator: ', operator.signer.address);
return res;
}
);
};
Loading
Loading