Skip to content

Commit

Permalink
Merge pull request #394 from etherisc/feature/deployment_improv
Browse files Browse the repository at this point in the history
Improve deployment scripts (#391)
  • Loading branch information
doerfli committed Jun 19, 2024
2 parents 98bc1be + 2387d04 commit fd4ee14
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 81 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ test_brownie/__pycache__
# custom stuff
*.log
deployment_state_*.json
libraries_*.json
lcov.info
.*.drawio.bkp
31 changes: 29 additions & 2 deletions scripts/libs/deployment_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const DEPLOYMENT_STATE_FILENAME_SUFFIX = ".json";

type State = {
contracts: ContractState[];
transactions: TransactionState[];
}

export type ContractState = {
Expand All @@ -17,12 +18,17 @@ export type ContractState = {
verified: boolean;
}

export type TransactionState = {
txId: string;
hash: string;
}

export class DeploymentState {

private state: State;

constructor(preloadedState: State | null) {
this.state = preloadedState ?? { contracts: [] };
this.state = preloadedState ?? { contracts: [], transactions: []};
}

public isDeployedAndVerified(contractName: string): boolean {
Expand Down Expand Up @@ -93,6 +99,27 @@ export class DeploymentState {
this.persistState();
}

public setTransactionId(txId: string, txHash: string): void {
const transactionState = this.state.transactions.find(t => t.txId === txId);
if (transactionState !== undefined) {
throw new Error(`Transaction state already exists for ${txId}`);
}
this.state.transactions.push({ txId: txId, hash: txHash });
this.persistState();
}

public hasTransactionId(txId: string): boolean {
return this.state.transactions.find(t => t.txId === txId) !== undefined;
}

public getTransactionHash(txId: string): string {
const transactionState = this.state.transactions.find(t => t.txId === txId);
if (transactionState === undefined) {
throw new Error(`Transaction state not found for ${txId}`);
}
return transactionState.hash;
}

private persistState() {
const json = JSON.stringify(this.state);
fs.writeFileSync(deploymentFilename(), json);
Expand All @@ -106,4 +133,4 @@ function deploymentFilename(): string {
const deploymentStateFromFile = fs.existsSync(deploymentFilename()) ? JSON.parse(fs.readFileSync(deploymentFilename()).toString()) : null;
export const deploymentState = new DeploymentState(deploymentStateFromFile);

export const isResumeableDeployment = process.env.RESUMEABLE_DEPLOYMENT === "true";
export const isResumeableDeployment = process.env.RESUMEABLE_DEPLOYMENT?.toLowerCase() === "true";
84 changes: 66 additions & 18 deletions scripts/libs/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { deployContract } from "./deployment";
import { LibraryAddresses } from "./libraries";
import { RegistryAddresses } from "./registry";
import { ServiceAddresses } from "./services";
import { executeTx, getFieldFromLogs, getFieldFromTxRcptLogs } from "./transaction";
import { executeTx, getFieldFromLogs, getFieldFromTxRcptLogs, getTxOpts } from "./transaction";

export type InstanceAddresses = {
accessManagerAddress: AddressLike,
Expand Down Expand Up @@ -38,7 +38,10 @@ export async function deployAndRegisterMasterInstance(
}
)
const accessManager = accessManagerBaseContract as AccessManagerExtendedInitializeable;
await executeTx(() => accessManager.initialize(resolveAddress(owner)));
await executeTx(
() => accessManager.initialize(resolveAddress(owner), getTxOpts()),
"masterInstance accessManager.initialize"
);

const { address: instanceAddress, contract: masterInstanceBaseContract } = await deployContract(
"Instance",
Expand All @@ -53,7 +56,10 @@ export async function deployAndRegisterMasterInstance(
}
);
const instance = masterInstanceBaseContract as Instance;
await executeTx(() => instance.initialize(accessManagerAddress, registry.registryAddress, resolveAddress(owner)));
await executeTx(
() => instance.initialize(accessManagerAddress, registry.registryAddress, resolveAddress(owner), getTxOpts()),
"masterInstance instance.initialize"
);

const { address: instanceStoreAddress, contract: masterInstanceStoreContract } = await deployContract(
"InstanceStore",
Expand All @@ -78,8 +84,14 @@ export async function deployAndRegisterMasterInstance(
);

const instanceStore = masterInstanceStoreContract as InstanceStore;
await executeTx(() => instanceStore.initialize(instanceAddress));
await executeTx(() => instance.setInstanceStore(instanceStore));
await executeTx(
() => instanceStore.initialize(instanceAddress, getTxOpts()),
"masterInstance instanceStore.initialize"
);
await executeTx(
() => instance.setInstanceStore(instanceStore, getTxOpts()),
"masterInstance instance.setInstanceStore"
);

const { address: instanceReaderAddress, contract: masterReaderBaseContract } = await deployContract(
"InstanceReader",
Expand All @@ -102,8 +114,14 @@ export async function deployAndRegisterMasterInstance(
}
);
const instanceReader = masterReaderBaseContract as InstanceReader;
await executeTx(() => instanceReader.initialize(instanceAddress));
await executeTx(() => instance.setInstanceReader(instanceReaderAddress));
await executeTx(
() => instanceReader.initialize(instanceAddress, getTxOpts()),
"masterInstance instanceReader.initialize"
);
await executeTx(
() => instance.setInstanceReader(instanceReaderAddress, getTxOpts()),
"masterInstance instance.setInstanceReader"
);

const {address: bundleManagerAddress, contract: bundleManagerBaseContrat} = await deployContract(
"BundleManager",
Expand All @@ -117,8 +135,14 @@ export async function deployAndRegisterMasterInstance(
}
);
const bundleManager = bundleManagerBaseContrat as BundleManager;
await executeTx(() => bundleManager.initialize(instanceAddress));
await executeTx(() => instance.setBundleManager(bundleManagerAddress));
await executeTx(
() => bundleManager.initialize(instanceAddress, getTxOpts()),
"masterInstance bundleManager.initialize"
);
await executeTx(
() => instance.setBundleManager(bundleManagerAddress, getTxOpts()),
"masterInstance instance.setBundleManager"
);

const { address: instanceAdminAddress, contract: instanceAdminBaseContract } = await deployContract(
"InstanceAdmin",
Expand All @@ -133,23 +157,44 @@ export async function deployAndRegisterMasterInstance(
);
const instanceAdmin = instanceAdminBaseContract as InstanceAdmin;
// grant admin role to master instance admin
await executeTx(() => accessManager.grantRole(0, instanceAdminAddress, 0));
await executeTx(() => instanceAdmin.initialize(instanceAddress));
await executeTx(() => instance.setInstanceAdmin(instanceAdmin));
await executeTx(
() => accessManager.grantRole(0, instanceAdminAddress, 0, getTxOpts()),
"masterInstance accessManager.grantRole masterinstanceadmin"
);
await executeTx(
() => instanceAdmin.initialize(instanceAddress, getTxOpts()),
"masterInstance instanceAdmin.initialize"
);
await executeTx(
() => instance.setInstanceAdmin(instanceAdmin, getTxOpts()),
"masterInstance instance.setInstanceAdmin"
);

logger.debug(`setting master addresses into instance service and registering master instance`);
const rcpt = await executeTx(() => services.instanceService.setAndRegisterMasterInstance(instanceAddress));
const rcpt = await executeTx(
() => services.instanceService.setAndRegisterMasterInstance(instanceAddress, getTxOpts()),
"masterInstance instanceService.setAndRegisterMasterInstance"
);
// this extracts the ObjectInfo struct from the LogRegistration event
const logRegistrationInfo = getFieldFromTxRcptLogs(rcpt!, registry.registry.interface, "LogRegistration", "nftId");
// nftId is the first field of the ObjectInfo struct
const masterInstanceNfdId = (logRegistrationInfo as unknown);

await executeTx(() => registry.chainNft.transferFrom(resolveAddress(owner), MASTER_INSTANCE_OWNER, BigInt(masterInstanceNfdId as string)));
await executeTx(
() => registry.chainNft.transferFrom(resolveAddress(owner), MASTER_INSTANCE_OWNER, BigInt(masterInstanceNfdId as string), getTxOpts()),
"masterInstance transfer ownership nft"
);

// revoke admin role for master instance admin
await executeTx(() => accessManager.revokeRole(0, instanceAdminAddress));
await executeTx(
() => accessManager.revokeRole(0, instanceAdminAddress, getTxOpts()),
"masterInstance accessManager.revokeRole masterinstanceadmin"
);
// revoke admin role for protocol owner
await executeTx(() => accessManager.renounceRole(0, owner));
await executeTx(
() => accessManager.renounceRole(0, owner, getTxOpts()),
"masterInstance accessManager.renounceRole owner"
);

logger.info(`master instance registered - masterInstanceNftId: ${masterInstanceNfdId}`);
logger.info(`master addresses set`);
Expand All @@ -172,7 +217,10 @@ export async function cloneInstance(masterInstance: InstanceAddresses, libraries

const instanceServiceAsClonedInstanceOwner = InstanceService__factory.connect(await resolveAddress(services.instanceServiceAddress), instanceOwner);
logger.debug(`cloning instance ${masterInstance.instanceAddress} ...`);
const cloneTx = await executeTx(async () => await instanceServiceAsClonedInstanceOwner.createInstanceClone());
const cloneTx = await executeTx(
async () => await instanceServiceAsClonedInstanceOwner.createInstanceClone(getTxOpts()),
"clonedInstance instanceService.createInstanceClone"
);
const clonedOzAccessManagerAddress = getFieldFromLogs(cloneTx.logs, instanceServiceAsClonedInstanceOwner.interface, "LogInstanceCloned", "clonedOzAccessManager");
const clonedInstanceAdminAddress = getFieldFromLogs(cloneTx.logs, instanceServiceAsClonedInstanceOwner.interface, "LogInstanceCloned", "clonedInstanceAdmin");
const clonedInstanceAddress = getFieldFromLogs(cloneTx.logs, instanceServiceAsClonedInstanceOwner.interface, "LogInstanceCloned", "clonedInstance");
Expand All @@ -198,7 +246,7 @@ export async function cloneInstanceFromRegistry(registryAddress: AddressLike, in
const registry = IRegistry__factory.connect(await resolveAddress(registryAddress), instanceOwner);
const instanceServiceAddress = await registry.getServiceAddress("InstanceService", "3");
const instanceServiceAsClonedInstanceOwner = InstanceService__factory.connect(await resolveAddress(instanceServiceAddress), instanceOwner);
const cloneTx = await executeTx(async () => await instanceServiceAsClonedInstanceOwner.createInstanceClone());
const cloneTx = await executeTx(async () => await instanceServiceAsClonedInstanceOwner.createInstanceClone(getTxOpts()));
const clonedInstanceAddress = getFieldFromLogs(cloneTx.logs, instanceServiceAsClonedInstanceOwner.interface, "LogInstanceCloned", "clonedInstanceAddress");
const clonedInstanceNftId = getFieldFromLogs(cloneTx.logs, instanceServiceAsClonedInstanceOwner.interface, "LogInstanceCloned", "clonedInstanceNftId");

Expand Down
11 changes: 10 additions & 1 deletion scripts/libs/libraries.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { AddressLike, Signer } from "ethers";
import { deployContract } from "./deployment";
import { logger } from "../logger";
import fs from 'fs';
import hre from 'hardhat';

export type LibraryAddresses = {
nftIdLibAddress: AddressLike;
Expand Down Expand Up @@ -297,6 +299,8 @@ export async function deployLibraries(owner: Signer): Promise<LibraryAddresses>
LIBRARY_ADDRESSES.set("StrLib", strLibAddress);

logger.info("======== Finished deployment of libraries ========");

dumpLibraryAddressesToFile(LIBRARY_ADDRESSES);

return {
nftIdLibAddress,
Expand Down Expand Up @@ -328,4 +332,9 @@ export async function deployLibraries(owner: Signer): Promise<LibraryAddresses>
strLibAddress,
};

}
}

function dumpLibraryAddressesToFile(addresses: Map<string, AddressLike>): void {
const data = JSON.stringify(Object.fromEntries(addresses), null, 2);
fs.writeFileSync(`./libraries_${hre.network.config.chainId}.json`, data);
}
19 changes: 13 additions & 6 deletions scripts/libs/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { logger } from "../logger";
import { deployContract, verifyContract } from "./deployment";
import { LibraryAddresses } from "./libraries";
import { executeTx, getTxOpts } from "./transaction";


export type RegistryAddresses = {
Expand Down Expand Up @@ -216,14 +217,20 @@ export async function deployAndInitializeRegistry(owner: Signer, libraries: Libr
const staking = Staking__factory.connect(stakingAddress, owner);
const stakingNftId = await registry["getNftId(address)"](stakingAddress);

console.log("stakingReader.initialize");
await stakingReader.initialize(stakingAddress, stakingStoreAddress);
await executeTx(
async () => await stakingReader.initialize(stakingAddress, stakingStoreAddress, getTxOpts()),
"stakingReader.initialize"
);

console.log("stakingStore.initialize");
await registry.initialize(releaseManagerAddress, tokenRegistryAddress, stakingAddress);
await executeTx(
async () => await registry.initialize(releaseManagerAddress, tokenRegistryAddress, stakingAddress, getTxOpts()),
"registry.initialize"
);

console.log("stakingManager.initialize");
await registryAdmin.completeSetup(registry, owner, owner);
await executeTx(
async () => await registryAdmin.completeSetup(registry, owner, owner, getTxOpts()),
"registryAdmin.completeSetup"
);

await verifyRegistryComponents(
registryAddress,
Expand Down
33 changes: 20 additions & 13 deletions scripts/libs/release.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@

import { AddressLike, BytesLike, Signer, resolveAddress, AbiCoder, keccak256, hexlify, Interface, solidityPacked, solidityPackedKeccak256, getCreate2Address, defaultAbiCoder, id, concat, Typed, BigNumberish } from "ethers";
import { AddressLike, BigNumberish, BytesLike, Signer } from "ethers";
import { BundleService__factory, DistributionService__factory, InstanceService__factory, PoolService__factory, RegistryService__factory } from "../../typechain-types";
import { logger } from "../logger";
import { PoolService__factory, BundleService__factory, DistributionService__factory, InstanceService__factory, RegistryService__factory, ReleaseManager__factory } from "../../typechain-types";
import { RegistryAddresses } from "./registry";
import { LibraryAddresses } from "./libraries";
import { executeTx, getFieldFromTxRcptLogs } from "./transaction";
import { executeTx, getFieldFromTxRcptLogs, getTxOpts } from "./transaction";


export type ReleaseAddresses = {
Expand Down Expand Up @@ -151,7 +150,7 @@ export type Release = {
};

// TODO implement release addresses computation
export async function computeReleaseAddresses(owner: Signer, registry: RegistryAddresses, libraries: LibraryAddresses, salt: BytesLike): Promise<ReleaseAddresses> {
export async function computeReleaseAddresses(/*owner: Signer, registry: RegistryAddresses, libraries: LibraryAddresses, salt: BytesLike*/): Promise<ReleaseAddresses> {

const releaseAddresses: ReleaseAddresses = {
registryServiceAddress: "0x0000000000000000000000000000000000000001",
Expand Down Expand Up @@ -188,9 +187,9 @@ export async function computeReleaseAddresses(owner: Signer, registry: RegistryA
}


export async function getReleaseConfig(owner: Signer, registry: RegistryAddresses, libraries: LibraryAddresses, salt: BytesLike): Promise<ReleaseConfig>
export async function getReleaseConfig(/*owner: Signer, registry: RegistryAddresses, libraries: LibraryAddresses, salt: BytesLike*/): Promise<ReleaseConfig>
{
const serviceAddresses = await computeReleaseAddresses(owner, registry, libraries, salt);
const serviceAddresses = await computeReleaseAddresses(/*owner, registry, libraries, salt*/);

// prepare config
const config: ReleaseConfig =
Expand Down Expand Up @@ -354,13 +353,21 @@ export async function getReleaseConfig(owner: Signer, registry: RegistryAddresse

export async function createRelease(owner: Signer, registry: RegistryAddresses, config: ReleaseConfig, salt: BytesLike): Promise<Release>
{
const releaseManager = await registry.releaseManager.connect(owner);
await releaseManager.createNextRelease();
const releaseManager = registry.releaseManager.connect(owner);

await executeTx(
async () => await releaseManager.createNextRelease(getTxOpts()),
"releaseManager.createNextRelease"
);


const rcpt = await executeTx(async () => releaseManager.prepareNextRelease(
registry.serviceAuthorizationV3,
salt
));
const rcpt = await executeTx(
async () => releaseManager.prepareNextRelease(
registry.serviceAuthorizationV3,
salt,
getTxOpts()
),
"releaseManager.prepareNextRelease");

let logCreationInfo = getFieldFromTxRcptLogs(rcpt!, registry.releaseManager.interface, "LogReleaseCreation", "version");
const releaseVersion = (logCreationInfo as BigNumberish);
Expand Down
Loading

0 comments on commit fd4ee14

Please sign in to comment.