Skip to content

Commit 8ff1ada

Browse files
author
Dev Kalra
authored
[cosmwasm] Injective cosmwasm (#680)
* update process_batch_attestation * add type to Response * add injective's support * add cfg feature * compilation feature flag * change price status to pyth status * update README * update injective's struct
1 parent 085f080 commit 8ff1ada

File tree

6 files changed

+174
-34
lines changed

6 files changed

+174
-34
lines changed

target_chains/cosmwasm/Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

target_chains/cosmwasm/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ This directory contains the code to perform all the steps. Read below for the de
1818

1919
First, build the contracts within [the current directory](./). You must have Docker installed.
2020

21+
NOTE: In order to build for Injective. We need to enable a feature in [Cargo.toml](./contracts/pyth/Cargo.toml) like this.
22+
23+
```toml
24+
[features]
25+
default=["injective"]
26+
...
27+
```
28+
2129
```sh
2230
bash build.sh
2331
```

target_chains/cosmwasm/contracts/pyth/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,20 @@ description = "Pyth price receiver"
99
crate-type = ["cdylib", "rlib"]
1010

1111
[features]
12+
# IMPORTANT: if you want to build for injective, enable the default feature below
13+
# default=["injective"]
1214
backtraces = ["cosmwasm-std/backtraces"]
1315
# use library feature to disable all init/handle/query exports
1416
library = []
17+
injective = ["dep:serde_repr"]
1518

1619
[dependencies]
1720
cosmwasm-std = { version = "1.0.0" }
1821
cosmwasm-storage = { version = "1.0.0" }
1922
schemars = "0.8.1"
2023
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
2124
serde_derive = { version = "1.0.103"}
25+
serde_repr = { version="0.1", optional = true}
2226
terraswap = "2.4.0"
2327
wormhole-bridge-terra-2 = { git = "https://github.com/wormhole-foundation/wormhole", tag = "v2.14.8", features = ["library"] }
2428
thiserror = { version = "1.0.20" }

target_chains/cosmwasm/contracts/pyth/src/contract.rs

Lines changed: 64 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
#[cfg(feature = "injective")]
2+
use crate::injective::{
3+
create_relay_pyth_prices_msg,
4+
InjectiveMsgWrapper as MsgWrapper,
5+
};
6+
#[cfg(not(feature = "injective"))]
7+
use cosmwasm_std::Empty as MsgWrapper;
18
use {
29
crate::{
310
governance::{
@@ -128,7 +135,12 @@ pub fn parse_and_verify_vaa(deps: DepsMut, block_time: u64, data: &Binary) -> St
128135
}
129136

130137
#[cfg_attr(not(feature = "library"), entry_point)]
131-
pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult<Response> {
138+
pub fn execute(
139+
deps: DepsMut,
140+
env: Env,
141+
info: MessageInfo,
142+
msg: ExecuteMsg,
143+
) -> StdResult<Response<MsgWrapper>> {
132144
match msg {
133145
ExecuteMsg::UpdatePriceFeeds { data } => update_price_feeds(deps, env, info, &data),
134146
ExecuteMsg::ExecuteGovernanceInstruction { data } => {
@@ -147,7 +159,7 @@ fn update_price_feeds(
147159
env: Env,
148160
info: MessageInfo,
149161
data: &[Binary],
150-
) -> StdResult<Response> {
162+
) -> StdResult<Response<MsgWrapper>> {
151163
let state = config_read(deps.storage).load()?;
152164

153165
// Check that a sufficient fee was sent with the message
@@ -157,8 +169,9 @@ fn update_price_feeds(
157169
return Err(PythContractError::InsufficientFee.into());
158170
}
159171

160-
let mut total_attestations: usize = 0;
161-
let mut new_attestations: usize = 0;
172+
let mut num_total_attestations: usize = 0;
173+
let mut total_new_attestations: Vec<PriceAttestation> = vec![];
174+
162175
for datum in data {
163176
let vaa = parse_and_verify_vaa(deps.branch(), env.block.time.seconds(), datum)?;
164177
verify_vaa_from_data_source(&state, &vaa)?;
@@ -167,16 +180,36 @@ fn update_price_feeds(
167180
let batch_attestation = BatchPriceAttestation::deserialize(&data[..])
168181
.map_err(|_| PythContractError::InvalidUpdatePayload)?;
169182

170-
let (num_updates, num_new) =
183+
let (num_attestations, new_attestations) =
171184
process_batch_attestation(&mut deps, &env, &batch_attestation)?;
172-
total_attestations += num_updates;
173-
new_attestations += num_new;
185+
num_total_attestations += num_attestations;
186+
for new_attestation in new_attestations {
187+
total_new_attestations.push(new_attestation.to_owned());
188+
}
174189
}
175190

176-
Ok(Response::new()
177-
.add_attribute("action", "update_price_feeds")
178-
.add_attribute("num_attestations", format!("{total_attestations}"))
179-
.add_attribute("num_updated", format!("{new_attestations}")))
191+
let num_total_new_attestations = total_new_attestations.len();
192+
193+
let response = Response::new();
194+
195+
#[cfg(feature = "injective")]
196+
{
197+
let inj_message =
198+
create_relay_pyth_prices_msg(env.contract.address, total_new_attestations);
199+
Ok(response
200+
.add_message(inj_message)
201+
.add_attribute("action", "update_price_feeds")
202+
.add_attribute("num_attestations", format!("{num_total_attestations}"))
203+
.add_attribute("num_updated", format!("{num_total_new_attestations}")))
204+
}
205+
206+
#[cfg(not(feature = "injective"))]
207+
{
208+
Ok(response
209+
.add_attribute("action", "update_price_feeds")
210+
.add_attribute("num_attestations", format!("{num_total_attestations}"))
211+
.add_attribute("num_updated", format!("{num_total_new_attestations}")))
212+
}
180213
}
181214

182215
/// Execute a governance instruction provided as the VAA `data`.
@@ -187,7 +220,7 @@ fn execute_governance_instruction(
187220
env: Env,
188221
_info: MessageInfo,
189222
data: &Binary,
190-
) -> StdResult<Response> {
223+
) -> StdResult<Response<MsgWrapper>> {
191224
let vaa = parse_and_verify_vaa(deps.branch(), env.block.time.seconds(), data)?;
192225
let state = config_read(deps.storage).load()?;
193226
verify_vaa_from_governance_source(&state, &vaa)?;
@@ -282,7 +315,7 @@ fn transfer_governance(
282315
next_config: &mut ConfigInfo,
283316
current_config: &ConfigInfo,
284317
parsed_claim_vaa: &ParsedVAA,
285-
) -> StdResult<Response> {
318+
) -> StdResult<Response<MsgWrapper>> {
286319
let claim_vaa_instruction =
287320
GovernanceInstruction::deserialize(parsed_claim_vaa.payload.as_slice())
288321
.map_err(|_| PythContractError::InvalidGovernancePayload)?;
@@ -335,7 +368,7 @@ fn transfer_governance(
335368
/// Upgrades the contract at `address` to `new_code_id` (by sending a `Migrate` message). The
336369
/// migration will fail unless this contract is the admin of the contract being upgraded.
337370
/// (Typically, `address` is this contract's address, and the contract is its own admin.)
338-
fn upgrade_contract(address: &Addr, new_code_id: u64) -> StdResult<Response> {
371+
fn upgrade_contract(address: &Addr, new_code_id: u64) -> StdResult<Response<MsgWrapper>> {
339372
Ok(Response::new()
340373
.add_message(CosmosMsg::Wasm(WasmMsg::Migrate {
341374
contract_addr: address.to_string(),
@@ -371,26 +404,23 @@ fn verify_vaa_from_governance_source(state: &ConfigInfo, vaa: &ParsedVAA) -> Std
371404
}
372405

373406
/// Update the on-chain storage for any new price updates provided in `batch_attestation`.
374-
fn process_batch_attestation(
407+
fn process_batch_attestation<'a>(
375408
deps: &mut DepsMut,
376409
env: &Env,
377-
batch_attestation: &BatchPriceAttestation,
378-
) -> StdResult<(usize, usize)> {
379-
let mut new_attestations_cnt: usize = 0;
410+
batch_attestation: &'a BatchPriceAttestation,
411+
) -> StdResult<(usize, Vec<&'a PriceAttestation>)> {
412+
let mut new_attestations = vec![];
380413

381414
// Update prices
382415
for price_attestation in batch_attestation.price_attestations.iter() {
383416
let price_feed = create_price_feed_from_price_attestation(price_attestation);
384417

385418
if update_price_feed_if_new(deps, env, price_feed)? {
386-
new_attestations_cnt += 1;
419+
new_attestations.push(price_attestation);
387420
}
388421
}
389422

390-
Ok((
391-
batch_attestation.price_attestations.len(),
392-
new_attestations_cnt,
393-
))
423+
Ok((batch_attestation.price_attestations.len(), new_attestations))
394424
}
395425

396426
fn create_price_feed_from_price_attestation(price_attestation: &PriceAttestation) -> PriceFeed {
@@ -691,7 +721,7 @@ mod test {
691721
emitter_address: &[u8],
692722
emitter_chain: u16,
693723
funds: &[Coin],
694-
) -> StdResult<Response> {
724+
) -> StdResult<Response<MsgWrapper>> {
695725
let (mut deps, env) = setup_test();
696726
config(&mut deps.storage).save(config_info).unwrap();
697727

@@ -706,11 +736,11 @@ mod test {
706736
let attestations = BatchPriceAttestation {
707737
price_attestations: vec![],
708738
};
709-
let (total_attestations, new_attestations) =
739+
let (num_attestations, new_attestations) =
710740
process_batch_attestation(&mut deps.as_mut(), &env, &attestations).unwrap();
711741

712-
assert_eq!(total_attestations, 0);
713-
assert_eq!(new_attestations, 0);
742+
assert_eq!(num_attestations, 0);
743+
assert_eq!(new_attestations.len(), 0);
714744
}
715745

716746
#[test]
@@ -824,7 +854,7 @@ mod test {
824854
let attestations = BatchPriceAttestation {
825855
price_attestations: vec![price_attestation],
826856
};
827-
let (total_attestations, new_attestations) =
857+
let (num_attestations, new_attestations) =
828858
process_batch_attestation(&mut deps.as_mut(), &env, &attestations).unwrap();
829859

830860

@@ -834,8 +864,8 @@ mod test {
834864
let price = stored_price_feed.get_price_unchecked();
835865
let ema_price = stored_price_feed.get_ema_price_unchecked();
836866

837-
assert_eq!(total_attestations, 1);
838-
assert_eq!(new_attestations, 1);
867+
assert_eq!(num_attestations, 1);
868+
assert_eq!(new_attestations.len(), 1);
839869

840870
// for price
841871
assert_eq!(price.price, 99);
@@ -876,7 +906,7 @@ mod test {
876906
let attestations = BatchPriceAttestation {
877907
price_attestations: vec![price_attestation],
878908
};
879-
let (total_attestations, new_attestations) =
909+
let (num_attestations, new_attestations) =
880910
process_batch_attestation(&mut deps.as_mut(), &env, &attestations).unwrap();
881911

882912

@@ -886,8 +916,8 @@ mod test {
886916
let price = stored_price_feed.get_price_unchecked();
887917
let ema_price = stored_price_feed.get_ema_price_unchecked();
888918

889-
assert_eq!(total_attestations, 1);
890-
assert_eq!(new_attestations, 1);
919+
assert_eq!(num_attestations, 1);
920+
assert_eq!(new_attestations.len(), 1);
891921

892922
// for price
893923
assert_eq!(price.price, 100);
@@ -1147,7 +1177,7 @@ mod test {
11471177
fn apply_governance_vaa(
11481178
initial_config: &ConfigInfo,
11491179
vaa: &ParsedVAA,
1150-
) -> StdResult<(Response, ConfigInfo)> {
1180+
) -> StdResult<(Response<MsgWrapper>, ConfigInfo)> {
11511181
let (mut deps, env) = setup_test();
11521182
config(&mut deps.storage).save(initial_config).unwrap();
11531183

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use {
2+
cosmwasm_std::{
3+
Addr,
4+
CosmosMsg,
5+
CustomMsg,
6+
},
7+
pyth_wormhole_attester_sdk::PriceAttestation,
8+
schemars::JsonSchema,
9+
serde::{
10+
Deserialize,
11+
Serialize,
12+
},
13+
};
14+
15+
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
16+
pub struct InjectivePriceAttestation {
17+
pub price_id: String,
18+
pub price: i64,
19+
pub conf: u64,
20+
pub expo: i32,
21+
pub ema_price: i64,
22+
pub ema_conf: u64,
23+
pub ema_expo: i32,
24+
pub publish_time: i64,
25+
}
26+
27+
impl From<&PriceAttestation> for InjectivePriceAttestation {
28+
fn from(pa: &PriceAttestation) -> Self {
29+
InjectivePriceAttestation {
30+
price_id: pa.price_id.to_hex(),
31+
price: pa.price,
32+
conf: pa.conf,
33+
expo: pa.expo,
34+
ema_price: pa.ema_price,
35+
ema_conf: pa.ema_conf,
36+
ema_expo: pa.expo,
37+
publish_time: pa.publish_time,
38+
}
39+
}
40+
}
41+
42+
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
43+
#[serde(rename_all = "snake_case")]
44+
pub enum InjectiveMsg {
45+
RelayPythPrices {
46+
sender: Addr,
47+
price_attestations: Vec<InjectivePriceAttestation>,
48+
},
49+
}
50+
51+
52+
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
53+
#[serde(rename_all = "snake_case")]
54+
pub struct InjectiveMsgWrapper {
55+
pub route: String,
56+
pub msg_data: InjectiveMsg,
57+
}
58+
59+
pub fn create_relay_pyth_prices_msg(
60+
sender: Addr,
61+
price_attestations: Vec<PriceAttestation>,
62+
) -> CosmosMsg<InjectiveMsgWrapper> {
63+
InjectiveMsgWrapper {
64+
route: "oracle".to_string(),
65+
msg_data: InjectiveMsg::RelayPythPrices {
66+
sender,
67+
price_attestations: price_attestations
68+
.iter()
69+
.map(InjectivePriceAttestation::from)
70+
.collect(),
71+
},
72+
}
73+
.into()
74+
}
75+
76+
impl From<InjectiveMsgWrapper> for CosmosMsg<InjectiveMsgWrapper> {
77+
fn from(s: InjectiveMsgWrapper) -> CosmosMsg<InjectiveMsgWrapper> {
78+
CosmosMsg::Custom(s)
79+
}
80+
}
81+
82+
impl CustomMsg for InjectiveMsgWrapper {
83+
}

target_chains/cosmwasm/contracts/pyth/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@ pub mod contract;
55
pub mod governance;
66
pub mod msg;
77
pub mod state;
8+
9+
#[cfg(feature = "injective")]
10+
mod injective;

0 commit comments

Comments
 (0)