Skip to content

Commit

Permalink
Tested with cold wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
shirren committed Dec 15, 2023
1 parent bafcfad commit 5a500ce
Show file tree
Hide file tree
Showing 11 changed files with 1,556 additions and 46 deletions.
40 changes: 21 additions & 19 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,31 +39,31 @@ const config: HardhatUserConfig = {
// Define here to easily specify private keys
localhost: loadAndValidateEnvironment('localhost')
? {
url: 'http://127.0.0.1:8545',
accounts: [process.env.DEPLOYER_PRIV_KEY!, process.env.WALLET_IMPL_CHANGER_PRIV_KEY!]
}
url: 'http://127.0.0.1:8545',
accounts: [process.env.DEPLOYER_PRIV_KEY!, process.env.WALLET_IMPL_CHANGER_PRIV_KEY!]
}
: {
url: 'SET ENVIRONMENT VARIABLES',
accounts: []
},
url: 'SET ENVIRONMENT VARIABLES',
accounts: []
},
devnet: loadAndValidateEnvironment('devnet')
? {
url: 'https://rpcx.dev.immutable.com',
accounts: [process.env.DEPLOYER_PRIV_KEY!, process.env.WALLET_IMPL_CHANGER_PRIV_KEY!]
}
url: 'https://rpcx.dev.immutable.com',
accounts: [process.env.DEPLOYER_PRIV_KEY!, process.env.WALLET_IMPL_CHANGER_PRIV_KEY!]
}
: {
url: 'SET ENVIRONMENT VARIABLES',
accounts: []
},
url: 'SET ENVIRONMENT VARIABLES',
accounts: []
},
testnet: loadAndValidateEnvironment('testnet')
? {
url: 'https://rpc.testnet.immutable.com',
accounts: [process.env.DEPLOYER_PRIV_KEY!, process.env.WALLET_IMPL_CHANGER_PRIV_KEY!]
}
url: 'https://rpc.testnet.immutable.com',
accounts: [process.env.DEPLOYER_PRIV_KEY!, process.env.WALLET_IMPL_CHANGER_PRIV_KEY!]
}
: {
url: 'SET ENVIRONMENT VARIABLES',
accounts: []
},
url: 'SET ENVIRONMENT VARIABLES',
accounts: []
},
sepolia: networkConfig('sepolia'),
mainnet: networkConfig('mainnet'),
ropsten: networkConfig('ropsten'),
Expand Down Expand Up @@ -104,5 +104,7 @@ export default config;

function loadAndValidateEnvironment(network: string): boolean {
require('dotenv').config({ path: `.env.${network}` });
return !!process.env.DEPLOYER_PRIV_KEY && !!process.env.WALLET_IMPL_CHANGER_PRIV_KEY;
return !!process.env.DEPLOYER_PRIV_KEY &&
!!process.env.WALLET_IMPL_CHANGER_PRIV_KEY &&
!!process.env.DEPLOYER_CONTRACT_ADDRESS;
}
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@
"stop:ganache": "ps aux | grep ganache | grep -v grep | awk '{print $2}' | xargs kill -9",
"deploy:ganache": "hardhat run --network ganache utils/deploy-contracts.ts",
"deploy": "hardhat run utils/deploy-contracts.ts --network",
"deployToLocal": "npx hardhat run scripts/index.ts --network localhost",
"deployToDevnet": "npx hardhat run scripts/index.ts --network devnet",
"deployToTestnet": "npx hardhat run scripts/index.ts --network testnet",
"deployToMainnet": "npx hardhat run scripts/index.ts --network mainnet",
"deployToLocal": "npx hardhat run scripts/deploy.ts --network localhost",
"deployToDevnet": "npx hardhat run scripts/deploy.ts --network devnet",
"deployToTestnet": "npx hardhat run scripts/deploy.ts --network testnet",
"deployToMainnet": "npx hardhat run scripts/deploy.ts --network mainnet",
"verify": "hardhat verify --network",
"release": "yarn publish src"
},
Expand All @@ -45,7 +45,10 @@
"devDependencies": {
"@0xsequence/deployer": "^0.21.5",
"@ethersproject/abi": "^5.7.0",
"@ethersproject/hardware-wallets": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
"@ledgerhq/hw-app-eth": "^6.35.0",
"@ledgerhq/hw-transport-node-hid": "^6.28.0",
"@nomicfoundation/hardhat-chai-matchers": "^1.0.6",
"@nomicfoundation/hardhat-network-helpers": "^1.0.8",
"@nomiclabs/hardhat-ethers": "^2.0.2",
Expand Down Expand Up @@ -82,9 +85,6 @@
"ts-node": "^10.9.1",
"typechain": "^8.1.1",
"typescript": "^4.8.4",
"@ethersproject/hardware-wallets": "^5.7.0",
"@ledgerhq/hw-app-eth": "^6.35.0",
"@ledgerhq/hw-transport-node-hid": "^6.28.0",
"yesno": "^0.4.0"
},
"config": {
Expand All @@ -100,4 +100,4 @@
"ethereum-private-key-to-public-key": "^0.0.5",
"ethereum-public-key-to-address": "^0.0.5"
}
}
}
1,350 changes: 1,350 additions & 0 deletions scripts/abi/OwnableCreate2Deployer.json

