Skip to content
This repository has been archived by the owner on Oct 6, 2023. It is now read-only.

Commit

Permalink
Move checkIfProxyAdmin to checkIfManagedByProxyAdmin
Browse files Browse the repository at this point in the history
  • Loading branch information
0xNeshi committed Sep 19, 2023
1 parent 113a36c commit 4bd7310
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 72 deletions.
66 changes: 2 additions & 64 deletions tasks/manage/changeProxyAdmin.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import {task} from "hardhat/config";
import {
ITransparentUpgradeableProxy__factory,
ProxyAdminMultiSig__factory,
ProxyContract__factory,
} from "typechain-types";
import {ITransparentUpgradeableProxy__factory, ProxyContract__factory} from "typechain-types";
import {confirmAction, getAddresses, getProxyAdminOwner, logger} from "utils";
import {submitMultiSigTx} from "../helpers";
import {HardhatRuntimeEnvironment} from "hardhat/types";

class CheckError extends Error {}

type TaskArgs = {
to?: string;
Expand Down Expand Up @@ -51,8 +44,6 @@ task("manage:changeProxyAdmin", "Will update the proxy admin the target proxy co
return logger.out(`"${targetAddress}" is already the proxy admin.`);
}

await checkIfProxyAdmin(curAdmin, proxyAdminOwner.address, hre);

logger.out(`Current Admin: ${curAdmin}`);

// submitting the Tx
Expand All @@ -78,59 +69,6 @@ task("manage:changeProxyAdmin", "Will update the proxy admin the target proxy co
}
}
} catch (error) {
if (error instanceof CheckError) {
logger.out(error, logger.Level.Warn);
} else {
logger.out(error, logger.Level.Error);
}
logger.out(error, logger.Level.Error);
}
});

/**
* It is possible the Endowment's proxy is not managed by our ProxyAdminMultiSig.
* In those cases we should skip submitting any Txs to avoid wasting gas.
* @param adminAddress Address of the current proxy admin
* @param adminOwner Address of one of the current proxy admin's owners
* @param hre HardhatRuntimeEnvironment
*/
async function checkIfProxyAdmin(
adminAddress: string,
adminOwner: string,
hre: HardhatRuntimeEnvironment
): Promise<void> {
// this is just a rudimendaty check that will throw if `curAdmin` is not really a ProxyAdminMultiSig
// in which case it'll throw and stop the execution. We check this by assuming that if the address
// is connected to a contract that contains the `submitTransaction` function, then there's a high
// likelihood that the said contract is ProxyAdminMultiSig
const proxyAdminMS = ProxyAdminMultiSig__factory.connect(adminAddress, hre.ethers.provider);
const bytecode = await hre.ethers.provider.getCode(adminAddress);

// No code : "0x" then functionA is definitely not there
if (bytecode.length <= 2) {
throw new CheckError("Skipping: admin address has no code");
}

// If the bytecode doesn't include the function selector submitTransaction()
// is definitely not present
const submitTransactionSelector = proxyAdminMS.interface.getSighash(
proxyAdminMS.interface.functions["submitTransaction(address,uint256,bytes,bytes)"]
);
if (!bytecode.includes(submitTransactionSelector.slice(2))) {
throw new CheckError(
"Skipping: not a ProxyAdminMultiSig - no submitTransaction() function selector in bytecode"
);
}

const isOwnersSelector = proxyAdminMS.interface.getSighash(
proxyAdminMS.interface.functions["isOwner(address)"]
);
if (!bytecode.includes(isOwnersSelector.slice(2))) {
throw new CheckError(
"Skipping: not a ProxyAdminMultiSig - no isOwner() function selector in bytecode"
);
}

if (!(await proxyAdminMS.isOwner(adminOwner))) {
throw new CheckError(`Skipping: "${adminOwner}" is not one of the target contract's owners`);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import {HardhatRuntimeEnvironment} from "hardhat/types";
import {IEndowmentMultiSigFactory__factory} from "typechain-types";
import {getAddresses} from "utils";
import {
IEndowmentMultiSigFactory__factory,
ProxyAdminMultiSig__factory,
ProxyContract__factory,
} from "typechain-types";
import {getAddresses, getProxyAdminOwner, logger} from "utils";

class CheckError extends Error {}

export default async function updateEndowmentProxiesAdmin(
targetAddress: string,
proxyAdminPkey: string | undefined,
hre: HardhatRuntimeEnvironment
) {
const addresses = await getAddresses(hre);
const proxyAdminOwner = await getProxyAdminOwner(hre, proxyAdminPkey);

const endowmentMultiSigFactory = IEndowmentMultiSigFactory__factory.connect(
addresses.multiSig.endowment.factory,
Expand All @@ -16,11 +23,78 @@ export default async function updateEndowmentProxiesAdmin(
const endowmentProxies = await endowmentMultiSigFactory.getInstantiations();

for (const endowmentProxy of endowmentProxies) {
await hre.run("manage:changeProxyAdmin", {
to: targetAddress,
proxy: endowmentProxy,
proxyAdminPkey: proxyAdminPkey,
yes: true,
});
try {
await checkIfManagedByProxyAdmin(endowmentProxy, proxyAdminOwner.address, hre);

await hre.run("manage:changeProxyAdmin", {
to: targetAddress,
proxy: endowmentProxy,
proxyAdminPkey: proxyAdminPkey,
yes: true,
});
} catch (error) {
if (error instanceof CheckError) {
logger.out(error, logger.Level.Warn);
} else {
logger.out(error, logger.Level.Error);
}
}
}
}

/**
* It is possible the Endowment's proxy is not managed by our ProxyAdminMultiSig.
* In those cases we should skip submitting any Txs to avoid wasting gas.
*
* This is just a rudimendaty check that will throw if `curAdmin` is not really a ProxyAdminMultiSig
* in which case it'll throw and stop the execution. We check this by assuming that if the address
* is connected to a contract that contains the `submitTransaction` and `isOwner` functions and also
* that the currently used proxy admin owner is really one of the owners of of the proxy's admin contract,
* then it's most likely that the said proxy admin contract is an instance of ProxyAdminMultiSig.
*
* @param proxy Address of the proxy to check
* @param proxyAdminOwner Address of one of the current proxy admin's owners
* @param hre HardhatRuntimeEnvironment
*/
async function checkIfManagedByProxyAdmin(
proxy: string,
proxyAdminOwner: string,
hre: HardhatRuntimeEnvironment
): Promise<void> {
const proxyContract = ProxyContract__factory.connect(proxy, hre.ethers.provider);
const curAdmin = await proxyContract.getAdmin();

const proxyAdminMS = ProxyAdminMultiSig__factory.connect(curAdmin, hre.ethers.provider);
const bytecode = await hre.ethers.provider.getCode(curAdmin);

// No code : "0x" then functionA is definitely not there
if (bytecode.length <= 2) {
throw new CheckError("Skipping: admin address has no code");
}

// If the bytecode doesn't include the function selector submitTransaction()
// is definitely not present
const submitTransactionSelector = proxyAdminMS.interface.getSighash(
proxyAdminMS.interface.functions["submitTransaction(address,uint256,bytes,bytes)"]
);
if (!bytecode.includes(submitTransactionSelector.slice(2))) {
throw new CheckError(
"Skipping: not a ProxyAdminMultiSig - no submitTransaction() function selector in bytecode"
);
}

const isOwnersSelector = proxyAdminMS.interface.getSighash(
proxyAdminMS.interface.functions["isOwner(address)"]
);
if (!bytecode.includes(isOwnersSelector.slice(2))) {
throw new CheckError(
"Skipping: not a ProxyAdminMultiSig - no isOwner() function selector in bytecode"
);
}

if (!(await proxyAdminMS.isOwner(proxyAdminOwner))) {
throw new CheckError(
`Skipping: "${proxyAdminOwner}" is not one of the target contract's owners`
);
}
}

0 comments on commit 4bd7310

Please sign in to comment.