Skip to content

[contract-manager] Evm upgrades #971

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Jul 25, 2023
Merged

[contract-manager] Evm upgrades #971

merged 23 commits into from
Jul 25, 2023

Conversation

m30m
Copy link
Contributor

@m30m m30m commented Jul 21, 2023

No description provided.

@vercel
Copy link

vercel bot commented Jul 21, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

2 Ignored Deployments
Name Status Preview Comments Updated (UTC)
example-oracle-amm ⬜️ Ignored (Inspect) Jul 25, 2023 6:12am
xc-admin-frontend ⬜️ Ignored (Inspect) Jul 25, 2023 6:12am

@m30m m30m changed the title Evm upgrades [contract-manager] Evm upgrades Jul 21, 2023
Comment on lines +23 to +34
export interface Price {
price: string;
conf: string;
publishTime: string;
expo: string;
}

export interface PriceFeed {
price: Price;
emaPrice: Price;
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, we have a PriceFeed definition in price-service-sdk. I think it's good to have it as it is now and don't using it. Just saying it for awareness.

/**
* Executes the governance instruction contained in the VAA using the sender credentials
* @param sender based on the contract type, this can be a private key, a mnemonic, a wallet, etc.
* @param senderPrivateKey private key of the sender in hex format without 0x prefix
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpik: Maybe we can also make this a buffer? In some places i see uint8 array as private key and Buffer seems to a more flexible choice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far, I'm seeing hex format as the more common input format. I think if the implementation needs the uint8 array or Buffer it can easily convert the privateKey via Buffer.from

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(i think a class is nice for stuff like this because it lets you write methods on the class itself for getting it in hex or other formats. not a big deal though)

}

getRpcUrl(): string {
if (this.rpcUrl.includes("$INFURA_KEY") && !process.env.INFURA_KEY)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpik: maybe it's better to do this check on the constructor

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it. If other packages start to use this one, they would all need the INFURA_KEY env variable even if they just want to use something general or not related to evm. That's why I put it here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with ali on this, though also understand your point. Ideally we would have a way to only instantiate the chains the user requests, such that we can perform validity checks on instantiation. Not sure if that's easy to do though.

at the very least, this seems worth a code comment explaining why it's like this and what it would take to improve the situation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to replace the current RPC endpoints with the public ones as the simplest solution and remove this logic😆

@@ -206,26 +236,30 @@ export class WormholeMultiSigTransaction {

async getState() {
const proposal = await this.squad.getTransaction(this.address);
return proposal.status; //TODO: make it typed and parse the status to a more readable format
return Object.keys(proposal.status)[0];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JS 🪄 🧙

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some comments

Comment on lines 165 to 166
contract.options.data = bytecode;
const deployTx = contract.deploy({ data: "" });
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we couldn't pass bytecode as data 🤔 can you comment that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will try that 👍 This was a direct copy-paste from some example contract deployment code on the web

const gas = await deployTx.estimateGas();
let gasPrice = await web3.eth.getGasPrice();

if (!chain.isMainnet()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you comment why we do this?

return transactionObject.send({
from: address,
value: updateFee,
gas: gasEstiamte * 2,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment on documenting why we do these.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a dirty attempt to eliminate any need for manually specifying gas and gasPrice values per chain. For the gasPrice I was not sure how costly would it be to double the value on mainnet chains, so I only do it for testnet chains for now. If this is not an issue, I can do it everywhere and we don't need to worry a lot about manual entry of these values.
For the gas value, the * 2 is just a very loose upper bound of how much gas is expected to be used.

mnemonic,
{ prefix }
);
await directSecp256k1HdWallet.getAccounts();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this call is needed? Can you document it? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the responsibility of this module? Is it the controller? If so is this name the best name? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the first (and only file in the beginning) on this package. I'll organize it soon

Copy link
Collaborator

@ali-behjati ali-behjati left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic! I left some minor inline comments.

I need to look at the codebase entirely to understand all the design decisions and grasp how different components relate to each other (and can be used) but this PR is very solid.

I also recommend moving chains definitions to a sub-module like this:

|- chains
  |- index.ts (or chains.ts on the folder outside)
  |- aptos.ts
  |- evm.ts
  |- ...
|- store.ts
|- entitities.ts

Copy link
Collaborator

@ali-behjati ali-behjati left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚢 it

Copy link
Contributor

@jayantk jayantk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very cool. I left a bunch of comments but nothing major.

/**
* Executes the governance instruction contained in the VAA using the sender credentials
* @param sender based on the contract type, this can be a private key, a mnemonic, a wallet, etc.
* @param senderPrivateKey private key of the sender in hex format without 0x prefix
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(i think a class is nice for stuff like this because it lets you write methods on the class itself for getting it in hex or other formats. not a big deal though)


protected constructor(
public id: string,
public mainnet: boolean,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should these be private?

protected constructor(
public id: string,
public mainnet: boolean,
wormholeChainName: string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest explaining the difference between id and wormholeChainName in a doc comment.

and that this is pyth's wormholeChainName, not wormhole's

export class GlobalChain extends Chain {
static type: string = "GlobalChain";
constructor() {
super("global", true, "unset");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically there are two global chains, one for mainnet and one for testnet right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, it is one global chain x 2 multisig emitters. testnet contracts are listening to one emitter while mainnet contracts are listening to the other one.

};
}

async getPriceFeed(feedId: string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please annotate the return type on all public methods

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, I didn't annotate the implementations or write any docs if the abstract parent function was annotated. I thought most IDEs are smart enough to show relevant information like this:
image
If that's not the case I can copy paste it in the implementations too.

* @param senderPrivateKey the private key to execute the governance instruction with
* @param vaa the VAA to execute
*/
export async function executeVaa(senderPrivateKey: string, vaa: Buffer) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is going to be great

);
} catch (e: any) {
if (!(e instanceof InvalidTransactionError)) throw e;
}
}
if (msgs.length > 0) return msgs;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you sure about this check? seems like if none of the txs sent wormhole messages, then the proposal executes successfully and returns 0 messages

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, this class was mainly designed for Wormhole messages, so I thought it should throw an exception if there is actually no wormhole instructions inside. I can add a check in constructor to verify this fact, but if someone is manually creating this object, hopefully they know what they are doing. I will add some comments. In the end, I think the main usage of this class will be in manual/dev testing as a cranker would do this automatically on our pre-configured vaults.

