Kaynağa Gözat

Upgrade contract & fix deployment script (#447)

* merge

* stuff

* gr

* config

* config

* config

* config

* config

* config

* config

* cleanup

* add commands

* cleaunp

* fix

* use u64

Co-authored-by: Jayant Krishnamurthy <jkrishnamurthy@jumptrading.com>
Co-authored-by: Jayant Krishnamurthy <jayant@jumpcrypto.com>
Jayant Krishnamurthy 2 yıl önce
ebeveyn
işleme
cdf99f4c60

+ 1 - 1
cosmwasm/README.md

@@ -21,7 +21,7 @@ First, build the contracts within [the current directory](./):
 bash build.sh
 ```
 
-This command will build and save the Pyth contract in the `artifact` directory.
+This command will build and save the Pyth contract in the `artifacts` directory.
 
 Then, to deploy the Pyth contract (`pyth_cosmwasm.wasm`), run the following command in the `tools` directory:
 

+ 33 - 4
cosmwasm/contracts/pyth/src/contract.rs

@@ -34,8 +34,10 @@ use {
         entry_point,
         has_coins,
         to_binary,
+        Addr,
         Binary,
         Coin,
+        CosmosMsg,
         Deps,
         DepsMut,
         Env,
@@ -46,6 +48,7 @@ use {
         Response,
         StdResult,
         Timestamp,
+        WasmMsg,
         WasmQuery,
     },
     p2w_sdk::BatchPriceAttestation,
@@ -67,9 +70,19 @@ use {
     },
 };
 
+/// Migration code that runs once when the contract is upgraded. On upgrade, the migrate
+/// function in the *new* code version is run, which allows the new code to update the on-chain
+/// state before any of its other functions are invoked.
+///
+/// After the upgrade is complete, the code in this function can be deleted (and replaced with
+/// different code for the next migration).
+///
+/// Most upgrades won't require any special migration logic. In those cases,
+/// this function can safely be implemented as:
+/// `Ok(Response::default())`
 #[cfg_attr(not(feature = "library"), entry_point)]
 pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult<Response> {
-    Ok(Response::new())
+    Ok(Response::default())
 }
 
 #[cfg_attr(not(feature = "library"), entry_point)]
@@ -182,9 +195,11 @@ fn execute_governance_instruction(
     }
 
     let response = match instruction.action {
-        UpgradeContract { .. } => {
-            // FIXME: implement this
-            Err(PythContractError::InvalidGovernancePayload)?
+        UpgradeContract { code_id } => {
+            if instruction.target_chain_id == 0 {
+                Err(PythContractError::InvalidGovernancePayload)?
+            }
+            upgrade_contract(&env.contract.address, code_id)?
         }
         AuthorizeGovernanceDataSourceTransfer { claim_vaa } => {
             let parsed_claim_vaa = parse_vaa(deps.branch(), env.block.time.seconds(), &claim_vaa)?;
@@ -286,6 +301,20 @@ fn transfer_governance(
     }
 }
 
+/// Upgrades the contract at `address` to `new_code_id` (by sending a `Migrate` message). The
+/// migration will fail unless this contract is the admin of the contract being upgraded.
+/// (Typically, `address` is this contract's address, and the contract is its own admin.)
+fn upgrade_contract(address: &Addr, new_code_id: u64) -> StdResult<Response> {
+    Ok(Response::new()
+        .add_message(CosmosMsg::Wasm(WasmMsg::Migrate {
+            contract_addr: address.to_string(),
+            new_code_id,
+            msg: to_binary(&MigrateMsg {})?,
+        }))
+        .add_attribute("action", "upgrade_contract")
+        .add_attribute("new_code_id", format!("{new_code_id}")))
+}
+
 /// Check that `vaa` is from a valid data source (and hence is a legitimate price update message).
 fn verify_vaa_from_data_source(state: &ConfigInfo, vaa: &ParsedVAA) -> StdResult<()> {
     let vaa_data_source = PythDataSource {

+ 6 - 7
cosmwasm/contracts/pyth/src/governance.rs

@@ -51,9 +51,9 @@ impl GovernanceModule {
 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
 #[repr(u8)]
 pub enum GovernanceAction {
-    UpgradeContract { address: [u8; 20] }, // 0
+    UpgradeContract { code_id: u64 },                            // 0
     AuthorizeGovernanceDataSourceTransfer { claim_vaa: Binary }, // 1
-    SetDataSources { data_sources: Vec<PythDataSource> }, // 2
+    SetDataSources { data_sources: Vec<PythDataSource> },        // 2
     // Set the fee to val * (10 ** expo)
     SetFee { val: u64, expo: u64 }, // 3
     // Set the default valid period to the provided number of seconds
@@ -92,9 +92,8 @@ impl GovernanceInstruction {
 
         let action: Result<GovernanceAction, String> = match action_type {
             0 => {
-                let mut address: [u8; 20] = [0; 20];
-                bytes.read_exact(&mut address)?;
-                Ok(GovernanceAction::UpgradeContract { address })
+                let code_id = bytes.read_u64::<BigEndian>()?;
+                Ok(GovernanceAction::UpgradeContract { code_id })
             }
             1 => {
                 let mut payload: Vec<u8> = vec![];
@@ -161,10 +160,10 @@ impl GovernanceInstruction {
         buf.write_u8(self.module.to_u8())?;
 
         match &self.action {
-            GovernanceAction::UpgradeContract { address } => {
+            GovernanceAction::UpgradeContract { code_id } => {
                 buf.write_u8(0)?;
                 buf.write_u16::<BigEndian>(self.target_chain_id)?;
-                buf.write_all(address)?;
+                buf.write_u64::<BigEndian>(*code_id)?;
             }
             GovernanceAction::AuthorizeGovernanceDataSourceTransfer { claim_vaa } => {
                 buf.write_u8(1)?;

+ 80 - 43
cosmwasm/tools/deploy-pyth-bridge.js

@@ -7,13 +7,10 @@ import {
 import { readFileSync } from "fs";
 import { Bech32, toHex } from "@cosmjs/encoding";
 import { zeroPad } from "ethers/lib/utils.js";
-import axios from "axios";
 import yargs from "yargs";
 import { hideBin } from "yargs/helpers";
 import assert from "assert";
 
-export const TERRA_GAS_PRICES_URL = "https://fcd.terra.dev/v1/txs/gas_prices";
-
 const argv = yargs(hideBin(process.argv))
   .option("network", {
     description: "Which network to deploy to",
@@ -64,38 +61,96 @@ const artifact = argv.artifact;
 const CONFIG = {
   mainnet: {
     terraHost: {
-      URL: "https://lcd.terra.dev",
-      chainID: "columbus-5",
+      URL: "https://phoenix-lcd.terra.dev",
+      chainID: "phoenix-1",
       name: "mainnet",
     },
-    wormholeContract: "terra1dq03ugtd40zu9hcgdzrsq6z2z4hwhc9tqk2uy5",
-    pythEmitterAddress:
-      "6bb14509a612f01fbbc4cffeebd4bbfb492a86df717ebe92eb6df432a3f00a25",
+    pyth_config: {
+      wormhole_contract:
+        "terra12mrnzvhx3rpej6843uge2yyfppfyd3u9c3uq223q8sl48huz9juqffcnh",
+      data_sources: [
+        {
+          emitter: Buffer.from(
+            "6bb14509a612f01fbbc4cffeebd4bbfb492a86df717ebe92eb6df432a3f00a25",
+            "hex"
+          ).toString("base64"),
+          chain_id: 1,
+        },
+        {
+          emitter: Buffer.from(
+            "f8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0",
+            "hex"
+          ).toString("base64"),
+          chain_id: 26,
+        },
+      ],
+      governance_source: {
+        emitter: Buffer.from(
+          "5635979a221c34931e32620b9293a463065555ea71fe97cd6237ade875b12e9e",
+          "hex"
+        ).toString("base64"),
+        chain_id: 1,
+      },
+      governance_source_index: 0,
+      governance_sequence_number: 0,
+      chain_id: 18,
+      valid_time_period_secs: 60,
+      fee: {
+        amount: "1",
+        denom: "uluna",
+      },
+    },
   },
   testnet: {
     terraHost: {
-      URL: "https://bombay-lcd.terra.dev",
-      chainID: "bombay-12",
+      URL: "https://pisco-lcd.terra.dev",
+      chainID: "pisco-1",
       name: "testnet",
     },
-    wormholeContract: "terra1pd65m0q9tl3v8znnz5f5ltsfegyzah7g42cx5v",
-    pythEmitterAddress:
-      "f346195ac02f37d60d4db8ffa6ef74cb1be3550047543a4a9ee9acf4d78697b0",
+    pyth_config: {
+      wormhole_contract:
+        "terra19nv3xr5lrmmr7egvrk2kqgw4kcn43xrtd5g0mpgwwvhetusk4k7s66jyv0",
+      data_sources: [
+        {
+          emitter: Buffer.from(
+            "f346195ac02f37d60d4db8ffa6ef74cb1be3550047543a4a9ee9acf4d78697b0",
+            "hex"
+          ).toString("base64"),
+          chain_id: 1,
+        },
+        {
+          emitter: Buffer.from(
+            "a27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b6",
+            "hex"
+          ).toString("base64"),
+          chain_id: 26,
+        },
+      ],
+      governance_source: {
+        emitter: Buffer.from(
+          "63278d271099bfd491951b3e648f08b1c71631e4a53674ad43e8f9f98068c385",
+          "hex"
+        ).toString("base64"),
+        chain_id: 1,
+      },
+      governance_source_index: 0,
+      governance_sequence_number: 0,
+      chain_id: 18,
+      valid_time_period_secs: 60,
+      fee: {
+        amount: "1",
+        denom: "uluna",
+      },
+    },
   },
 };
 
 const terraHost = CONFIG[argv.network].terraHost;
-const wormholeContract = CONFIG[argv.network].wormholeContract;
-const pythEmitterAddress = CONFIG[argv.network].pythEmitterAddress;
-
+const pythConfig = CONFIG[argv.network].pyth_config;
 const lcd = new LCDClient(terraHost);
 
 const feeDenoms = ["uluna"];
 
-const gasPrices = await axios
-  .get(TERRA_GAS_PRICES_URL)
-  .then((result) => result.data);
-
 const wallet = lcd.wallet(
   new MnemonicKey({
     mnemonic: argv.mnemonic,
@@ -124,22 +179,9 @@ if (argv.codeId !== undefined) {
     contract_bytes.toString("base64")
   );
 
-  const feeEstimate = await lcd.tx.estimateFee(
-    wallet.key.accAddress,
-    [store_code],
-    {
-      feeDenoms,
-      gasPrices,
-    }
-  );
-
-  console.log("Deploy fee: ", feeEstimate.amount.toString());
-
   const tx = await wallet.createAndSignTx({
     msgs: [store_code],
     feeDenoms,
-    gasPrices,
-    fee: feeEstimate,
   });
 
   const rs = await lcd.tx.broadcast(tx);
@@ -166,7 +208,7 @@ if (argv.codeId !== undefined) {
 if (argv.instantiate) {
   console.log("Instantiating a contract");
 
-  async function instantiate(codeId, inst_msg) {
+  async function instantiate(codeId, inst_msg, label) {
     var address;
     await wallet
       .createAndSignTx({
@@ -175,7 +217,9 @@ if (argv.instantiate) {
             wallet.key.accAddress,
             wallet.key.accAddress,
             codeId,
-            inst_msg
+            inst_msg,
+            undefined,
+            label
           ),
         ],
       })
@@ -199,13 +243,7 @@ if (argv.instantiate) {
     return address;
   }
 
-  const pythChain = 1;
-
-  const contractAddress = await instantiate(codeId, {
-    wormhole_contract: wormholeContract,
-    pyth_emitter: Buffer.from(pythEmitterAddress, "hex").toString("base64"),
-    pyth_emitter_chain: pythChain,
-  });
+  const contractAddress = await instantiate(codeId, pythConfig, "pyth");
 
   console.log(`Deployed Pyth contract at ${contractAddress}`);
 }
@@ -233,7 +271,6 @@ if (argv.migrate) {
       ),
     ],
     feeDenoms,
-    gasPrices,
   });
 
   const rs = await lcd.tx.broadcast(tx);

+ 1 - 1
cosmwasm/tools/package.json

@@ -5,7 +5,7 @@
   "main": "deploy.js",
   "type": "module",
   "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "deploy-pyth": "node deploy-pyth-bridge.js"
   },
   "author": "",
   "license": "ISC",