Skip to content

Commit 1b8fee9

Browse files
authored
[contract_manager] Add some nice-to-have features (#1650)
* gr * some stuff * comment * gr * cleanup
1 parent 046ba8a commit 1b8fee9

File tree

4 files changed

+83
-32
lines changed

4 files changed

+83
-32
lines changed

contract_manager/scripts/check_proposal.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ async function main() {
140140
if (instruction.governanceAction instanceof EvmExecute) {
141141
// Note: it only checks for upgrade entropy contracts right now
142142
console.log(
143-
`Verifying EVMExecute Contract on ${instruction.governanceAction.targetChainId}`
143+
`Verifying EVMExecute on ${instruction.governanceAction.targetChainId}`
144144
);
145145
for (const chain of Object.values(DefaultStore.chains)) {
146146
if (
@@ -153,7 +153,8 @@ async function main() {
153153
const callAddress = instruction.governanceAction.callAddress;
154154
const calldata = instruction.governanceAction.calldata;
155155

156-
// currently executor is only being used by the entropy contract
156+
// TODO: If we add additional EVM contracts using the executor, we need to
157+
// add some logic here to identify what kind of contract is at the call address.
157158
const contract = new EvmEntropyContract(chain, callAddress);
158159
const owner = await contract.getOwner();
159160

@@ -169,10 +170,13 @@ async function main() {
169170
continue;
170171
}
171172

173+
// TODO: This logic assumes we are calling upgradeTo on the contract at callAddress.
174+
// In the future, this logic may need to be generalized to support calling other functions.
175+
const invokedMethod = "upgradeTo(address)";
172176
const calldataHex = calldata.toString("hex");
173177
const web3 = new Web3();
174178
const methodSignature = web3.eth.abi
175-
.encodeFunctionSignature("upgradeTo(address)")
179+
.encodeFunctionSignature(invokedMethod)
176180
.replace("0x", "");
177181

178182
let newImplementationAddress: string | undefined = undefined;
@@ -196,7 +200,10 @@ async function main() {
196200
);
197201
// this should be the same keccak256 of the deployedCode property generated by truffle
198202
console.log(
199-
`${chain.getId()} new implementation address:${newImplementationAddress} digest:${newImplementationCode}`
203+
`${chain.getId()} call ${invokedMethod} with arguments (${newImplementationAddress}) on ${contract.getType()} at address:${callAddress} from executor:${executorAddress}.`
204+
);
205+
console.log(
206+
`${chain.getId()} new implementation address:${newImplementationAddress} has code digest:${newImplementationCode}`
200207
);
201208
}
202209
}

contract_manager/scripts/upgrade_evm_entropy_contracts.ts

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import yargs from "yargs";
22
import { hideBin } from "yargs/helpers";
33
import { DefaultStore, loadHotWallet, toPrivateKey } from "../src";
44
import { readFileSync } from "fs";
5+
import { PythCluster } from "@pythnetwork/client/lib/cluster";
56

67
import {
78
COMMON_UPGRADE_OPTIONS,
@@ -27,6 +28,18 @@ const parser = yargs(hideBin(process.argv))
2728
},
2829
});
2930

31+
// Override these URLs to use a different RPC node for mainnet / testnet.
32+
// TODO: extract these RPCs to a config file (?)
33+
const RPCS = {
34+
"mainnet-beta": "https://api.mainnet-beta.solana.com",
35+
testnet: "https://api.testnet.solana.com",
36+
devnet: "https://api.devnet.solana.com",
37+
} as Record<PythCluster, string>;
38+
39+
function registry(cluster: PythCluster): string {
40+
return RPCS[cluster];
41+
}
42+
3043
async function main() {
3144
const argv = await parser.argv;
3245
const cacheFile =
@@ -45,40 +58,56 @@ async function main() {
4558

4659
console.log("Using cache file", cacheFile);
4760

61+
// Try to deploy on every chain, then collect any failures at the end. This logic makes it simpler to
62+
// identify deployment problems (e.g., not enough gas) on every chain where they occur.
4863
const payloads: Buffer[] = [];
64+
const failures: string[] = [];
4965
for (const contract of Object.values(DefaultStore.entropy_contracts)) {
5066
if (selectedChains.includes(contract.chain)) {
5167
const artifact = JSON.parse(readFileSync(argv["std-output"], "utf8"));
5268
console.log("Deploying contract to", contract.chain.getId());
53-
const address = await runIfNotCached(
54-
`deploy-${contract.chain.getId()}`,
55-
() => {
56-
return contract.chain.deploy(
57-
toPrivateKey(argv["private-key"]),
58-
artifact["abi"],
59-
artifact["bytecode"],
60-
[],
61-
2
62-
);
63-
}
64-
);
65-
console.log(
66-
`Deployed contract at ${address} on ${contract.chain.getId()}`
67-
);
68-
const payload =
69-
argv["contract-type"] === "executor"
70-
? await contract.generateUpgradeExecutorContractsPayload(address)
71-
: await contract.generateUpgradeEntropyContractPayload(address);
69+
try {
70+
const address = await runIfNotCached(
71+
`deploy-${contract.chain.getId()}`,
72+
() => {
73+
return contract.chain.deploy(
74+
toPrivateKey(argv["private-key"]),
75+
artifact["abi"],
76+
artifact["bytecode"],
77+
[],
78+
2
79+
);
80+
}
81+
);
82+
console.log(
83+
`Deployed contract at ${address} on ${contract.chain.getId()}`
84+
);
85+
const payload =
86+
argv["contract-type"] === "executor"
87+
? await contract.generateUpgradeExecutorContractsPayload(address)
88+
: await contract.generateUpgradeEntropyContractPayload(address);
7289

73-
console.log(payload.toString("hex"));
74-
payloads.push(payload);
90+
console.log(payload.toString("hex"));
91+
payloads.push(payload);
92+
} catch (e) {
93+
console.log(`error deploying: ${e}`);
94+
failures.push(contract.chain.getId());
95+
}
7596
}
7697
}
7798

99+
if (failures.length > 0) {
100+
throw new Error(
101+
`Some chains could not be deployed: ${failures.join(
102+
", "
103+
)}. Scroll up to see the errors from each chain.`
104+
);
105+
}
106+
78107
console.log("Using vault at for proposal", vault.getId());
79108
const wallet = await loadHotWallet(argv["ops-key-path"]);
80109
console.log("Using wallet ", wallet.publicKey.toBase58());
81-
await vault.connect(wallet);
110+
vault.connect(wallet, registry);
82111
const proposal = await vault.proposeWormholeMessage(payloads);
83112
console.log("Proposal address", proposal.address.toBase58());
84113
}

contract_manager/src/chains.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -447,12 +447,19 @@ export class EvmChain extends Chain {
447447
);
448448
}
449449

450-
const deployedContract = await deployTx.send({
451-
from: signer.address,
452-
gas,
453-
gasPrice: gasPrice.toString(),
454-
});
455-
return deployedContract.options.address;
450+
try {
451+
const deployedContract = await deployTx.send({
452+
from: signer.address,
453+
gas,
454+
gasPrice: gasPrice.toString(),
455+
});
456+
return deployedContract.options.address;
457+
} catch (e) {
458+
// RPC errors often have useful information in the non-primary message field. Log the whole error
459+
// to simplify identifying the problem.
460+
console.log(`Error deploying contract: ${JSON.stringify(e)}`);
461+
throw e;
462+
}
456463
}
457464

458465
async getAccountAddress(privateKey: PrivateKey): Promise<string> {

contract_manager/src/governance.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export class SubmittedWormholeMessage {
5858
* Inspects the transaction logs to find the sequence number
5959
* @param signature signature of the transaction to inspect
6060
* @param cluster the cluster the transaction was submitted to
61+
* @param registry registry of RPC nodes to use for each solana network. Defaults to the Solana public RPCs if not provided.
6162
*/
6263
static async fromTransactionSignature(
6364
signature: string,
@@ -152,6 +153,11 @@ export class WormholeEmitter {
152153
return this.wallet.publicKey;
153154
}
154155

156+
/**
157+
* Send a wormhole message containing payload through wormhole.
158+
* @param payload the contents of the message
159+
* @param registry registry of RPC nodes to use for each solana network. Defaults to the Solana public RPCs if not provided.
160+
*/
155161
async sendMessage(
156162
payload: Buffer,
157163
registry: SolanaRpcRegistry = getPythClusterApiUrl
@@ -299,6 +305,7 @@ export class Vault extends Storable {
299305
* Connects the vault to a wallet that can be used to submit proposals
300306
* The wallet should be a multisig signer of the vault
301307
* @param wallet
308+
* @param registry registry of RPC nodes to use for each solana network. Defaults to the Solana public RPCs if not provided.
302309
*/
303310
public connect(
304311
wallet: Wallet,
@@ -314,6 +321,7 @@ export class Vault extends Storable {
314321

315322
/**
316323
* Gets the emitter address of the vault
324+
* @param registry registry of RPC nodes to use for each solana network. Defaults to the Solana public RPCs if not provided.
317325
*/
318326
public async getEmitter(registry: SolanaRpcRegistry = getPythClusterApiUrl) {
319327
const squad = SquadsMesh.endpoint(

0 commit comments

Comments
 (0)