use cosmwasm_std::{ entry_point, to_binary, Binary, CosmosMsg, Deps, DepsMut, Env, MessageInfo, QueryRequest, Response, StdError, StdResult, WasmMsg, WasmQuery, }; use crate::{ msg::{ ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, }, state::{ config, config_read, price_info, price_info_read, sequence, sequence_read, ConfigInfo, UpgradeContract, }, types::PriceAttestation, }; use wormhole::{ byte_utils::get_string_from_32, error::ContractError, msg::QueryMsg as WormholeQueryMsg, state::{ vaa_archive_add, vaa_archive_check, GovernancePacket, ParsedVAA, }, }; // Chain ID of Terra const CHAIN_ID: u16 = 3; #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { Ok(Response::new()) } #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, _env: Env, _info: MessageInfo, msg: InstantiateMsg, ) -> StdResult { // Save general wormhole info let state = ConfigInfo { gov_chain: msg.gov_chain, gov_address: msg.gov_address.as_slice().to_vec(), wormhole_contract: msg.wormhole_contract, pyth_emitter: msg.pyth_emitter.as_slice().to_vec(), pyth_emitter_chain: msg.pyth_emitter_chain, }; config(deps.storage).save(&state)?; sequence(deps.storage).save(&0)?; Ok(Response::default()) } pub fn parse_vaa(deps: DepsMut, block_time: u64, data: &Binary) -> StdResult { let cfg = config_read(deps.storage).load()?; let vaa: ParsedVAA = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: cfg.wormhole_contract.clone(), msg: to_binary(&WormholeQueryMsg::VerifyVAA { vaa: data.clone(), block_time, })?, }))?; Ok(vaa) } #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult { match msg { ExecuteMsg::SubmitVaa { data } => submit_vaa(deps, env, info, &data), } } fn submit_vaa( mut deps: DepsMut, env: Env, _info: MessageInfo, data: &Binary, ) -> StdResult { let state = config_read(deps.storage).load()?; let vaa = parse_vaa(deps.branch(), env.block.time.seconds(), data)?; let data = vaa.payload; // check if vaa is from governance if state.gov_chain == vaa.emitter_chain && state.gov_address == vaa.emitter_address { if vaa_archive_check(deps.storage, vaa.hash.as_slice()) { return ContractError::VaaAlreadyExecuted.std_err(); } vaa_archive_add(deps.storage, vaa.hash.as_slice())?; return handle_governance_payload(deps, env, &data); } // IMPORTANT: VAA replay-protection is not implemented in this code-path // Sequences are used to prevent replay or price rollbacks let message = PriceAttestation::deserialize(&data[..]).map_err(|_| ContractError::InvalidVAA.std())?; if vaa.emitter_address != state.pyth_emitter || vaa.emitter_chain != state.pyth_emitter_chain { return ContractError::InvalidVAA.std_err(); } // Check sequence let last_sequence = sequence_read(deps.storage).load()?; if vaa.sequence <= last_sequence && last_sequence != 0 { return Err(StdError::generic_err( "price sequences need to be monotonically increasing", )); } sequence(deps.storage).save(&vaa.sequence)?; // Update price price_info(deps.storage).save(&message.price_id.to_bytes()[..], &data)?; Ok(Response::new() .add_attribute("action", "price_update") .add_attribute("price_feed", message.price_id.to_string())) } fn handle_governance_payload(deps: DepsMut, env: Env, data: &Vec) -> StdResult { let gov_packet = GovernancePacket::deserialize(&data)?; let module = get_string_from_32(&gov_packet.module); if module != "PythBridge" { return Err(StdError::generic_err("this is not a valid module")); } if gov_packet.chain != 0 && gov_packet.chain != CHAIN_ID { return Err(StdError::generic_err( "the governance VAA is for another chain", )); } match gov_packet.action { 2u8 => handle_upgrade_contract(deps, env, &gov_packet.payload), _ => ContractError::InvalidVAAAction.std_err(), } } fn handle_upgrade_contract(_deps: DepsMut, env: Env, data: &Vec) -> StdResult { let UpgradeContract { new_contract } = UpgradeContract::deserialize(&data)?; Ok(Response::new() .add_message(CosmosMsg::Wasm(WasmMsg::Migrate { contract_addr: env.contract.address.to_string(), new_code_id: new_contract, msg: to_binary(&MigrateMsg {})?, })) .add_attribute("action", "contract_upgrade")) } #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::PriceInfo { price_id } => { to_binary(&query_price_info(deps, price_id.as_slice())?) } } } pub fn query_price_info(deps: Deps, address: &[u8]) -> StdResult { match price_info_read(deps.storage).load(address) { Ok(data) => PriceAttestation::deserialize(&data[..]).map_err(|_| { StdError::parse_err("PriceAttestation", "failed to decode price attestation") }), Err(_) => ContractError::AssetNotFound.std_err(), } }