Skip to content

Commit

Permalink
Devops/deployment scripts (#164)
Browse files Browse the repository at this point in the history
* safety redundancy

* llint

* Update RPLVault.sol

* typo

* deployment scripts

* add more impl

* fix tests
  • Loading branch information
teddy-nodeset authored Apr 29, 2024
1 parent 41c6644 commit 1253529
Show file tree
Hide file tree
Showing 9 changed files with 268 additions and 108 deletions.
4 changes: 2 additions & 2 deletions contracts/FundRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ contract FundRouter is UpgradeableBase {
);
}

function sendEthToDistributors() public {
function sendEthToDistributors() public nonReentrant {
console.log('sendEthToDistributors.A');
// Convert entire WETH balance of this contract to ETH
IWETH WETH = IWETH(_directory.getWETHAddress());
Expand Down Expand Up @@ -140,7 +140,7 @@ contract FundRouter is UpgradeableBase {
console.log('sendEthToDistributors.I');
}

function sendRplToDistributors() public {
function sendRplToDistributors() public nonReentrant {
console.log('sendRplToDistributors.A');

// Initialize the RPLVault and the Operator Distributor addresses
Expand Down
13 changes: 13 additions & 0 deletions contracts/Testing/Mocks/MockNodeAccountV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX License Identifier: GPL v3

pragma solidity 0.8.17;

import '../../Operator/NodeAccount.sol';

contract MockNodeAccountV2 is NodeAccount {

function test() public pure returns(uint256) {
return 69;
}

}
3 changes: 2 additions & 1 deletion contracts/Tokens/RPLVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ contract RPLVault is UpgradeableBase, ERC4626Upgradeable {
* @param caller The address initiating the deposit.
* @param receiver The address designated to receive the issued shares for the deposit.
* @param assets The amount of assets being deposited.
* @param shares The number of shares to be minted in exchange for the deposit.
* @param shares The number of shares to be exchanged for the deposit.
*/ function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual override {
require(caller == receiver, "caller must be receiver");
if (_directory.isSanctioned(caller, receiver)) {
return;
}
Expand Down
38 changes: 21 additions & 17 deletions gas-report.txt

Large diffs are not rendered by default.

43 changes: 1 addition & 42 deletions scripts/dev_setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,54 +6,13 @@ import { getNextContractAddress } from "../test/utils/utils";
import { IXRETHOracle, NodeAccountFactory } from "../typechain-types";
import { expect } from "chai";
import readline from 'readline';
import { generateBytes32Identifier, retryOperation } from "./utils/deployment";

// Function to prompt user for input
function askQuestion(query: string): Promise<string> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

return new Promise<string>(resolve => rl.question(query, ans => {
rl.close();
resolve(ans);
}));
}

// Updated retry operation function
async function retryOperation(operation: () => Promise<any>, retries: number = 3, extendedRetries: number = 3) {
try {
return await operation();
} catch (error) {
console.log(error);

if (retries > 0) {
console.log(`Retrying operation, attempts remaining: ${retries}...`);
return await retryOperation(operation, retries - 1, extendedRetries);
} else if (extendedRetries > 0) {
const answer = await askQuestion('Operation failed. Do you want to retry? (y/n): ');
if (answer.toLowerCase() === 'y') {
console.log(`Extended retry, attempts remaining: ${extendedRetries}...`);
return await retryOperation(operation, 0, extendedRetries - 1);
} else {
throw new Error('Operation aborted by the user.');
}
} else {
throw error;
}
}
}

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

// Function to generate bytes32 representation for contract identifiers
const generateBytes32Identifier = (identifier: string) => {
// Correctly concatenate 'contract.address' with the identifier before hashing
return ethers.utils.solidityKeccak256(["string"], [`contract.address${identifier}`]);
};


// Contract identifiers
const contractIdentifiers = {
Expand Down
180 changes: 180 additions & 0 deletions scripts/utils/deployment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { ethers, upgrades } from "hardhat";
import fs from 'fs';
import path from 'path';
import { FunctionFragment } from 'ethers/lib/utils';
import { getNextContractAddress } from "../../test/utils/utils";
import { getInitializerData } from "@openzeppelin/hardhat-upgrades/dist/utils";
import readline from 'readline';
import { AdminTreasury, Directory, FundRouter, NodeAccountFactory, OperatorDistributor, PriceFetcher, RPLVault, WETHVault, Whitelist, YieldDistributor } from "../../typechain-types";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";

// Function to prompt user for input
function askQuestion(query: string): Promise<string> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

return new Promise<string>(resolve => rl.question(query, ans => {
rl.close();
resolve(ans);
}));
}

// Updated retry operation function
export async function retryOperation(operation: () => Promise<any>, retries: number = 3, extendedRetries: number = 3) {
try {
return await operation();
} catch (error) {
console.log(error);

if (retries > 0) {
console.log(`Retrying operation, attempts remaining: ${retries}...`);
return await retryOperation(operation, retries - 1, extendedRetries);
} else if (extendedRetries > 0) {
const answer = await askQuestion('Operation failed. Do you want to retry? (y/n): ');
if (answer.toLowerCase() === 'y') {
console.log(`Extended retry, attempts remaining: ${extendedRetries}...`);
return await retryOperation(operation, 0, extendedRetries - 1);
} else {
throw new Error('Operation aborted by the user.');
}
} else {
throw error;
}
}
}

// Function to generate bytes32 representation for contract identifiers
export const generateBytes32Identifier = (identifier: string) => {
// Correctly concatenate 'contract.address' with the identifier before hashing
return ethers.utils.solidityKeccak256(["string"], [`contract.address${identifier}`]);
};

export async function fastDeployProtocol(deployer: SignerWithAddress, directoryDeployer: SignerWithAddress, rocketStorage: string, weth: string, sanctions: string, uniswapV3: string, oracle: string, admin: string, log: boolean) {

const directoryAddress = await getNextContractAddress(directoryDeployer, 1)

const whitelistProxy = await retryOperation(async () => {
const whitelist = await upgrades.deployProxy(await ethers.getContractFactory("contracts/Whitelist/Whitelist.sol:Whitelist", deployer), [directoryAddress], { 'initializer': 'initializeWhitelist', 'kind': 'uups', 'unsafeAllow': ['constructor'] });
if (log) console.log("whitelist deployed to", whitelist.address)
return whitelist;
});

const vCWETHProxy = await retryOperation(async () => {
const vCWETH = await upgrades.deployProxy(await ethers.getContractFactory("WETHVault", deployer), [directoryAddress, weth], { 'initializer': 'initializeVault', 'kind': 'uups', 'unsafeAllow': ['constructor', 'delegatecall'] });
if (log) console.log("vaulted constellation eth deployed to", vCWETH.address)
return vCWETH;
});

const addressRplContract = await retryOperation(async () => {
const bytes32IdentifierRplContract = generateBytes32Identifier('rocketTokenRPL');
const rocketStorageDeployment = await ethers.getContractAt("RocketStorage", rocketStorage);
const addressRplContract = await rocketStorageDeployment.getAddress(bytes32IdentifierRplContract);
return addressRplContract
})

const rplContract = await retryOperation(async function () {
return await ethers.getContractAt("@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20", addressRplContract);
});

const vCRPLProxy = await retryOperation(async function () {
const vCRPL = await upgrades.deployProxy(await ethers.getContractFactory("RPLVault", deployer), [directoryAddress, rplContract.address], { 'initializer': 'initializeVault', 'kind': 'uups', 'unsafeAllow': ['constructor', 'delegatecall'] });
if (log) console.log("vaulted constellation rpl deployed to", vCRPL.address)
return vCRPL
})

const depositPoolProxy = await retryOperation(async function () {
const dp = await upgrades.deployProxy(await ethers.getContractFactory("FundRouter", deployer), [directoryAddress], { 'initializer': 'initialize', 'kind': 'uups', 'unsafeAllow': ['constructor', 'delegatecall'] });
if (log) console.log("Fund Router (Deposit Pool) deployed to", dp.address)
return dp
})

const operatorDistributorProxy = await retryOperation(async function () {
const od = await upgrades.deployProxy(await ethers.getContractFactory("OperatorDistributor", deployer), [directoryAddress], { 'initializer': 'initialize', 'kind': 'uups', 'unsafeAllow': ['constructor', 'delegatecall'] });
if (log) console.log("operator distributor deployed to", od.address)
return od
})

const yieldDistributorProxy = await retryOperation(async function () {
const yd = await upgrades.deployProxy(await ethers.getContractFactory("YieldDistributor", deployer), [directoryAddress], { 'initializer': 'initialize', 'kind': 'uups', 'unsafeAllow': ['constructor', 'delegatecall'] });
if (log) console.log("yield distributor deployed to", yd.address)
return yd
})

const priceFetcherProxy = await retryOperation(async function () {
const pf = await upgrades.deployProxy(await ethers.getContractFactory("PriceFetcher", deployer), [directoryAddress], { 'initializer': 'initialize', 'kind': 'uups', 'unsafeAllow': ['constructor'] });
if (log) console.log("price fetcher deployed to", pf.address)
return pf
})

const adminTreasuryProxy = await retryOperation(async function () {
const at = await upgrades.deployProxy(await ethers.getContractFactory("AdminTreasury", deployer), [directoryAddress], { 'initializer': 'initialize', 'kind': 'uups', 'unsafeAllow': ['constructor'] });
if (log) console.log("admin treasury deployed to", at.address)
return at
})

const nodeAccountLogic = await retryOperation(async function () {
const NodeAccountLogic = await ethers.getContractFactory("NodeAccount", deployer);
const nodeAccountLogic = await NodeAccountLogic.deploy();
await nodeAccountLogic.deployed();
if (log) console.log("node account impl for cloning deployed to", nodeAccountLogic.address)
return nodeAccountLogic;
})

const nodeAccountFactoryProxy = await retryOperation(async () => {
const naf = await upgrades.deployProxy(await ethers.getContractFactory("NodeAccountFactory", deployer), [directoryAddress, nodeAccountLogic.address], { 'initializer': 'initializeWithImplementation', 'kind': 'uups', 'unsafeAllow': ['constructor'] })
if (log) console.log("node account factory deployed to", naf.address)
return naf
});

const directoryProxy = await retryOperation(async () => {
const dir = await upgrades.deployProxy(await ethers.getContractFactory("Directory", directoryDeployer),
[
[
whitelistProxy.address,
vCWETHProxy.address,
vCRPLProxy.address,
depositPoolProxy.address,
operatorDistributorProxy.address,
nodeAccountFactoryProxy.address,
yieldDistributorProxy.address,
oracle,
priceFetcherProxy.address,
rocketStorage,
weth,
uniswapV3,
sanctions,
],
adminTreasuryProxy.address,
admin,
], { 'initializer': 'initialize', 'kind': 'uups', 'unsafeAllow': ['constructor'] });

if (log) console.log("directory deployed to", dir.address)

return dir
})

if(log) {
if(directoryAddress.toLocaleLowerCase() === directoryProxy.address.toLocaleLowerCase()) {
console.log("directory matches predicted address", directoryAddress)
} else {
console.error("failed to deploy directory address to predicted address", directoryAddress, directoryProxy.address)
throw new Error("Bad predicted directory")
}
}


return {
whitelist: whitelistProxy as Whitelist,
vCWETH: vCWETHProxy as WETHVault,
vCRPL: vCRPLProxy as RPLVault,
depositPool: depositPoolProxy as FundRouter,
operatorDistributor: operatorDistributorProxy as OperatorDistributor,
yieldDistributor: yieldDistributorProxy as YieldDistributor,
priceFetcher: priceFetcherProxy as PriceFetcher,
adminTreasury: adminTreasuryProxy as AdminTreasury,
nodeAccountFactory: nodeAccountFactoryProxy as NodeAccountFactory,
directory: directoryProxy as Directory
}
}
36 changes: 34 additions & 2 deletions test/test-validator-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,47 @@ import { generateDepositData } from "./rocketpool/_helpers/minipool";
describe("Validator Account Factory", function () {

it("test initialize", async function () {

const setupData = await loadFixture(protocolFixture);
const { protocol, signers } = setupData;

await prepareOperatorDistributionContract(setupData, 2);
const NodeAccounts = await registerNewValidator(setupData, [signers.random, signers.random2]);
const nodeAccounts = await registerNewValidator(setupData, [signers.random, signers.random2]);


});

it("test upgrade", async function() {
const setupData = await loadFixture(protocolFixture);
const { protocol, signers } = setupData;

await prepareOperatorDistributionContract(setupData, 2);
const nodeAccounts = await registerNewValidator(setupData, [signers.random, signers.random2]);

const vaf = protocol.NodeAccountFactory;
const oldImpl = vaf.implementationAddress();

const V2 = await ethers.getContractFactory("MockNodeAccountV2");
const v2 = await V2.deploy();

await vaf.connect(signers.admin).setImplementation(v2.address);

expect(await vaf.implementationAddress()).not.equals(oldImpl);
expect(await vaf.implementationAddress()).equals(v2.address);

await nodeAccounts[0].connect(signers.random).upgradeTo(await vaf.implementationAddress());

const v2_0 = await ethers.getContractAt("MockNodeAccountV2", nodeAccounts[0].address);
const v2_1 = await ethers.getContractAt("MockNodeAccountV2", nodeAccounts[1].address);

expect(await v2_0.test()).equals(69)
await expect(v2_1.test()).to.be.rejectedWith("CALL_EXCEPTION")

await nodeAccounts[1].connect(signers.random2).upgradeTo(await vaf.implementationAddress());

expect(await v2_0.test()).equals(69)
expect(await v2_1.test()).equals(69)

})


});
Loading

0 comments on commit 1253529

Please sign in to comment.