@@ -113,6 +113,8 @@ export class PythGovernanceHeader {
module = MODULE_TARGET;
action = TargetAction[this.action as keyof typeof TargetAction];
}
if (toChainId(this.targetChainId) === undefined)
throw new Error(`Invalid chain id ${this.targetChainId}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw we should make validity-checked types for all these things so that we don't need to put checks like this everywhere. ChainName shouldn't be able to represent an invalid chain.

mainnet: false
rpcUrl: https://testnet.aurora.dev
networkId: 1313161555
type: EVMChain
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the ship may have sailed on this but i think this should be capitalized like EvmChain. I generally prefer to treat acronyms as a "word" for the purpose of camelcasing -- there's a whole discussion of this in the google coding conventions which i thought was persuasive.

definitely not important though

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you mentioned this before and I thought I fixed it everywhere. I missed this one, will make the changes 👍

}

getRpcUrl(): string {
if (this.rpcUrl.includes("$INFURA_KEY") && !process.env.INFURA_KEY)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with ali on this, though also understand your point. Ideally we would have a way to only instantiate the chains the user requests, such that we can perform validity checks on instantiation. Not sure if that's easy to do though.

at the very least, this seems worth a code comment explaining why it's like this and what it would take to improve the situation.

@m30m
Copy link
Contributor Author

m30m commented Jul 25, 2023

Thanks for all the comments, some more cleanups and changes are definitely still remaining to do and will be done in followup PRs.

@m30m m30m merged commit 31ad2b6 into main Jul 25, 2023
@m30m m30m deleted the evm-upgrades branch July 25, 2023 06:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants