Skip to content

Commit

Permalink
feat: clone nor script
Browse files Browse the repository at this point in the history
  • Loading branch information
avsetsin committed Jul 10, 2024
1 parent f0e95e0 commit 3f2f611
Show file tree
Hide file tree
Showing 12 changed files with 669 additions and 9 deletions.
486 changes: 486 additions & 0 deletions abi/aragon/APMRegistry.json

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions configs/deployed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const importConfigFile = (path?: string) => {
return json;
};

export const getContracts = () => {
export const getConfig = () => {
const deployedFile = envs?.DEPLOYED;

if (!deployedFile) {
Expand All @@ -30,7 +30,11 @@ export const getContracts = () => {
};

export const getContractDeploy = (path: string) => {
return getValueByPath(getContracts(), path);
return getValueByPath(getConfig(), path);
};

export const getConfigValue = (path: string) => {
return getValueByPath(getConfig(), path);
};

export const getDeployedAddress = (...contractKeys: string[]) => {
Expand Down Expand Up @@ -65,7 +69,7 @@ export const getOptionalDeployedAddress = (...contractKeys: string[]) => {
};

export const getAddressMap = () => {
const contracts = getContracts();
const contracts = getConfig();

return Object.entries(contracts).reduce(
(acc, [key, value]) => {
Expand Down
7 changes: 7 additions & 0 deletions contracts/apm-registry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Contract } from 'ethers';
import { wallet } from '@providers';
import { getDeployedAddress } from '@configs';
import abi from 'abi/aragon/APMRegistry.json';

export const lidoApmAddress = getDeployedAddress('lidoApm');
export const lidoApmContract = new Contract(lidoApmAddress, abi, wallet);
1 change: 1 addition & 0 deletions contracts/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './allowed-list';
export * from './apm-registry';
export * from './app-proxy';
export * from './aragon';
export * from './burner';
Expand Down
21 changes: 21 additions & 0 deletions programs/aragon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { program } from '@command';
import { getConfigValue } from '@configs';
import { kernelContract } from '@contracts';
import { contractCallTxWithConfirm } from '@utils';
import { namehash } from 'ethers';

const aragon = program.command('aragon').description('interact with aragon contracts');

aragon
.command('deploy-app-proxy')
.aliases(['deploy-proxy'])
.description('deploy a proxy contract')
.argument('<app-name>', 'name of the app')
.action(async (appName) => {
const apmEnsName = getConfigValue('lidoApmEnsName');
const appFullName = `${appName}.${apmEnsName}`;
const appId = namehash(appFullName);
const kernelAddress = await kernelContract.getAddress();

await contractCallTxWithConfirm(kernelContract, 'newAppProxy(address,bytes32)', [kernelAddress, appId]);
});
1 change: 1 addition & 0 deletions programs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from './accounting-consensus';
export * from './accounting-oracle';
export * from './accounts';
export * from './allowed-list';
export * from './aragon';
export * from './burner';
export * from './csm-consensus';
export * from './csm-oracle';
Expand Down
136 changes: 136 additions & 0 deletions programs/omnibus-scripts/clone-nor-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { getConfigValue } from '@configs';
import {
aclContract,
aragonAgentAddress,
burnerAddress,
burnerContract,
kernelContract,
lidoApmAddress,
lidoApmContract,
locatorAddress,
norAddress,
norContract,
stakingRouterAddress,
stakingRouterContract,
votingAddress,
} from '@contracts';
import { encodeFromAgent, votingNewVote } from '@scripts';
import { CallScriptActionWithDescription, encodeCallScript, forwardVoteFromTm, getRoleHash } from '@utils';
import { Contract, Interface, namehash } from 'ethers';
import { joinVotingDesc } from './generators';
import { wallet } from '@providers';

const VERSION = [1, 0, 0];
const CONTENT_URI = '0x' + '00'.repeat(51);
const APP_NAME = 'simple-dvt';

const MODULE_NAME = 'SimpleDVT';
const TARGET_SHARE = 10_000; // 100%
const MODULE_FEE = 800; // 8%
const TREASURY_FEE = 200; // 2%

// Use `aragon deploy-proxy <APP_NAME>` for deployment
const APP_PROXY_ADDRESS = '0xD4b2843eAB523a2531b25697A1c74b1acB848619';

export const cloneNorModule = async () => {
const aragonProxyIface = new Interface(['function implementation() external view returns (address)']);
const norProxyContract = new Contract(norAddress, aragonProxyIface, wallet);

const implementation = await norProxyContract.implementation();
const apmEnsName = getConfigValue('lidoApmEnsName');
const appFullName = `${APP_NAME}.${apmEnsName}`;
const appId = namehash(appFullName);
const appProxyContract = (await norContract.attach(APP_PROXY_ADDRESS)) as Contract;

// 1. New repo with version
const newRepoWithVersionScript: CallScriptActionWithDescription = {
to: lidoApmAddress,
data: lidoApmContract.interface.encodeFunctionData('newRepoWithVersion', [
APP_NAME,
votingAddress,
VERSION,
implementation,
CONTENT_URI,
]),
desc: `Create a new repo with version ${VERSION.join('.')} for ${APP_NAME}`,
};

// 2. Link appId with implementation
const appBasesNamespace = await kernelContract.APP_BASES_NAMESPACE();
const linkAppIdScript: CallScriptActionWithDescription = {
to: await kernelContract.getAddress(),
data: kernelContract.interface.encodeFunctionData('setApp', [appBasesNamespace, appId, implementation]),
desc: `Link ${appId} with the implementation ${implementation}`,
};

// 3. Initialize
const moduleType = await norContract.getType();
const stuckPenaltyDelay = await norContract.getStuckPenaltyDelay();
const initializeScript: CallScriptActionWithDescription = {
to: APP_PROXY_ADDRESS,
data: appProxyContract.interface.encodeFunctionData('initialize', [locatorAddress, moduleType, stuckPenaltyDelay]),
desc: 'Initialize the new module',
};

// 4. Grant staking router permission
const stakingRouterRole = await getRoleHash(norContract, 'STAKING_ROUTER_ROLE');
const stakingRouterPermissionScript: CallScriptActionWithDescription = {
to: await aclContract.getAddress(),
data: aclContract.interface.encodeFunctionData('createPermission', [
stakingRouterAddress,
APP_PROXY_ADDRESS,
stakingRouterRole,
votingAddress,
]),
desc: 'Grant STAKING_ROUTER_ROLE permission to the new module',
};

// 5. Grant burn shares role
const burnSharesRoleHash = await getRoleHash(burnerContract, 'REQUEST_BURN_SHARES_ROLE');
const [, burnSharesPermissionScript] = encodeFromAgent({
to: burnerAddress,
data: burnerContract.interface.encodeFunctionData('grantRole', [burnSharesRoleHash, APP_PROXY_ADDRESS]),
desc: 'Grant REQUEST_BURN_SHARES_ROLE role to the new module',
});

// 6. Grant staking router manage role
const stakingRouterManageRoleHash = await getRoleHash(stakingRouterContract, 'STAKING_MODULE_MANAGE_ROLE');
const [, stakingRouterManageRoleScript] = encodeFromAgent({
to: stakingRouterAddress,
data: stakingRouterContract.interface.encodeFunctionData('grantRole', [
stakingRouterManageRoleHash,
aragonAgentAddress,
]),
desc: 'Grant STAKING_MODULE_MANAGE_ROLE role to the Agent',
});

// 7. Add module to router
const stakingRouterIface = new Interface(['function addStakingModule(string,address,uint256,uint256,uint256)']);
const [, addModuleScript] = encodeFromAgent({
to: stakingRouterAddress,
data: stakingRouterIface.encodeFunctionData('addStakingModule', [
MODULE_NAME,
APP_PROXY_ADDRESS,
TARGET_SHARE,
MODULE_FEE,
TREASURY_FEE,
]),
desc: 'Add the new module to the staking router',
});

const calls: CallScriptActionWithDescription[] = [
newRepoWithVersionScript,
linkAppIdScript,
initializeScript,
stakingRouterPermissionScript,
burnSharesPermissionScript,
stakingRouterManageRoleScript,
addModuleScript,
];

const description = joinVotingDesc(calls);
const voteEvmScript = encodeCallScript(calls);
const [newVoteCalldata] = votingNewVote(voteEvmScript, description);

await forwardVoteFromTm(newVoteCalldata);
};
5 changes: 1 addition & 4 deletions programs/omnibus-scripts/devnet-start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
promptScriptsVEBOInitialEpoch,
promptScriptsVEBOMembers,
promptRolesBeneficiary,
joinVotingDesc,
} from './generators';
import chalk from 'chalk';

Expand Down Expand Up @@ -158,7 +159,3 @@ const promptScriptsRoles = async () => {
);
return [...srScripts, ...norScripts, ...aoScripts, ...veboScripts, ...oracleConfigScripts, ...sanityCheckerScripts];
};

const joinVotingDesc = (calls: CallScriptActionWithDescription[]) => {
return calls.map(({ desc }, index) => `${index + 1}. ${desc}`).join('\n');
};
5 changes: 5 additions & 0 deletions programs/omnibus-scripts/generators/desc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { CallScriptActionWithDescription } from '@utils';

export const joinVotingDesc = (calls: CallScriptActionWithDescription[]) => {
return calls.map(({ desc }, index) => `${index + 1}. ${desc}`).join('\n');
};
1 change: 1 addition & 0 deletions programs/omnibus-scripts/generators/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './access-control';
export * from './accounting-oracle';
export * from './aragon';
export * from './desc';
export * from './dsm';
export * from './exit-bus-oracle';
export * from './lido';
Expand Down
1 change: 1 addition & 0 deletions programs/omnibus-scripts/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './clone-nor-module';
export * from './devnet-start';
export * from './staking-router-2';
export * from './staking-router-fix';
4 changes: 2 additions & 2 deletions utils/role-hash.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Contract, isHexString } from 'ethers';
import { wallet } from '@providers';

export const getRoleHash = async (contract: Contract, role: string) => {
export const getRoleHash = async (contract: Contract, role: string): Promise<string> => {
if (isHexString(role)) return role;
return await contract[role]();
};

export const getRoleHashByAddress = async (address: string, role: string) => {
export const getRoleHashByAddress = async (address: string, role: string): Promise<string> => {
if (isHexString(role)) return role;

const contract = new Contract(
Expand Down

0 comments on commit 3f2f611

Please sign in to comment.