Skip to content

Commit

Permalink
update deploy multisig bin to read from accounts spec
Browse files Browse the repository at this point in the history
  • Loading branch information
RnkSngh committed Oct 28, 2024
1 parent 8fd1743 commit 0a562c4
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 103 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@open-ibc/vibc-core-smart-contracts",
"version": "4.0.6",
"version": "4.0.7",
"main": "dist/index.js",
"bin": {
"verify-vibc-core-smart-contracts": "./dist/scripts/verify-contract-script.js",
Expand Down
6 changes: 3 additions & 3 deletions src/evm/schemas/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import fs from "fs";
import path from "path";
import { Registry } from "../../utils/registry";
import { renderString } from "../../utils/io";
import { initializedMultisig } from "./multisig";
import { initializedMultisigConfig, uninitializedMultisigConfig } from "./multisig";
import {
isMnemonic,
isPrivateKey,
Expand All @@ -24,7 +24,7 @@ const keyStore = z.object({
export type KeyStore = z.infer<typeof keyStore>;

export const evmAccounts = z.array(
z.union([singleSigAccount, initializedMultisig])
z.union([singleSigAccount, initializedMultisigConfig, uninitializedMultisigConfig])
); // Type of account that one can send transactions from
export type EvmAccounts = z.infer<typeof evmAccounts>;
export const EvmAccountsConfig = z.union([evmAccounts, keyStore]);
Expand Down Expand Up @@ -104,7 +104,7 @@ export class SingleSigAccountRegistry extends Registry<Wallet> {
// load a Map of { [name: string]: Wallet } from EvmAccountsSchema object
export function loadEvmAccounts(config: unknown): Registry<Wallet> {
if (!isEvmAccountsConfig(config)) {
throw new Error(`Error parsing schema: ${config}`);
throw new Error(`Error parsing schema: ${config}: \n ${EvmAccountsConfig.safeParse(config).error}`);
}
const walletMap = new Registry<Wallet>([]);

Expand Down
80 changes: 50 additions & 30 deletions src/evm/schemas/multisig.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,50 @@
import { z } from 'zod';
import {singleSigAccount, wallet} from './wallet';
import { wallet } from './wallet';

// defined in an account spec, which will be cconvertedi nto an initialized multisig config once we deploy the multisig contract
export const uninitializedMultisigConfig = z.object({
name: z.string().min(1),
chainId: z.number(),
owners: z.array(z.string().min(1)),
signer: singleSigAccount
}) .strict()
// defined in an account spec, which will be converted into an initialized multisig config once we deploy the multisig contract
export const uninitializedMultisigConfig = z
.object({
name: z.string().min(1),
privateKey: z.string().min(1),
owners: z.array(z.string().min(1)),
threshold: z.number(),
})
.strict();

// Defined in an account spec, which is not necessarily in a config
export const initializedMultisigConfig = z.object({
name: z.string().min(1),
chainId: z.number(),
safeAddress: z.string().min(1),
signer: singleSigAccount
}) .strict()

export const initializedMultisigConfig = z
.object({
name: z.string().min(1),
chainId: z.number(),
privateKey: z.string().min(1),
safeAddress: z.string().min(1),
})
.strict();

// Multisig which is described in an account spec but is not yet initialized. (i.e. multisig contract has not been deployed yet)
export const unInitializedMultisig = z.intersection(
uninitializedMultisigConfig,
z.object({wallet: wallet})
)
export const unInitializedMultisig = z.object({
name: z.string().min(1),
privateKey: z.string().min(1),
owners: z.array(z.string().min(1)),
threshold: z.number(),
wallet: wallet,
});

// Multisig which has been deployed & can be used to propose transactions. This is the type that loadEvmAccounts will return for multisig types
export const initializedMultisig = z.intersection(
initializedMultisigConfig,
z.object({wallet: wallet})
);
export const initializedMultisig = z.object({
name: z.string().min(1),
chainId: z.number(),
privateKey: z.string().min(1),
safeAddress: z.string().min(1),
wallet: wallet,
});

export type UninitializedMultisigConfig = z.infer<typeof uninitializedMultisigConfig>;
export type InitializedMultisigConfig = z.infer<typeof initializedMultisigConfig>;
export type UninitializedMultisigConfig = z.infer<
typeof uninitializedMultisigConfig
>;
export type InitializedMultisigConfig = z.infer<
typeof initializedMultisigConfig
>;
export type UninitializedMultisig = z.infer<typeof unInitializedMultisig>;
export type InitializedMultisig = z.infer<typeof initializedMultisig>;

Expand All @@ -41,7 +54,7 @@ export const isUninitializedMultisigConfig = (
): account is UninitializedMultisigConfig => {
return uninitializedMultisigConfig.safeParse(account).success;
};

export const isUninitializedMultisig = (
account: unknown
): account is UninitializedMultisig => {
Expand All @@ -60,10 +73,17 @@ export const isInitializedMultisig = (
return initializedMultisig.safeParse(account).success;
};

export const isMultisig = (account: unknown): account is InitializedMultisig | UninitializedMultisig => {
export const isMultisig = (
account: unknown
): account is InitializedMultisig | UninitializedMultisig => {
return isInitializedMultisig(account) || isUninitializedMultisig(account);
};

export const isMultisigConfig = (account: unknown): account is InitializedMultisigConfig | UninitializedMultisigConfig => {
return isInitializedMultisigConfig(account) || isUninitializedMultisigConfig(account);
}
export const isMultisigConfig = (
account: unknown
): account is InitializedMultisigConfig | UninitializedMultisigConfig => {
return (
isInitializedMultisigConfig(account) ||
isUninitializedMultisigConfig(account)
);
};
34 changes: 21 additions & 13 deletions src/evm/schemas/sendingAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ import { fs } from "zx";
import { Registry } from "../../utils/registry";
import {
createWallet,
EvmAccountsConfig,
isEvmAccounts,
isEvmAccountsConfig,
isKeyStore,
} from "./account";
import { isPrivateKey, isSingleSigAccount, Wallet } from "./wallet";
import {
InitializedMultisig,
isInitializedMultisig,
isMultisig,
isMultisigConfig,
isUninitializedMultisig,
unInitializedMultisig,
UninitializedMultisig,
} from "./multisig";
import path from "path";
Expand All @@ -33,7 +33,10 @@ export class SendingAccountRegistry extends Registry<SendingAccount> {
}

static loadMultiple(registryItems: { name: string; registry: any }[]) {
const result = new Registry([] as SendingAccountRegistry[], {
const result = new Registry<
SendingAccountRegistry,
{ name: string; registry: any }
>([], {
toObj: (t) => {
return { name: t.name, registry: t.serialize() };
},
Expand Down Expand Up @@ -78,20 +81,17 @@ export class SendingAccountRegistry extends Registry<SendingAccount> {
const account = this.mustGet(accountName);
if (isSingleSigAccount(account) && isPrivateKey(account)) {
return account.privateKey;
} else if (
isInitializedMultisig(account) ||
isUninitializedMultisig(account)
) {
} else if (isMultisig(account)) {
return account.wallet.privateKey;
}
throw new Error(
`Can't find private key for ${accountName} in this registry`
);
};

// Connect all accounts to the provider
public connectProviderAccounts = (rpc: string) => {
const provider = ethers.getDefaultProvider(rpc);
// const newAccounts = this.subset([]);
for (const [name, account] of this.entries()) {
if (isMultisig(account)) {
const newMultisigWallet = {
Expand All @@ -107,21 +107,29 @@ export class SendingAccountRegistry extends Registry<SendingAccount> {
};
}

// Load a map of evm accounts from a config through connecting wallets, can either take in sending accounts or not
// Load a map of evm accounts from a config through connecting wallets, can either take in sending accounts a single sig account
// This will convert either from MultisigAccountConfig -> MultisigAccount or SingleSigAccountConfig -> Wallet
export function loadSendingAccounts(config: unknown): Registry<SendingAccount> {
if (!isEvmAccountsConfig(config)) {
throw new Error(`Error parsing schema: ${config}`);
throw new Error(
`Error parsing schema: ${config} \n ${
EvmAccountsConfig.safeParse(config).error
}`
);
}

const walletMap = new Registry<SendingAccount>([]);

if (isEvmAccounts(config)) {
for (const account of config) {
if (isMultisigConfig(account)) {
const wallet = createWallet(account.signer);
const multisigAccount: InitializedMultisig = {
const wallet = createWallet({
name: account.name,
privateKey: account.privateKey,
});
const multisigAccount: InitializedMultisig | UninitializedMultisig = {
...account,
wallet: wallet,
wallet,
};
walletMap.set(account.name, multisigAccount);
} else if (isSingleSigAccount(account)) {
Expand Down
2 changes: 0 additions & 2 deletions src/multisig/safe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ export const newSafeFromOwner = async (
owners: string[],
threshold: number
) => {
// TODO: check owners is indeed an array and not a string (for edge case of one address)

const safeFactory = await SafeFactory.init({
provider: RPC_URL,
signer: ownerKey,
Expand Down
35 changes: 25 additions & 10 deletions src/scripts/deploy-multisig.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,61 @@
#!/usr/bin/env node
import { ethers } from "ethers";
import { ethers, toBigInt } from "ethers";
import { SingleSigAccountRegistry, parseObjFromFile } from "..";
import { newSafeFromOwner } from "../multisig/safe";

import {
parseMultisigInitArgsFromCLI,
saveMultisigAddressToAccountsSpec,
} from "../utils/io";
import { SendingAccountRegistry } from "../evm/schemas/sendingAccount";
import { isUninitializedMultisig } from "../evm/schemas/multisig";

async function main() {
const { rpcUrl, owners, initiator, accountsSpecPath, threshold } =
const { rpcUrl, initiator, accountsSpecPath, chainId } =
await parseMultisigInitArgsFromCLI();

const accountConfigFromYaml = {
name: "multisig-accounts",
registry: parseObjFromFile(accountsSpecPath),
};

const accounts = SingleSigAccountRegistry.loadMultiple([
const accounts = SendingAccountRegistry.loadMultiple([
accountConfigFromYaml,
]).mustGet("multisig-accounts");

const multisigAccount = accounts.mustGet(initiator);

if (!isUninitializedMultisig(multisigAccount)) {
throw new Error(
"Account read from yaml but isn't a multisig account that needs to be initialized."
);
}

const senderPrivateKey = accounts.getSinglePrivateKeyFromAccount(initiator);
if (!senderPrivateKey) {
throw new Error(`Could not find private key for owner ${initiator}`);
}

const provider = new ethers.JsonRpcProvider(rpcUrl);
const providerChainId = (await provider.getNetwork()).chainId;
if (!providerChainId || providerChainId !== toBigInt(chainId)) {
throw new Error(
`Chain id mismatch between multisig account and rpc url. ${chainId} is specified in accounts spec, but ${providerChainId} is the chain id of the rpc url`
);
}

const newSafeAddress = await newSafeFromOwner(
rpcUrl,
senderPrivateKey,
owners,
threshold
multisigAccount.owners,
multisigAccount.threshold
);

const provider = new ethers.JsonRpcProvider(rpcUrl);
const chainId = (await provider.getNetwork()).chainId;

await saveMultisigAddressToAccountsSpec(
newSafeAddress,
accountsSpecPath,
initiator,
chainId
chainId,
initiator
);
}

Expand Down
15 changes: 7 additions & 8 deletions src/scripts/execute-multisig-tx.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
#!/usr/bin/env node
import { AccountRegistry, parseObjFromFile } from "..";
import { executeMultisigTx} from "../multisig/safe";
import { parseObjFromFile } from "..";
import { executeMultisigTx } from "../multisig/safe";

import {
parseExecuteMultisigTxArgsFromCLI,
} from "../utils/io";
import { isParsedMultiSigWallet } from "../evm/schemas/account";
import { parseExecuteMultisigTxArgsFromCLI } from "../utils/io";
import { isInitializedMultisig } from "../evm/schemas/multisig";
import { SendingAccountRegistry } from "../evm/schemas/sendingAccount";

async function main() {
const { executor, rpcUrl, txIndex, accountsSpecPath } =
await parseExecuteMultisigTxArgsFromCLI();

const accounts = AccountRegistry.load(
const accounts = SendingAccountRegistry.load(
parseObjFromFile(accountsSpecPath),
"multisig-accounts"
);

const multisigAccount = accounts.mustGet(executor);
if (!isParsedMultiSigWallet(multisigAccount)) {
if (!isInitializedMultisig(multisigAccount)) {
throw new Error("Can only execute transactions on a multisig wallet");
}

Expand Down
4 changes: 2 additions & 2 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ export const UPDATE_SPECS_PATH = process.env.UPDATE_SPECS_PATH
? process.env.UPDATE_SPECS_PATH
: path.resolve(SPECS_BASE_PATH, "update.spec.yaml");

export const ACCOUNTS_SPECS_PATH = process.env.ACCOUNTS_SPECS_PATH
? process.env.ACCOUNTS_SPECS_PATH
export const ACCOUNT_SPECS_PATH = process.env.ACCOUNT_SPECS_PATH
? process.env.ACCOUNT_SPECS_PATH
: path.resolve(SPECS_BASE_PATH, "evm.accounts.yaml");

export const EXTRA_BINDINGS_PATH = process.env.EXTRA_BINDINGS_PATH;
Expand Down
Loading

0 comments on commit 0a562c4

Please sign in to comment.