Skip to content

Commit 0a562c4

Browse files
committed
update deploy multisig bin to read from accounts spec
1 parent 8fd1743 commit 0a562c4

File tree

9 files changed

+140
-103
lines changed

9 files changed

+140
-103
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@open-ibc/vibc-core-smart-contracts",
3-
"version": "4.0.6",
3+
"version": "4.0.7",
44
"main": "dist/index.js",
55
"bin": {
66
"verify-vibc-core-smart-contracts": "./dist/scripts/verify-contract-script.js",

src/evm/schemas/account.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import fs from "fs";
44
import path from "path";
55
import { Registry } from "../../utils/registry";
66
import { renderString } from "../../utils/io";
7-
import { initializedMultisig } from "./multisig";
7+
import { initializedMultisigConfig, uninitializedMultisigConfig } from "./multisig";
88
import {
99
isMnemonic,
1010
isPrivateKey,
@@ -24,7 +24,7 @@ const keyStore = z.object({
2424
export type KeyStore = z.infer<typeof keyStore>;
2525

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

src/evm/schemas/multisig.ts

+50-30
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,50 @@
11
import { z } from 'zod';
2-
import {singleSigAccount, wallet} from './wallet';
2+
import { wallet } from './wallet';
33

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

1214
// Defined in an account spec, which is not necessarily in a config
13-
export const initializedMultisigConfig = z.object({
14-
name: z.string().min(1),
15-
chainId: z.number(),
16-
safeAddress: z.string().min(1),
17-
signer: singleSigAccount
18-
}) .strict()
19-
15+
export const initializedMultisigConfig = z
16+
.object({
17+
name: z.string().min(1),
18+
chainId: z.number(),
19+
privateKey: z.string().min(1),
20+
safeAddress: z.string().min(1),
21+
})
22+
.strict();
2023

2124
// Multisig which is described in an account spec but is not yet initialized. (i.e. multisig contract has not been deployed yet)
22-
export const unInitializedMultisig = z.intersection(
23-
uninitializedMultisigConfig,
24-
z.object({wallet: wallet})
25-
)
25+
export const unInitializedMultisig = z.object({
26+
name: z.string().min(1),
27+
privateKey: z.string().min(1),
28+
owners: z.array(z.string().min(1)),
29+
threshold: z.number(),
30+
wallet: wallet,
31+
});
2632

2733
// Multisig which has been deployed & can be used to propose transactions. This is the type that loadEvmAccounts will return for multisig types
28-
export const initializedMultisig = z.intersection(
29-
initializedMultisigConfig,
30-
z.object({wallet: wallet})
31-
);
34+
export const initializedMultisig = z.object({
35+
name: z.string().min(1),
36+
chainId: z.number(),
37+
privateKey: z.string().min(1),
38+
safeAddress: z.string().min(1),
39+
wallet: wallet,
40+
});
3241

33-
export type UninitializedMultisigConfig = z.infer<typeof uninitializedMultisigConfig>;
34-
export type InitializedMultisigConfig = z.infer<typeof initializedMultisigConfig>;
42+
export type UninitializedMultisigConfig = z.infer<
43+
typeof uninitializedMultisigConfig
44+
>;
45+
export type InitializedMultisigConfig = z.infer<
46+
typeof initializedMultisigConfig
47+
>;
3548
export type UninitializedMultisig = z.infer<typeof unInitializedMultisig>;
3649
export type InitializedMultisig = z.infer<typeof initializedMultisig>;
3750

@@ -41,7 +54,7 @@ export const isUninitializedMultisigConfig = (
4154
): account is UninitializedMultisigConfig => {
4255
return uninitializedMultisigConfig.safeParse(account).success;
4356
};
44-
57+
4558
export const isUninitializedMultisig = (
4659
account: unknown
4760
): account is UninitializedMultisig => {
@@ -60,10 +73,17 @@ export const isInitializedMultisig = (
6073
return initializedMultisig.safeParse(account).success;
6174
};
6275

63-
export const isMultisig = (account: unknown): account is InitializedMultisig | UninitializedMultisig => {
76+
export const isMultisig = (
77+
account: unknown
78+
): account is InitializedMultisig | UninitializedMultisig => {
6479
return isInitializedMultisig(account) || isUninitializedMultisig(account);
6580
};
6681

67-
export const isMultisigConfig = (account: unknown): account is InitializedMultisigConfig | UninitializedMultisigConfig => {
68-
return isInitializedMultisigConfig(account) || isUninitializedMultisigConfig(account);
69-
}
82+
export const isMultisigConfig = (
83+
account: unknown
84+
): account is InitializedMultisigConfig | UninitializedMultisigConfig => {
85+
return (
86+
isInitializedMultisigConfig(account) ||
87+
isUninitializedMultisigConfig(account)
88+
);
89+
};

src/evm/schemas/sendingAccount.ts

+21-13
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ import { fs } from "zx";
22
import { Registry } from "../../utils/registry";
33
import {
44
createWallet,
5+
EvmAccountsConfig,
56
isEvmAccounts,
67
isEvmAccountsConfig,
78
isKeyStore,
89
} from "./account";
910
import { isPrivateKey, isSingleSigAccount, Wallet } from "./wallet";
1011
import {
1112
InitializedMultisig,
12-
isInitializedMultisig,
1313
isMultisig,
1414
isMultisigConfig,
15-
isUninitializedMultisig,
15+
unInitializedMultisig,
1616
UninitializedMultisig,
1717
} from "./multisig";
1818
import path from "path";
@@ -33,7 +33,10 @@ export class SendingAccountRegistry extends Registry<SendingAccount> {
3333
}
3434

3535
static loadMultiple(registryItems: { name: string; registry: any }[]) {
36-
const result = new Registry([] as SendingAccountRegistry[], {
36+
const result = new Registry<
37+
SendingAccountRegistry,
38+
{ name: string; registry: any }
39+
>([], {
3740
toObj: (t) => {
3841
return { name: t.name, registry: t.serialize() };
3942
},
@@ -78,20 +81,17 @@ export class SendingAccountRegistry extends Registry<SendingAccount> {
7881
const account = this.mustGet(accountName);
7982
if (isSingleSigAccount(account) && isPrivateKey(account)) {
8083
return account.privateKey;
81-
} else if (
82-
isInitializedMultisig(account) ||
83-
isUninitializedMultisig(account)
84-
) {
84+
} else if (isMultisig(account)) {
8585
return account.wallet.privateKey;
8686
}
8787
throw new Error(
8888
`Can't find private key for ${accountName} in this registry`
8989
);
9090
};
91+
9192
// Connect all accounts to the provider
9293
public connectProviderAccounts = (rpc: string) => {
9394
const provider = ethers.getDefaultProvider(rpc);
94-
// const newAccounts = this.subset([]);
9595
for (const [name, account] of this.entries()) {
9696
if (isMultisig(account)) {
9797
const newMultisigWallet = {
@@ -107,21 +107,29 @@ export class SendingAccountRegistry extends Registry<SendingAccount> {
107107
};
108108
}
109109

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

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

118123
if (isEvmAccounts(config)) {
119124
for (const account of config) {
120125
if (isMultisigConfig(account)) {
121-
const wallet = createWallet(account.signer);
122-
const multisigAccount: InitializedMultisig = {
126+
const wallet = createWallet({
127+
name: account.name,
128+
privateKey: account.privateKey,
129+
});
130+
const multisigAccount: InitializedMultisig | UninitializedMultisig = {
123131
...account,
124-
wallet: wallet,
132+
wallet,
125133
};
126134
walletMap.set(account.name, multisigAccount);
127135
} else if (isSingleSigAccount(account)) {

src/multisig/safe.ts

-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ export const newSafeFromOwner = async (
1717
owners: string[],
1818
threshold: number
1919
) => {
20-
// TODO: check owners is indeed an array and not a string (for edge case of one address)
21-
2220
const safeFactory = await SafeFactory.init({
2321
provider: RPC_URL,
2422
signer: ownerKey,

src/scripts/deploy-multisig.ts

+25-10
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,61 @@
11
#!/usr/bin/env node
2-
import { ethers } from "ethers";
2+
import { ethers, toBigInt } from "ethers";
33
import { SingleSigAccountRegistry, parseObjFromFile } from "..";
44
import { newSafeFromOwner } from "../multisig/safe";
55

66
import {
77
parseMultisigInitArgsFromCLI,
88
saveMultisigAddressToAccountsSpec,
99
} from "../utils/io";
10+
import { SendingAccountRegistry } from "../evm/schemas/sendingAccount";
11+
import { isUninitializedMultisig } from "../evm/schemas/multisig";
1012

1113
async function main() {
12-
const { rpcUrl, owners, initiator, accountsSpecPath, threshold } =
14+
const { rpcUrl, initiator, accountsSpecPath, chainId } =
1315
await parseMultisigInitArgsFromCLI();
1416

1517
const accountConfigFromYaml = {
1618
name: "multisig-accounts",
1719
registry: parseObjFromFile(accountsSpecPath),
1820
};
1921

20-
const accounts = SingleSigAccountRegistry.loadMultiple([
22+
const accounts = SendingAccountRegistry.loadMultiple([
2123
accountConfigFromYaml,
2224
]).mustGet("multisig-accounts");
2325

26+
const multisigAccount = accounts.mustGet(initiator);
27+
28+
if (!isUninitializedMultisig(multisigAccount)) {
29+
throw new Error(
30+
"Account read from yaml but isn't a multisig account that needs to be initialized."
31+
);
32+
}
33+
2434
const senderPrivateKey = accounts.getSinglePrivateKeyFromAccount(initiator);
2535
if (!senderPrivateKey) {
2636
throw new Error(`Could not find private key for owner ${initiator}`);
2737
}
2838

39+
const provider = new ethers.JsonRpcProvider(rpcUrl);
40+
const providerChainId = (await provider.getNetwork()).chainId;
41+
if (!providerChainId || providerChainId !== toBigInt(chainId)) {
42+
throw new Error(
43+
`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`
44+
);
45+
}
46+
2947
const newSafeAddress = await newSafeFromOwner(
3048
rpcUrl,
3149
senderPrivateKey,
32-
owners,
33-
threshold
50+
multisigAccount.owners,
51+
multisigAccount.threshold
3452
);
3553

36-
const provider = new ethers.JsonRpcProvider(rpcUrl);
37-
const chainId = (await provider.getNetwork()).chainId;
38-
3954
await saveMultisigAddressToAccountsSpec(
4055
newSafeAddress,
4156
accountsSpecPath,
42-
initiator,
43-
chainId
57+
chainId,
58+
initiator
4459
);
4560
}
4661

src/scripts/execute-multisig-tx.ts

+7-8
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
#!/usr/bin/env node
2-
import { AccountRegistry, parseObjFromFile } from "..";
3-
import { executeMultisigTx} from "../multisig/safe";
2+
import { parseObjFromFile } from "..";
3+
import { executeMultisigTx } from "../multisig/safe";
44

5-
import {
6-
parseExecuteMultisigTxArgsFromCLI,
7-
} from "../utils/io";
8-
import { isParsedMultiSigWallet } from "../evm/schemas/account";
5+
import { parseExecuteMultisigTxArgsFromCLI } from "../utils/io";
6+
import { isInitializedMultisig } from "../evm/schemas/multisig";
7+
import { SendingAccountRegistry } from "../evm/schemas/sendingAccount";
98

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

14-
const accounts = AccountRegistry.load(
13+
const accounts = SendingAccountRegistry.load(
1514
parseObjFromFile(accountsSpecPath),
1615
"multisig-accounts"
1716
);
1817

1918
const multisigAccount = accounts.mustGet(executor);
20-
if (!isParsedMultiSigWallet(multisigAccount)) {
19+
if (!isInitializedMultisig(multisigAccount)) {
2120
throw new Error("Can only execute transactions on a multisig wallet");
2221
}
2322

src/utils/constants.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ export const UPDATE_SPECS_PATH = process.env.UPDATE_SPECS_PATH
4444
? process.env.UPDATE_SPECS_PATH
4545
: path.resolve(SPECS_BASE_PATH, "update.spec.yaml");
4646

47-
export const ACCOUNTS_SPECS_PATH = process.env.ACCOUNTS_SPECS_PATH
48-
? process.env.ACCOUNTS_SPECS_PATH
47+
export const ACCOUNT_SPECS_PATH = process.env.ACCOUNT_SPECS_PATH
48+
? process.env.ACCOUNT_SPECS_PATH
4949
: path.resolve(SPECS_BASE_PATH, "evm.accounts.yaml");
5050

5151
export const EXTRA_BINDINGS_PATH = process.env.EXTRA_BINDINGS_PATH;

0 commit comments

Comments
 (0)