浏览代码

refactor(contract_manager): add docs to ton upgrade + use parseUrl (#2160)

Ali Behjati 11 月之前
父节点
当前提交
f410820759

+ 6 - 7
contract_manager/scripts/upgrade_ton_contract.ts

@@ -5,18 +5,17 @@ import fs from "fs";
 import path from "path";
 import { Cell } from "@ton/ton";
 
+// This script upgrades the Pyth contract on TON after the governance has authorized the upgrade
+// If you are starting over, the process is like the following:
+// 1. create a governance proposal using generate_upgrade_ton_contract_proposal script
+// 2. once approved and executed, relay it to TON using sync_governance_vaas script
+// 3. upgrade the contract on TON using this script
 const parser = yargs(hideBin(process.argv))
   .usage(
     "Upgrades the Pyth contract on TON and creates a governance proposal for it.\n" +
-      "Usage: $0 --network <mainnet|testnet> --contract <contract_name> --private-key <private_key>"
+      "Usage: $0 --contract <contract_name> --private-key <private_key>"
   )
   .options({
-    network: {
-      type: "string",
-      choices: ["mainnet", "testnet"],
-      description: "Network to deploy to",
-      demandOption: true,
-    },
     contract: {
       type: "string",
       description: "Contract name",

+ 34 - 23
contract_manager/src/chains.ts

@@ -37,6 +37,26 @@ import {
 import { keyPairFromSeed } from "@ton/crypto";
 import { PythContract } from "@pythnetwork/pyth-ton-js";
 
+/**
+ * Returns the chain rpc url with any environment variables replaced or throws an error if any are missing
+ */
+export function parseRpcUrl(rpcUrl: string): string {
+  const envMatches = rpcUrl.match(/\$ENV_\w+/);
+  if (envMatches) {
+    for (const envMatch of envMatches) {
+      const envName = envMatch.replace("$ENV_", "");
+      const envValue = process.env[envName];
+      if (!envValue) {
+        throw new Error(
+          `Missing env variable ${envName} required for this RPC: ${rpcUrl}`
+        );
+      }
+      rpcUrl = rpcUrl.replace(envMatch, envValue);
+    }
+  }
+  return rpcUrl;
+}
+
 export type ChainConfig = Record<string, string> & {
   mainnet: boolean;
   id: string;
@@ -352,23 +372,10 @@ export class EvmChain extends Chain {
   }
 
   /**
-   * Returns the chain rpc url with any environment variables replaced or throws an error if any are missing
+   * Returns a web3 provider for this chain
    */
-  getRpcUrl(): string {
-    const envMatches = this.rpcUrl.match(/\$ENV_\w+/);
-    if (envMatches) {
-      for (const envMatch of envMatches) {
-        const envName = envMatch.replace("$ENV_", "");
-        const envValue = process.env[envName];
-        if (!envValue) {
-          throw new Error(
-            `Missing env variable ${envName} required for chain ${this.id} rpc: ${this.rpcUrl}`
-          );
-        }
-        this.rpcUrl = this.rpcUrl.replace(envMatch, envValue);
-      }
-    }
-    return this.rpcUrl;
+  getWeb3(): Web3 {
+    return new Web3(parseRpcUrl(this.rpcUrl));
   }
 
   /**
@@ -419,7 +426,7 @@ export class EvmChain extends Chain {
   }
 
   async getGasPrice() {
-    const web3 = new Web3(this.getRpcUrl());
+    const web3 = this.getWeb3();
     let gasPrice = await web3.eth.getGasPrice();
     // some testnets have inaccuarte gas prices that leads to transactions not being mined, we double it since it's free!
     if (!this.isMainnet()) {
@@ -459,7 +466,7 @@ export class EvmChain extends Chain {
     gasMultiplier = 1,
     gasPriceMultiplier = 1
   ): Promise<string> {
-    const web3 = new Web3(this.getRpcUrl());
+    const web3 = this.getWeb3();
     const signer = web3.eth.accounts.privateKeyToAccount(privateKey);
     web3.eth.accounts.wallet.add(signer);
     const contract = new web3.eth.Contract(abi);
@@ -494,13 +501,13 @@ export class EvmChain extends Chain {
   }
 
   async getAccountAddress(privateKey: PrivateKey): Promise<string> {
-    const web3 = new Web3(this.getRpcUrl());
+    const web3 = this.getWeb3();
     const signer = web3.eth.accounts.privateKeyToAccount(privateKey);
     return signer.address;
   }
 
   async getAccountBalance(privateKey: PrivateKey): Promise<number> {
-    const web3 = new Web3(this.getRpcUrl());
+    const web3 = this.getWeb3();
     const balance = await web3.eth.getBalance(
       await this.getAccountAddress(privateKey)
     );
@@ -757,15 +764,19 @@ export class TonChain extends Chain {
     mainnet: boolean,
     wormholeChainName: string,
     nativeToken: TokenId | undefined,
-    public rpcUrl: string
+    private rpcUrl: string
   ) {
     super(id, mainnet, wormholeChainName, nativeToken);
   }
 
   async getClient(): Promise<TonClient> {
-    // add apiKey if facing rate limit
+    // We are hacking rpcUrl to include the apiKey header which is a
+    // header that is used to bypass rate limits on the TON network
+    const [rpcUrl, apiKey] = parseRpcUrl(this.rpcUrl).split("#");
+
     const client = new TonClient({
-      endpoint: this.rpcUrl,
+      endpoint: rpcUrl,
+      apiKey,
     });
     return client;
   }

+ 20 - 21
contract_manager/src/contracts/evm.ts

@@ -23,10 +23,9 @@ import {
  * with the deployedCode property generated by truffle builds
  */
 export async function getCodeDigestWithoutAddress(
-  rpcUrl: string,
+  web3: Web3,
   address: string
 ): Promise<string> {
-  const web3 = new Web3(rpcUrl);
   const code = await web3.eth.getCode(address);
   const strippedCode = code.replaceAll(
     address.toLowerCase().replace("0x", ""),
@@ -70,7 +69,7 @@ export class EvmWormholeContract extends WormholeContract {
     super();
   }
   getContract(): Contract {
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     return new web3.eth.Contract(WORMHOLE_ABI, this.address);
   }
 
@@ -99,7 +98,7 @@ export class EvmWormholeContract extends WormholeContract {
   }
 
   async upgradeGuardianSets(senderPrivateKey: PrivateKey, vaa: Buffer) {
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     const { address } = web3.eth.accounts.wallet.add(senderPrivateKey);
     const wormholeContract = new web3.eth.Contract(WORMHOLE_ABI, this.address);
     const transactionObject = wormholeContract.methods.submitNewGuardianSet(
@@ -222,7 +221,7 @@ export class EvmEntropyContract extends Storable {
   ): Promise<Buffer> {
     // Executor contract is the owner of entropy contract
     const executorAddr = await this.getOwner();
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     const executor = new web3.eth.Contract(EXECUTOR_ABI, executorAddr);
     const data = executor.methods.upgradeTo(newImplementation).encodeABI();
     return this.chain.generateExecutorPayload(executorAddr, executorAddr, data);
@@ -252,7 +251,7 @@ export class EvmEntropyContract extends Storable {
   }
 
   getContract() {
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     return new web3.eth.Contract(EXTENDED_ENTROPY_ABI, this.address);
   }
 
@@ -327,7 +326,7 @@ export class EvmEntropyContract extends Storable {
     sequenceNumber: number,
     senderPrivateKey: PrivateKey
   ) {
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     // can not use `this.getContract()` because it uses another web3 instance without the wallet
     const contract = new web3.eth.Contract(EXTENDED_ENTROPY_ABI, this.address);
     const { address } = web3.eth.accounts.wallet.add(senderPrivateKey);
@@ -343,7 +342,7 @@ export class EvmEntropyContract extends Storable {
   }
 
   generateUserRandomNumber() {
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     return web3.utils.randomHex(32);
   }
 
@@ -358,7 +357,7 @@ export class EvmEntropyContract extends Storable {
     senderPrivateKey: PrivateKey,
     withCallback?: boolean
   ) {
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     const userCommitment = web3.utils.keccak256(userRandomNumber);
     const contract = new web3.eth.Contract(EXTENDED_ENTROPY_ABI, this.address);
     const fee = await contract.methods.getFee(provider).call();
@@ -392,7 +391,7 @@ export class EvmEntropyContract extends Storable {
     sequenceNumber: string,
     senderPrivateKey: PrivateKey
   ) {
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     const contract = new web3.eth.Contract(EXTENDED_ENTROPY_ABI, this.address);
     const { address } = web3.eth.accounts.wallet.add(senderPrivateKey);
     const transactionObject = contract.methods.reveal(
@@ -486,7 +485,7 @@ export class EvmExpressRelayContract extends Storable {
   }
 
   getContract() {
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     return new web3.eth.Contract(EXPRESS_RELAY_ABI, this.address);
   }
 }
@@ -499,7 +498,7 @@ export class EvmExecutorContract {
   }
 
   async getWormholeContract(): Promise<EvmWormholeContract> {
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     //Unfortunately, there is no public method to get the wormhole address
     //Found 251 by using `forge build --extra-output storageLayout` and finding the slot for the wormhole variable.
     let address = await web3.eth.getStorageAt(this.address, 251);
@@ -508,7 +507,7 @@ export class EvmExecutorContract {
   }
 
   getContract() {
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     return new web3.eth.Contract(EXECUTOR_ABI, this.address);
   }
 
@@ -542,7 +541,7 @@ export class EvmExecutorContract {
     senderPrivateKey: PrivateKey,
     vaa: Buffer
   ) {
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     const { address } = web3.eth.accounts.wallet.add(senderPrivateKey);
     const executorContract = new web3.eth.Contract(EXECUTOR_ABI, this.address);
     const transactionObject = executorContract.methods.execute(
@@ -589,7 +588,7 @@ export class EvmPriceFeedContract extends PriceFeedContract {
   }
 
   getContract() {
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     const pythContract = new web3.eth.Contract(EXTENDED_PYTH_ABI, this.address);
     return pythContract;
   }
@@ -599,12 +598,12 @@ export class EvmPriceFeedContract extends PriceFeedContract {
    */
   async getCode(): Promise<string> {
     // TODO: handle proxy contracts
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     return web3.eth.getCode(this.address);
   }
 
   async getImplementationAddress(): Promise<string> {
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) according to EIP-1967
     const storagePosition =
       "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
@@ -617,11 +616,11 @@ export class EvmPriceFeedContract extends PriceFeedContract {
    * Returns the keccak256 digest of the contract bytecode
    */
   async getCodeDigestWithoutAddress(): Promise<string> {
-    return getCodeDigestWithoutAddress(this.chain.getRpcUrl(), this.address);
+    return getCodeDigestWithoutAddress(this.chain.getWeb3(), this.address);
   }
 
   async getTotalFee(): Promise<TokenQty> {
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     const amount = BigInt(await web3.eth.getBalance(this.address));
     return {
       amount,
@@ -712,7 +711,7 @@ export class EvmPriceFeedContract extends PriceFeedContract {
   }
 
   async executeUpdatePriceFeed(senderPrivateKey: PrivateKey, vaas: Buffer[]) {
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     const { address } = web3.eth.accounts.wallet.add(senderPrivateKey);
     const pythContract = new web3.eth.Contract(EXTENDED_PYTH_ABI, this.address);
     const priceFeedUpdateData = vaas.map((vaa) => "0x" + vaa.toString("hex"));
@@ -732,7 +731,7 @@ export class EvmPriceFeedContract extends PriceFeedContract {
     senderPrivateKey: PrivateKey,
     vaa: Buffer
   ) {
-    const web3 = new Web3(this.chain.getRpcUrl());
+    const web3 = this.chain.getWeb3();
     const { address } = web3.eth.accounts.wallet.add(senderPrivateKey);
     const pythContract = new web3.eth.Contract(EXTENDED_PYTH_ABI, this.address);
     const transactionObject = pythContract.methods.executeGovernanceInstruction(

+ 6 - 2
contract_manager/store/chains/TonChains.yaml

@@ -1,10 +1,14 @@
 - id: ton_testnet
   wormholeChainName: ton_testnet
   mainnet: false
-  rpcUrl: https://testnet.toncenter.com/api/v2/jsonRPC
+  # using # is a hack so we can later separate the key from the URL and pass
+  # it as a header
+  rpcUrl: https://testnet.toncenter.com/api/v2/#$ENV_TON_TESTNET_API_KEY
   type: TonChain
 - id: ton_mainnet
   wormholeChainName: ton_mainnet
   mainnet: true
-  rpcUrl: https://toncenter.com/api/v2/jsonRPC
+  # using # is a hack so we can later separate the key from the URL and pass
+  # it as a header
+  rpcUrl: https://toncenter.com/api/v2/jsonRPC#$ENV_TON_MAINNET_API_KEY
   type: TonChain