Large diffs are not rendered by default.

File renamed without changes.
File renamed without changes.
2 changes: 0 additions & 2 deletions scripts/index.ts → scripts/archived/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import * as path from 'path';
import * as fs from 'fs';
import * as hre from 'hardhat';
import { ethers, Contract, ContractFactory, Signer } from 'ethers';
import { ethers as hardhat } from 'hardhat';

import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { EnvironmentInfo, loadEnvironmentInfo } from './environment';
import { newContractFactory } from './helper-functions';
import { newWalletOptions, WalletOptions } from './wallet-options';
Expand Down
File renamed without changes.
167 changes: 167 additions & 0 deletions scripts/deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import * as fs from 'fs';
import * as hre from 'hardhat';
import { Contract, ContractFactory, Signer, utils } from 'ethers';
import { ethers as hardhat } from 'hardhat';

import { EnvironmentInfo, loadEnvironmentInfo } from './environment';
import { newContractFactory } from './helper-functions';
import { newWalletOptions, WalletOptions } from './wallet-options';
import ContractDeployerInterface from './abi/OwnableCreate2Deployer.json';

/**
* We use the key to generate a salt to generate a deterministic address for
* the contract that isn't dependent on the nonce of the contract deployer account.
*/
const getSaltFromKey = (key: number) => {
return utils.keccak256(utils.defaultAbiCoder.encode(['string'], [key.toString()]));
};

/**
* Load the OwnableCreate2Deployer
*/
const loadDeployerContract = async (env: EnvironmentInfo, walletOptions: WalletOptions): Promise<Contract> => {
return new Contract(env.deployerContractAddress, ContractDeployerInterface.abi, walletOptions.getWallet());
}

/**
* Deploy the contract using the OwnableCreate2Deployer contract.
*/
async function deployContract(
env: EnvironmentInfo,
walletsOptions: WalletOptions,
deployerKey: number,
contractName: string,
constructorArgs: Array<string | undefined>): Promise<Contract> {

let overrides = {
gasLimit: 30000000
};

const salt = getSaltFromKey(deployerKey);
const deployer = await loadDeployerContract(env, walletsOptions);
const contractFactory: ContractFactory = await newContractFactory(walletsOptions.getWallet(), contractName);
const bytecode = contractFactory.getDeployTransaction(...constructorArgs).data;

// Deploy the contract
let tx = await deployer.deploy(bytecode, salt, overrides);
await tx.wait();
// console.log(`[${env.network}] TX hash: ${tx.hash}`);

// Calculate the address the contract is deployed to
const [owner] = await hardhat.getSigners();
const contractAddress = await deployer.deployedAddress(bytecode, owner.address, salt);
return contractFactory.attach(contractAddress);
}

