Skip to content

Commit 9f31ae4

Browse files
authored
feat: Transfer Ownership on Universal SpokePool Deployment (#1121)
* feat: Transfer Ownership on Universal SpokePool Deployment Signed-off-by: Faisal Usmani <[email protected]> * using safe sdk to check for owners Signed-off-by: Faisal Usmani <[email protected]> * Refactor & error on no safe wallet Signed-off-by: Faisal Usmani <[email protected]> --------- Signed-off-by: Faisal Usmani <[email protected]>
1 parent ccb7195 commit 9f31ae4

File tree

6 files changed

+90
-32
lines changed

6 files changed

+90
-32
lines changed

deploy/111_deploy_universal_spokepool.ts

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
import { DeployFunction } from "hardhat-deploy/types";
22
import { HardhatRuntimeEnvironment } from "hardhat/types";
33
import { deployNewProxy, getSpokePoolDeploymentInfo } from "../utils/utils.hre";
4-
import { FILL_DEADLINE_BUFFER, L2_ADDRESS_MAP, QUOTE_TIME_BUFFER, USDC, ZERO_ADDRESS } from "./consts";
4+
import {
5+
EXPECTED_SAFE_ADDRESS,
6+
FILL_DEADLINE_BUFFER,
7+
L2_ADDRESS_MAP,
8+
QUOTE_TIME_BUFFER,
9+
USDC,
10+
ZERO_ADDRESS,
11+
} from "./consts";
512
import { CHAIN_IDs, PRODUCTION_NETWORKS, TOKEN_SYMBOLS_MAP } from "../utils/constants";
6-
import { getOftEid, toWei } from "../utils/utils";
13+
import { getOftEid, toWei, predictedSafe } from "../utils/utils";
14+
import { getNodeUrl } from "../utils";
715
import { getDeployedAddress } from "../src/DeploymentUtils";
16+
import "@nomiclabs/hardhat-ethers";
17+
import Safe from "@safe-global/protocol-kit";
818

919
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
1020
const { hubPool, hubChainId, spokeChainId } = await getSpokePoolDeploymentInfo(hre);
@@ -53,7 +63,38 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
5363
// target address of the spoke pool. This is because the HubPool does not pass in the chainId when calling
5464
// relayMessage() on the Adapter. Therefore, if Universal SpokePools share the same address,
5565
// then a message designed to be sent to one chain could be sent to another's SpokePool.
56-
await deployNewProxy("Universal_SpokePool", constructorArgs, initArgs);
66+
const { proxyAddress } = await deployNewProxy("Universal_SpokePool", constructorArgs, initArgs);
67+
68+
if (!proxyAddress) {
69+
return;
70+
}
71+
72+
const nodeUrl = getNodeUrl(spokeChainId);
73+
74+
const protocolKit = await Safe.init({
75+
provider: nodeUrl,
76+
predictedSafe,
77+
});
78+
79+
const existingProtocolKit = await protocolKit.connect({
80+
safeAddress: EXPECTED_SAFE_ADDRESS,
81+
});
82+
const isDeployed = await existingProtocolKit.isSafeDeployed();
83+
84+
if (!isDeployed) {
85+
throw new Error("Expected Safe address is not deployed, please deploy it first");
86+
}
87+
88+
const factory = await hre.ethers.getContractFactory("Universal_SpokePool");
89+
const contract = factory.attach(proxyAddress);
90+
91+
const owner = await contract.owner();
92+
if (owner !== EXPECTED_SAFE_ADDRESS) {
93+
await (await contract.transferOwnership(EXPECTED_SAFE_ADDRESS)).wait();
94+
console.log("Transferred ownership to Expected Safe address:", await contract.owner());
95+
} else {
96+
console.log("Expected Safe address is already the owner of the Universal SpokePool");
97+
}
5798
};
5899
module.exports = func;
59100
func.tags = ["UniversalSpokePool"];

deploy/consts.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ export const ZK_L1_GAS_TO_L2_GAS_PER_PUBDATA_LIMIT = 800;
2222
export const ZK_L2_GAS_LIMIT = 2000000;
2323
export const ZK_MAX_GASPRICE = "10000000000000"; // 10k gwei
2424

25+
// Expected Safe address for Universal SpokePool
26+
export const EXPECTED_SAFE_ADDRESS = "0x0Fc8E2BB9bEd4FDb51a0d36f2415c4C7F9e75F6e";
27+
export const EXPECTED_SAFE_OWNERS = [
28+
"0x868CF19464e17F76D6419ACC802B122c22D2FD34",
29+
"0xcc400c09ecBAC3e0033e4587BdFAABB26223e37d",
30+
"0x837219D7a9C666F5542c4559Bf17D7B804E5c5fe",
31+
"0x1d933Fd71FF07E69f066d50B39a7C34EB3b69F05",
32+
"0x996267d7d1B7f5046543feDe2c2Db473Ed4f65e9",
33+
];
34+
2535
export const L1_ADDRESS_MAP: { [key: number]: { [contractName: string]: string } } = {
2636
[CHAIN_IDs.MAINNET]: {
2737
finder: "0x40f941E48A552bF496B154Af6bf55725f18D77c3",

hardhat.config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,14 @@ const config: HardhatUserConfig = {
309309
browserURL: "https://explorer.redstone.xyz",
310310
},
311311
},
312+
{
313+
network: "plasma",
314+
chainId: 9745,
315+
urls: {
316+
apiURL: "https://api.routescan.io/v2/network/mainnet/evm/9745/etherscan",
317+
browserURL: "https://plasmascan.to",
318+
},
319+
},
312320
{
313321
network: "soneium",
314322
chainId: CHAIN_IDs.SONEIUM,

scripts/deployMultisig.ts

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,8 @@
11
import { getNodeUrl } from "../utils";
2-
import { ethers } from "../utils/utils";
2+
import { ethers, predictedSafe } from "../utils/utils";
33
import { hre } from "../utils/utils.hre";
4-
import Safe, { SafeAccountConfig, PredictedSafeProps } from "@safe-global/protocol-kit";
5-
6-
const safeAccountConfig: SafeAccountConfig = {
7-
owners: [
8-
"0x868CF19464e17F76D6419ACC802B122c22D2FD34",
9-
"0xcc400c09ecBAC3e0033e4587BdFAABB26223e37d",
10-
"0x837219D7a9C666F5542c4559Bf17D7B804E5c5fe",
11-
"0x1d933Fd71FF07E69f066d50B39a7C34EB3b69F05",
12-
"0x996267d7d1B7f5046543feDe2c2Db473Ed4f65e9",
13-
],
14-
threshold: 2,
15-
};
16-
const EXPECTED_SAFE_ADDRESS = "0x0Fc8E2BB9bEd4FDb51a0d36f2415c4C7F9e75F6e";
17-
const predictedSafe: PredictedSafeProps = {
18-
safeAccountConfig,
19-
safeDeploymentConfig: {
20-
// Safe addresses are deterministic based on owners and salt nonce.
21-
saltNonce: "0x1234",
22-
},
23-
};
4+
import { EXPECTED_SAFE_ADDRESS } from "../deploy/consts";
5+
import Safe from "@safe-global/protocol-kit";
246

257
/**
268
* Script to deploy a new Safe Multisig contract via the Safe SDK. Run via:

utils/utils.hre.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export async function deployNewProxy(
4343
constructorArgs: FnArgs[],
4444
initArgs: FnArgs[],
4545
implementationOnly?: boolean
46-
): Promise<void> {
46+
): Promise<{ proxyAddress?: string; implementationAddress: string }> {
4747
const { deployments, run, upgrades, getChainId } = hre;
4848
let chainId = Number(await getChainId());
4949

@@ -54,11 +54,12 @@ export async function deployNewProxy(
5454
}
5555

5656
// If a SpokePool can be found in deployments/deployments.json, then only deploy an implementation contract.
57-
const proxy = getDeployedAddress("SpokePool", chainId, false);
58-
implementationOnly ??= proxy !== undefined;
57+
let proxyAddress = getDeployedAddress("SpokePool", chainId, false);
58+
implementationOnly ??= proxyAddress !== undefined;
59+
let implementationAddress: string;
5960
if (implementationOnly) {
60-
console.log(`${name} deployment already detected @ ${proxy}, deploying new implementation.`);
61-
instance = (await upgrades.deployImplementation(await getContractFactory(name, {}), {
61+
console.log(`${name} deployment already detected @ ${proxyAddress}, deploying new implementation.`);
62+
implementationAddress = instance = (await upgrades.deployImplementation(await getContractFactory(name, {}), {
6263
constructorArgs,
6364
kind: "uups",
6465
unsafeAllow: unsafeAllowArgs as unsafeAllowTypes,
@@ -71,9 +72,9 @@ export async function deployNewProxy(
7172
constructorArgs,
7273
initializer: "initialize",
7374
});
74-
instance = (await proxy.deployed()).address;
75+
proxyAddress = instance = (await proxy.deployed()).address;
7576
console.log(`New ${name} proxy deployed @ ${instance}`);
76-
const implementationAddress = await upgrades.erc1967.getImplementationAddress(instance);
77+
implementationAddress = await upgrades.erc1967.getImplementationAddress(instance);
7778
console.log(`${name} implementation deployed @ ${implementationAddress}`);
7879
}
7980

@@ -93,6 +94,8 @@ export async function deployNewProxy(
9394
// https://docs.openzeppelin.com/upgrades-plugins/1.x/api-hardhat-upgrades#verify
9495
const contract = `contracts/${name}.sol:${name}`;
9596
await verifyContract(instance, constructorArgs, contract);
97+
98+
return { proxyAddress, implementationAddress };
9699
}
97100

98101
export async function verifyContract(address: string, constructorArguments: any[], contract?: string) {

utils/utils.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import { smock, FakeContract } from "@defi-wonderland/smock";
88
import { FactoryOptions } from "hardhat/types";
99
import { ethers } from "hardhat";
1010
import { BigNumber, Signer, Contract, ContractFactory, BaseContract } from "ethers";
11-
import { OFT_EIDs } from "../deploy/consts";
11+
import { EXPECTED_SAFE_OWNERS, OFT_EIDs } from "../deploy/consts";
1212
export { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
13+
import { SafeAccountConfig, PredictedSafeProps } from "@safe-global/protocol-kit";
1314

1415
chai.use(smock.matchers);
1516

@@ -218,3 +219,16 @@ export function getOftEid(chainId: number): number {
218219
}
219220
return value;
220221
}
222+
223+
export const safeAccountConfig: SafeAccountConfig = {
224+
owners: EXPECTED_SAFE_OWNERS,
225+
threshold: 2,
226+
};
227+
228+
export const predictedSafe: PredictedSafeProps = {
229+
safeAccountConfig,
230+
safeDeploymentConfig: {
231+
// Safe addresses are deterministic based on owners and salt nonce.
232+
saltNonce: "0x1234",
233+
},
234+
};

0 commit comments

Comments
 (0)