Bläddra i källkod

Add query methods to cosmwasm contract (#441)

* implement query operations

* more governance

* revert

* update the terra sdk?

* wtf

* fix ci

* add tests

Co-authored-by: Jayant Krishnamurthy <jkrishnamurthy@jumptrading.com>
Jayant Krishnamurthy 2 år sedan
förälder
incheckning
6b07ae607c

+ 84 - 3
cosmwasm/contracts/pyth/src/contract.rs

@@ -23,6 +23,7 @@ use {
         },
     },
     cosmwasm_std::{
+        coin,
         entry_point,
         has_coins,
         to_binary,
@@ -32,6 +33,8 @@ use {
         DepsMut,
         Env,
         MessageInfo,
+        OverflowError,
+        OverflowOperation,
         QueryRequest,
         Response,
         StdResult,
@@ -365,9 +368,8 @@ fn update_price_feed_if_new(
 pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult<Binary> {
     match msg {
         QueryMsg::PriceFeed { id } => to_binary(&query_price_feed(deps, env, id.as_ref())?),
-        // TODO: implement queries for the update fee and valid time period along the following lines:
-        // QueryMsg::GetUpdateFee { data: bytes[] } => ???,
-        // QueryMsg::GetValidTimePeriod => ???
+        QueryMsg::GetUpdateFee { vaas } => to_binary(&get_update_fee(deps, &vaas)?),
+        QueryMsg::GetValidTimePeriod => to_binary(&get_valid_time_period(deps)?),
     }
 }
 
@@ -403,6 +405,26 @@ pub fn query_price_feed(deps: Deps, env: Env, address: &[u8]) -> StdResult<Price
     }
 }
 
+pub fn get_update_fee(deps: Deps, vaas: &[Binary]) -> StdResult<Coin> {
+    let config = config_read(deps.storage).load()?;
+    Ok(coin(
+        config
+            .fee
+            .u128()
+            .checked_mul(vaas.len() as u128)
+            .ok_or(OverflowError::new(
+                OverflowOperation::Mul,
+                config.fee,
+                vaas.len(),
+            ))?,
+        config.fee_denom,
+    ))
+}
+
+pub fn get_valid_time_period(deps: Deps) -> StdResult<Duration> {
+    Ok(config_read(deps.storage).load()?.valid_time_period)
+}
+
 #[cfg(test)]
 mod test {
     use {
@@ -846,6 +868,65 @@ mod test {
         );
     }
 
+    #[test]
+    fn test_get_update_fee() {
+        let (mut deps, _env) = setup_test();
+        let fee_denom: String = "test".into();
+        config(&mut deps.storage)
+            .save(&ConfigInfo {
+                fee: Uint128::new(10),
+                fee_denom: fee_denom.clone(),
+                ..create_zero_config_info()
+            })
+            .unwrap();
+
+        let updates = vec![Binary::from([1u8]), Binary::from([2u8])];
+
+        assert_eq!(
+            get_update_fee(deps.as_ref(), &updates[0..0]),
+            Ok(Coin::new(0, fee_denom.clone()))
+        );
+        assert_eq!(
+            get_update_fee(deps.as_ref(), &updates[0..1]),
+            Ok(Coin::new(10, fee_denom.clone()))
+        );
+        assert_eq!(
+            get_update_fee(deps.as_ref(), &updates[0..2]),
+            Ok(Coin::new(20, fee_denom.clone()))
+        );
+
+        let big_fee: Uint128 = Uint128::from((u128::MAX / 4) * 3);
+        config(&mut deps.storage)
+            .save(&ConfigInfo {
+                fee: big_fee,
+                fee_denom: fee_denom.clone(),
+                ..create_zero_config_info()
+            })
+            .unwrap();
+
+        assert_eq!(
+            get_update_fee(deps.as_ref(), &updates[0..1]),
+            Ok(Coin::new(big_fee.u128(), fee_denom))
+        );
+        assert!(get_update_fee(deps.as_ref(), &updates[0..2]).is_err());
+    }
+
+    #[test]
+    fn test_get_valid_time_period() {
+        let (mut deps, _env) = setup_test();
+        config(&mut deps.storage)
+            .save(&ConfigInfo {
+                valid_time_period: Duration::from_secs(10),
+                ..create_zero_config_info()
+            })
+            .unwrap();
+
+        assert_eq!(
+            get_valid_time_period(deps.as_ref()),
+            Ok(Duration::from_secs(10))
+        );
+    }
+
     #[test]
     fn test_add_data_source_ok_with_owner() {
         let (mut deps, env) = setup_test();

+ 19 - 4
cosmwasm/contracts/pyth/src/msg.rs

@@ -4,6 +4,10 @@ use {
         Binary,
         Uint128,
     },
+    pyth_sdk_cw::{
+        PriceFeed,
+        PriceIdentifier,
+    },
     schemars::JsonSchema,
     serde::{
         Deserialize,
@@ -14,6 +18,7 @@ use {
 type HumanAddr = String;
 
 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
 pub struct InstantiateMsg {
     pub wormhole_contract:          HumanAddr,
     // TODO: this should support multiple emitters
@@ -43,7 +48,17 @@ pub enum ExecuteMsg {
 #[serde(rename_all = "snake_case")]
 pub struct MigrateMsg {}
 
-pub use pyth_sdk_cw::{
-    PriceFeedResponse,
-    QueryMsg,
-};
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum QueryMsg {
+    PriceFeed { id: PriceIdentifier },
+    GetUpdateFee { vaas: Vec<Binary> },
+    GetValidTimePeriod,
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct PriceFeedResponse {
+    pub price_feed: PriceFeed,
+}

+ 2 - 2
third_party/pyth/p2w-relay/package-lock.json

@@ -50,7 +50,7 @@
       "dependencies": {
         "@certusone/wormhole-sdk": "0.2.1",
         "@improbable-eng/grpc-web-node-http-transport": "^0.14.1",
-        "@pythnetwork/pyth-sdk-js": "^1.0.0"
+        "@pythnetwork/pyth-sdk-js": "^1.1.0"
       },
       "devDependencies": {
         "@openzeppelin/contracts": "^4.2.0",
@@ -9305,7 +9305,7 @@
         "@certusone/wormhole-sdk": "0.2.1",
         "@improbable-eng/grpc-web-node-http-transport": "^0.14.1",
         "@openzeppelin/contracts": "^4.2.0",
-        "@pythnetwork/pyth-sdk-js": "^1.0.0",
+        "@pythnetwork/pyth-sdk-js": "^1.1.0",
         "@typechain/ethers-v5": "^7.1.2",
         "@types/long": "^4.0.1",
         "@types/node": "^16.6.1",

+ 24 - 9
third_party/pyth/p2w-relay/src/relay/terra.ts

@@ -1,4 +1,3 @@
-import { fromUint8Array } from "js-base64";
 import {
   Coin,
   LCDClient,
@@ -6,7 +5,6 @@ import {
   MnemonicKey,
   MsgExecuteContract,
 } from "@terra-money/terra.js";
-import { hexToUint8Array } from "@certusone/wormhole-sdk";
 import axios from "axios";
 import { logger } from "../helpers";
 
@@ -66,6 +64,9 @@ export class TerraRelay implements Relay {
 
       const wallet = lcdClient.wallet(mk);
 
+      logger.debug("TIME: Querying fee");
+      let fee: Coin = await this.getUpdateFee(signedVAAs);
+
       logger.debug("TIME: creating messages");
       let msgs = new Array<MsgExecuteContract>();
       for (let idx = 0; idx < signedVAAs.length; ++idx) {
@@ -77,8 +78,7 @@ export class TerraRelay implements Relay {
               data: Buffer.from(signedVAAs[idx], "hex").toString("base64"),
             },
           },
-          // TODO: Query the fee before
-          [new Coin(this.coinDenom, 1)]
+          [fee]
         );
 
         msgs.push(msg);
@@ -181,11 +181,6 @@ export class TerraRelay implements Relay {
     logger.info("Querying terra for price info for priceId [" + priceId + "]");
 
     const lcdClient = new LCDClient(this.lcdConfig);
-
-    const mk = new MnemonicKey({
-      mnemonic: this.walletPrivateKey,
-    });
-
     return await lcdClient.wasm.contractQuery(this.contractAddress, {
       price_feed: {
         id: priceId,
@@ -193,6 +188,26 @@ export class TerraRelay implements Relay {
     });
   }
 
+  async getUpdateFee(hexVAAs: Array<string>): Promise<Coin> {
+    const lcdClient = new LCDClient(this.lcdConfig);
+
+    let base64VAAs = [];
+    for (let idx = 0; idx < hexVAAs.length; ++idx) {
+      base64VAAs.push(Buffer.from(hexVAAs[idx], "hex").toString("base64"));
+    }
+
+    let result = await lcdClient.wasm.contractQuery<Coin.Data>(
+      this.contractAddress,
+      {
+        get_update_fee: {
+          vaas: base64VAAs,
+        },
+      }
+    );
+
+    return Coin.fromData(result);
+  }
+
   async getPayerInfo(): Promise<{ address: string; balance: bigint }> {
     const lcdClient = new LCDClient(this.lcdConfig);