/**
* main function deploys all the SCW infrastructure.
**/
async function main(): Promise<EnvironmentInfo> {
const env = loadEnvironmentInfo(hre.network.name);
const { network, submitterAddress, signerAddress, outputPath } = env;

console.log('');
console.log(`[${network}] Starting deployment...`);
console.log(`[${network}] Submitter address ${submitterAddress}`);
console.log(`[${network}] Signer address ${signerAddress}`);
console.log(`[${network}] Output ${outputPath}`);

// Administration accounts
let multiCallAdminPubKey = '0x575be326c482a487add43974e0eaf232e3366e13';
let factoryAdminPubKey = '0xddb70ddcd14dbd57ae18ec591f47454e4fc818bb';

// CHANGEME: When deploying to mainnet, this address needs to match the second address from the wallet
let walletImplLocatorAdmin = '0xb49c99a17776c10350c2be790e13d4d8dfb1c578';
let signerRootAdminPubKey = '0x65af83f71a05d7f6d06ef9a57c9294b4128ccc2c';
let signerAdminPubKey = '0x69d09644159e7327dbfd0af9a66f8e332c593e79';

// Setup wallet
const wallets: WalletOptions = await newWalletOptions(env);
console.log(
`[${network}] Wallet Impl Locator Changer Address: ${await wallets.getWalletImplLocatorChanger().getAddress()}`
);

// TOTAL deployment cost = 0.009766773 GWEI = 0.000000000009766773 ETHER
// Deployments with esimated gas costs (GWEI)
console.log(`[${network}] Deploying contracts...`);

// Key for the salt, use this to change the address of the contract
let key = 2;

// 1. Deploy multi call deploy
const multiCallDeploy = await deployContract(env, wallets, key, 'MultiCallDeploy', [multiCallAdminPubKey, submitterAddress]);
console.log(`[${network}] MultiCallDeploy address is ${multiCallDeploy.address}`);

// 2. Deploy factory with multi call deploy address as deployer role EST
const factory = await deployContract(env, wallets, key, 'Factory', [factoryAdminPubKey, multiCallDeploy.address]);
console.log(`[${network}] Factory address is ${factory.address}`);

// 3. Deploy wallet impl locator
const walletImplLocator = await deployContract(env, wallets, key, 'LatestWalletImplLocator', [
walletImplLocatorAdmin, await wallets.getWalletImplLocatorChanger().getAddress()
]);
console.log(`[${network}] LatestWalletImplLocator address is ${walletImplLocator.address}`);

// 4. Deploy startup wallet impl
const startupWalletImpl = await deployContract(env, wallets, key, 'StartupWalletImpl', [walletImplLocator.address]);
console.log(`[${network}] StartupWalletImpl address is ${startupWalletImpl.address}`);

// 5. Deploy main module dynamic auth
const mainModuleDynamicAuth = await deployContract(env, wallets, key, 'MainModuleDynamicAuth', [factory.address, startupWalletImpl.address]);
console.log(`[${network}] MainModuleDynamicAuth address is ${mainModuleDynamicAuth.address}`);

// 6. Deploy immutable signer
const immutableSigner = await deployContract(env, wallets, key, 'ImmutableSigner', [signerRootAdminPubKey, signerAdminPubKey, signerAddress]);
console.log(`[${network}] ImmutableSigner address is ${immutableSigner.address}`);

// Fund the implementation changer
// WARNING: If the deployment fails at this step, DO NOT RERUN without commenting out the code a prior which deploys the contracts.
const fundingTx = await wallets.getWallet().sendTransaction({
to: await wallets.getWalletImplLocatorChanger().getAddress(),
value: hardhat.utils.parseEther('10'),
});
await fundingTx.wait();
console.log(`[${network}] Transfered funds to the wallet locator implementer changer with hash ${fundingTx.hash}`);

// Set implementation address on impl locator to dynamic module auth addr
const tx = await walletImplLocator
.connect(wallets.getWalletImplLocatorChanger())
.changeWalletImplementation(mainModuleDynamicAuth.address);
await tx.wait();
console.log(`[${network}] Wallet Impl Locator implementation changed to: ${mainModuleDynamicAuth.address}`);

// Output JSON file with addresses and role addresses
const jsonOutput = {
FactoryAddress: factory.address,
WalletImplLocatorAddress: walletImplLocator.address,
StartupWalletImplAddress: startupWalletImpl.address,
MainModuleDynamicAuthAddress: mainModuleDynamicAuth.address,
ImmutableSignerContractAddress: immutableSigner.address,
MultiCallDeployAddress: multiCallDeploy.address,
DeployerAddress: await wallets.getWallet().getAddress(),
FactoryAdminAddress: factoryAdminPubKey,
FactoryDeployerAddress: env.submitterAddress,
WalletImplLocatorAdminAddress: walletImplLocatorAdmin,
WalletImplLocatorImplChangerAddress: await wallets.getWalletImplLocatorChanger().getAddress(),
SignerRootAdminAddress: signerRootAdminPubKey,
SignerAdminAddress: signerAdminPubKey,
ImmutableSignerAddress: env.signerAddress,
MultiCallAdminAddress: multiCallAdminPubKey,
MultiCallExecutorAddress: env.submitterAddress
};

fs.writeFileSync(env.outputPath, JSON.stringify(jsonOutput, null, 1));

return env;
}

// Call primary function
main()
.then((env: EnvironmentInfo) => {
console.log(`[${env.network}] Contracts deployment successful...`);
process.exit();
})
.catch(err => {
console.error(err.message);
process.exit();
});
2 changes: 2 additions & 0 deletions scripts/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import dotenv from 'dotenv';
export interface EnvironmentInfo {
submitterAddress?: string;
signerAddress?: string;
deployerContractAddress: string;
outputPath: string;
network: string;
}
Expand All @@ -25,6 +26,7 @@ export function loadEnvironmentInfo(hreNetworkName: string): EnvironmentInfo {
return {
submitterAddress: process.env.RELAYER_SUBMITTER_EOA_PUB_KEY,
signerAddress: process.env.IMMUTABLE_SIGNER_PUB_KEY,
deployerContractAddress: process.env.DEPLOYER_CONTRACT_ADDRESS || '',
outputPath: path.join(__dirname, process.env.OUTPUT_FILE_NAME || ''),
network: hreNetworkName
};
Expand Down
9 changes: 0 additions & 9 deletions scripts/errors.ts

This file was deleted.

16 changes: 8 additions & 8 deletions scripts/wallet-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ const mainnetEnv = 'mainnet';
export class WalletOptions {
private useLedger: boolean;
private ledger: LedgerSigner;
private contractDeployer: Signer;
private coldWallet: Signer;
private walletImplLocatorImplChanger: Signer;

constructor(env: EnvironmentInfo, contractDeployer: Signer, walletImplLocatorImplChanger: Signer) {
constructor(env: EnvironmentInfo, coldWallet: Signer, walletImplLocatorImplChanger: Signer) {
if (env.network == mainnetEnv || env.network == 'localhost') {
console.log(`[${env.network}] Using ledger for operations...`);
this.useLedger = true;
Expand All @@ -29,12 +29,12 @@ export class WalletOptions {
this.ledger = new LedgerSigner(hardhat.provider, derivationPath0);

// Setup the 2 programmatic wallets
this.contractDeployer = contractDeployer;
this.coldWallet = coldWallet;
this.walletImplLocatorImplChanger = walletImplLocatorImplChanger;
}

public getContractDeployer(): Signer {
return this.useLedger ? this.ledger : this.contractDeployer;
public getWallet(): Signer {
return this.useLedger ? this.ledger : this.coldWallet;
}

public getWalletImplLocatorChanger(): Signer {
Expand All @@ -48,8 +48,8 @@ export class WalletOptions {
*/
export async function newWalletOptions(env: EnvironmentInfo): Promise<WalletOptions> {
// Required private keys:
// 1. Deployer
// 1. coldWallet
// 2. walletImplLocatorChanger
const [contractDeployer, walletImplLocatorImplChanger]: Signer[] = await hardhat.getSigners();
return new WalletOptions(env, contractDeployer, walletImplLocatorImplChanger);
const [coldWallet, walletImplLocatorImplChanger]: Signer[] = await hardhat.getSigners();
return new WalletOptions(env, coldWallet, walletImplLocatorImplChanger);
}

0 comments on commit 5a500ce

Please sign in to comment.