[cosmwasm] Osmosis multiple fee token support (#763)
* osmosis txfee supoort * add get update fee for denom * query for tx fee support * add fee checker * add comment * implement osmosis code * correct err response * fix bugs * cargo update * disable rust feature * return bool instead of result * add wormhole comment * tests added * address feedback * improve comments * cargo update
This commit is contained in:
parent
42ddfb6466
commit
15060d6a5e
File diff suppressed because it is too large
Load Diff
|
@ -9,12 +9,15 @@ description = "Pyth price receiver"
|
|||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
# IMPORTANT: if you want to build for injective, enable the default feature below
|
||||
# IMPORTANT: if you want to build for injective or osmosis, enable injective default feature for injective
|
||||
# and osmosis default feature for osmosis
|
||||
# default=["injective"]
|
||||
# default=["osmosis"]
|
||||
backtraces = ["cosmwasm-std/backtraces"]
|
||||
# use library feature to disable all init/handle/query exports
|
||||
library = []
|
||||
injective = ["dep:serde_repr"]
|
||||
osmosis=["pyth-sdk-cw/osmosis"]
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = { version = "1.0.0" }
|
||||
|
@ -24,9 +27,7 @@ serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
|||
serde_derive = { version = "1.0.103"}
|
||||
serde_repr = { version="0.1", optional = true}
|
||||
terraswap = "2.4.0"
|
||||
wormhole-bridge-terra-2 = { git = "https://github.com/wormhole-foundation/wormhole", tag = "v2.14.8", features = ["library"] }
|
||||
thiserror = { version = "1.0.20" }
|
||||
k256 = { version = "0.9.4", default-features = false, features = ["ecdsa"] }
|
||||
sha3 = { version = "0.9.1", default-features = false }
|
||||
generic-array = { version = "0.14.4" }
|
||||
hex = "0.4.2"
|
||||
|
@ -36,6 +37,7 @@ pyth-wormhole-attester-sdk = { path = "../../../../wormhole_attester/sdk/rust" }
|
|||
pyth-sdk = "0.7.0"
|
||||
byteorder = "1.4.3"
|
||||
cosmwasm-schema = "1.1.9"
|
||||
osmosis-std = "0.15.2"
|
||||
pyth-sdk-cw = { path = "../../sdk/rust" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -5,6 +5,8 @@ use crate::injective::{
|
|||
};
|
||||
#[cfg(not(feature = "injective"))]
|
||||
use cosmwasm_std::Empty as MsgWrapper;
|
||||
#[cfg(feature = "osmosis")]
|
||||
use osmosis_std::types::osmosis::txfees::v1beta1::TxfeesQuerier;
|
||||
use {
|
||||
crate::{
|
||||
governance::{
|
||||
|
@ -32,11 +34,14 @@ use {
|
|||
ConfigInfo,
|
||||
PythDataSource,
|
||||
},
|
||||
wormhole::{
|
||||
ParsedVAA,
|
||||
WormholeQueryMsg,
|
||||
},
|
||||
},
|
||||
cosmwasm_std::{
|
||||
coin,
|
||||
entry_point,
|
||||
has_coins,
|
||||
to_binary,
|
||||
Addr,
|
||||
Binary,
|
||||
|
@ -74,10 +79,6 @@ use {
|
|||
iter::FromIterator,
|
||||
time::Duration,
|
||||
},
|
||||
wormhole::{
|
||||
msg::QueryMsg as WormholeQueryMsg,
|
||||
state::ParsedVAA,
|
||||
},
|
||||
};
|
||||
|
||||
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
@ -156,6 +157,78 @@ pub fn execute(
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "osmosis"))]
|
||||
fn is_fee_sufficient(deps: &Deps, info: MessageInfo, data: &[Binary]) -> StdResult<bool> {
|
||||
use cosmwasm_std::has_coins;
|
||||
|
||||
let state = config_read(deps.storage).load()?;
|
||||
|
||||
// For any chain other than osmosis there is only one base denom
|
||||
// If base denom is present in coins and has enough amount this will return true
|
||||
// or if the base fee is set to 0
|
||||
// else it will return false
|
||||
return Ok(state.fee.amount.u128() == 0
|
||||
|| has_coins(info.funds.as_ref(), &get_update_fee(deps, data)?));
|
||||
}
|
||||
|
||||
// it only checks for fee denoms other than the base denom
|
||||
#[cfg(feature = "osmosis")]
|
||||
fn is_allowed_tx_fees_denom(deps: &Deps, denom: &String) -> bool {
|
||||
// TxFeesQuerier uses stargate queries which we can't mock as of now.
|
||||
// The capability has not been implemented in `cosmwasm-std` yet.
|
||||
// Hence, we are hacking it with a feature flag to be able to write tests.
|
||||
// FIXME
|
||||
#[cfg(test)]
|
||||
if denom == "uion"
|
||||
|| denom == "ibc/FF3065989E34457F342D4EFB8692406D49D4E2B5C70F725F127862E22CE6BDCD"
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
let querier = TxfeesQuerier::new(&deps.querier);
|
||||
match querier.denom_pool_id(denom.to_string()) {
|
||||
Ok(_) => true,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add tests for these
|
||||
#[cfg(feature = "osmosis")]
|
||||
fn is_fee_sufficient(deps: &Deps, info: MessageInfo, data: &[Binary]) -> StdResult<bool> {
|
||||
let state = config_read(deps.storage).load()?;
|
||||
|
||||
// how to change this in future
|
||||
// for given coins verify they are allowed in txfee module
|
||||
// convert each of them to the base token that is 'uosmo'
|
||||
// combine all the converted token
|
||||
// check with `has_coins`
|
||||
|
||||
// FIXME: should we accept fee for a single transaction in different tokens?
|
||||
let mut total_amount = 0u128;
|
||||
for coin in &info.funds {
|
||||
if coin.denom != state.fee.denom && !is_allowed_tx_fees_denom(deps, &coin.denom) {
|
||||
return Err(PythContractError::InvalidFeeDenom {
|
||||
denom: coin.denom.to_string(),
|
||||
})?;
|
||||
}
|
||||
total_amount = total_amount
|
||||
.checked_add(coin.amount.u128())
|
||||
.ok_or(OverflowError::new(
|
||||
OverflowOperation::Add,
|
||||
total_amount,
|
||||
coin.amount,
|
||||
))?;
|
||||
}
|
||||
|
||||
let base_denom_fee = get_update_fee(deps, data)?;
|
||||
|
||||
// NOTE: the base fee denom right now is = denom: 'uosmo', amount: 1, which is almost negligible
|
||||
// It's not important to convert the price right now. For now
|
||||
// we are keeping the base fee amount same for each valid denom -> 1
|
||||
// but this logic will be updated to use spot price for different valid tokens in future
|
||||
Ok(base_denom_fee.amount.u128() <= total_amount)
|
||||
}
|
||||
|
||||
/// Update the on-chain price feeds given the array of price update VAAs `data`.
|
||||
/// Each price update VAA must be a valid Wormhole message and sent from an authorized emitter.
|
||||
///
|
||||
|
@ -169,11 +242,8 @@ fn update_price_feeds(
|
|||
) -> StdResult<Response<MsgWrapper>> {
|
||||
let state = config_read(deps.storage).load()?;
|
||||
|
||||
// Check that a sufficient fee was sent with the message
|
||||
if state.fee.amount.u128() > 0
|
||||
&& !has_coins(info.funds.as_ref(), &get_update_fee(&deps.as_ref(), data)?)
|
||||
{
|
||||
return Err(PythContractError::InsufficientFee.into());
|
||||
if !is_fee_sufficient(&deps.as_ref(), info, data)? {
|
||||
return Err(PythContractError::InsufficientFee)?;
|
||||
}
|
||||
|
||||
let mut num_total_attestations: usize = 0;
|
||||
|
@ -504,6 +574,10 @@ 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, id.as_ref())?),
|
||||
#[cfg(feature = "osmosis")]
|
||||
QueryMsg::GetUpdateFeeForDenom { vaas, denom } => {
|
||||
to_binary(&get_update_fee_for_denom(&deps, &vaas, denom)?)
|
||||
}
|
||||
QueryMsg::GetUpdateFee { vaas } => to_binary(&get_update_fee(&deps, &vaas)?),
|
||||
QueryMsg::GetValidTimePeriod => to_binary(&get_valid_time_period(&deps)?),
|
||||
}
|
||||
|
@ -519,6 +593,7 @@ pub fn query_price_feed(deps: &Deps, feed_id: &[u8]) -> StdResult<PriceFeedRespo
|
|||
|
||||
/// Get the fee that a caller must pay in order to submit a price update.
|
||||
/// The fee depends on both the current contract configuration and the update data `vaas`.
|
||||
/// The fee is in the denoms as stored in the current configuration
|
||||
pub fn get_update_fee(deps: &Deps, vaas: &[Binary]) -> StdResult<Coin> {
|
||||
let config = config_read(deps.storage).load()?;
|
||||
|
||||
|
@ -537,6 +612,39 @@ pub fn get_update_fee(deps: &Deps, vaas: &[Binary]) -> StdResult<Coin> {
|
|||
))
|
||||
}
|
||||
|
||||
#[cfg(feature = "osmosis")]
|
||||
/// Osmosis can support multiple tokens for transaction fees
|
||||
/// This will return update fee for the given denom only if that denom is allowed in Osmosis's txFee module
|
||||
/// Else it will throw error
|
||||
pub fn get_update_fee_for_denom(deps: &Deps, vaas: &[Binary], denom: String) -> StdResult<Coin> {
|
||||
let config = config_read(deps.storage).load()?;
|
||||
|
||||
// if the denom is not a base denom it should be an allowed one
|
||||
if denom != config.fee.denom && !is_allowed_tx_fees_denom(deps, &denom) {
|
||||
return Err(PythContractError::InvalidFeeDenom { denom })?;
|
||||
}
|
||||
|
||||
// the base fee is set to -> denom = base denom of a chain, amount = 1
|
||||
// which is very minimal
|
||||
// for other valid denoms too we are using the base amount as 1
|
||||
// base amount is multiplied to number of vaas to get the total amount
|
||||
|
||||
// this will be change later on to add custom logic using spot price for valid tokens
|
||||
Ok(coin(
|
||||
config
|
||||
.fee
|
||||
.amount
|
||||
.u128()
|
||||
.checked_mul(vaas.len() as u128)
|
||||
.ok_or(OverflowError::new(
|
||||
OverflowOperation::Mul,
|
||||
config.fee.amount,
|
||||
vaas.len(),
|
||||
))?,
|
||||
denom,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_valid_time_period(deps: &Deps) -> StdResult<Duration> {
|
||||
Ok(config_read(deps.storage).load()?.valid_time_period)
|
||||
}
|
||||
|
@ -808,6 +916,80 @@ mod test {
|
|||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "osmosis"))]
|
||||
#[test]
|
||||
fn test_is_fee_sufficient() {
|
||||
let mut config_info = default_config_info();
|
||||
config_info.fee = Coin::new(100, "foo");
|
||||
|
||||
let (mut deps, _env) = setup_test();
|
||||
config(&mut deps.storage).save(&config_info).unwrap();
|
||||
|
||||
let mut info = mock_info("123", coins(100, "foo").as_slice());
|
||||
let data = create_price_update_msg(default_emitter_addr().as_slice(), EMITTER_CHAIN);
|
||||
|
||||
// sufficient fee -> true
|
||||
let result = is_fee_sufficient(&deps.as_ref(), info.clone(), &[data.clone()]);
|
||||
assert_eq!(result, Ok(true));
|
||||
|
||||
// insufficient fee -> false
|
||||
info.funds = coins(50, "foo");
|
||||
let result = is_fee_sufficient(&deps.as_ref(), info.clone(), &[data.clone()]);
|
||||
assert_eq!(result, Ok(false));
|
||||
|
||||
// insufficient fee -> false
|
||||
info.funds = coins(150, "bar");
|
||||
let result = is_fee_sufficient(&deps.as_ref(), info, &[data]);
|
||||
assert_eq!(result, Ok(false));
|
||||
}
|
||||
|
||||
#[cfg(feature = "osmosis")]
|
||||
#[test]
|
||||
fn test_is_fee_sufficient() {
|
||||
// setup config with base fee
|
||||
let base_denom = "foo";
|
||||
let base_amount = 100;
|
||||
let mut config_info = default_config_info();
|
||||
config_info.fee = Coin::new(base_amount, base_denom);
|
||||
let (mut deps, _env) = setup_test();
|
||||
config(&mut deps.storage).save(&config_info).unwrap();
|
||||
|
||||
// a dummy price data
|
||||
let data = create_price_update_msg(default_emitter_addr().as_slice(), EMITTER_CHAIN);
|
||||
|
||||
// sufficient fee in base denom -> true
|
||||
let info = mock_info("123", coins(base_amount, base_denom).as_slice());
|
||||
let result = is_fee_sufficient(&deps.as_ref(), info.clone(), &[data.clone()]);
|
||||
assert_eq!(result, Ok(true));
|
||||
|
||||
// insufficient fee in base denom -> false
|
||||
let info = mock_info("123", coins(50, base_denom).as_slice());
|
||||
let result = is_fee_sufficient(&deps.as_ref(), info, &[data.clone()]);
|
||||
assert_eq!(result, Ok(false));
|
||||
|
||||
// valid denoms are 'uion' or 'ibc/FF3065989E34457F342D4EFB8692406D49D4E2B5C70F725F127862E22CE6BDCD'
|
||||
// a valid denom other than base denom with sufficient fee
|
||||
let info = mock_info("123", coins(100, "uion").as_slice());
|
||||
let result = is_fee_sufficient(&deps.as_ref(), info, &[data.clone()]);
|
||||
assert_eq!(result, Ok(true));
|
||||
|
||||
// insufficient fee in valid denom -> false
|
||||
let info = mock_info("123", coins(50, "uion").as_slice());
|
||||
let result = is_fee_sufficient(&deps.as_ref(), info, &[data.clone()]);
|
||||
assert_eq!(result, Ok(false));
|
||||
|
||||
// an invalid denom -> Err invalid fee denom
|
||||
let info = mock_info("123", coins(100, "invalid_denom").as_slice());
|
||||
let result = is_fee_sufficient(&deps.as_ref(), info, &[data.clone()]);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(PythContractError::InvalidFeeDenom {
|
||||
denom: "invalid_denom".to_string(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_batch_attestation_empty_array() {
|
||||
let (mut deps, env) = setup_test();
|
||||
|
@ -935,7 +1117,6 @@ mod test {
|
|||
let (num_attestations, new_attestations) =
|
||||
process_batch_attestation(&mut deps.as_mut(), &env, &attestations).unwrap();
|
||||
|
||||
|
||||
let stored_price_feed = price_feed_read_bucket(&deps.storage)
|
||||
.load(&[0u8; 32])
|
||||
.unwrap();
|
||||
|
@ -987,7 +1168,6 @@ mod test {
|
|||
let (num_attestations, new_attestations) =
|
||||
process_batch_attestation(&mut deps.as_mut(), &env, &attestations).unwrap();
|
||||
|
||||
|
||||
let stored_price_feed = price_feed_read_bucket(&deps.storage)
|
||||
.load(&[0u8; 32])
|
||||
.unwrap();
|
||||
|
@ -1044,44 +1224,6 @@ mod test {
|
|||
assert_eq!(result, Err(PythContractError::InvalidUpdateEmitter.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_price_feeds_insufficient_fee() {
|
||||
let mut config_info = default_config_info();
|
||||
config_info.fee = Coin::new(100, "foo");
|
||||
|
||||
let result = apply_price_update(
|
||||
&config_info,
|
||||
default_emitter_addr().as_slice(),
|
||||
EMITTER_CHAIN,
|
||||
&[],
|
||||
);
|
||||
assert_eq!(result, Err(PythContractError::InsufficientFee.into()));
|
||||
|
||||
let result = apply_price_update(
|
||||
&config_info,
|
||||
default_emitter_addr().as_slice(),
|
||||
EMITTER_CHAIN,
|
||||
coins(100, "foo").as_slice(),
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let result = apply_price_update(
|
||||
&config_info,
|
||||
default_emitter_addr().as_slice(),
|
||||
EMITTER_CHAIN,
|
||||
coins(99, "foo").as_slice(),
|
||||
);
|
||||
assert_eq!(result, Err(PythContractError::InsufficientFee.into()));
|
||||
|
||||
let result = apply_price_update(
|
||||
&config_info,
|
||||
default_emitter_addr().as_slice(),
|
||||
EMITTER_CHAIN,
|
||||
coins(100, "bar").as_slice(),
|
||||
);
|
||||
assert_eq!(result, Err(PythContractError::InsufficientFee.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_price_feed_if_new_first_price_ok() {
|
||||
let (mut deps, env) = setup_test();
|
||||
|
@ -1232,6 +1374,118 @@ mod test {
|
|||
assert!(get_update_fee(&deps.as_ref(), &updates[0..2]).is_err());
|
||||
}
|
||||
|
||||
#[cfg(feature = "osmosis")]
|
||||
#[test]
|
||||
fn test_get_update_fee_for_denom() {
|
||||
let (mut deps, _env) = setup_test();
|
||||
let base_denom = "test";
|
||||
config(&mut deps.storage)
|
||||
.save(&ConfigInfo {
|
||||
fee: Coin::new(10, base_denom),
|
||||
..create_zero_config_info()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let updates = vec![Binary::from([1u8]), Binary::from([2u8])];
|
||||
|
||||
// test for base denom
|
||||
assert_eq!(
|
||||
get_update_fee_for_denom(&deps.as_ref(), &updates[0..0], base_denom.to_string()),
|
||||
Ok(Coin::new(0, base_denom))
|
||||
);
|
||||
assert_eq!(
|
||||
get_update_fee_for_denom(&deps.as_ref(), &updates[0..1], base_denom.to_string()),
|
||||
Ok(Coin::new(10, base_denom))
|
||||
);
|
||||
assert_eq!(
|
||||
get_update_fee_for_denom(&deps.as_ref(), &updates[0..2], base_denom.to_string()),
|
||||
Ok(Coin::new(20, base_denom))
|
||||
);
|
||||
|
||||
// test for valid but not base denom
|
||||
// valid denoms are 'uion' or 'ibc/FF3065989E34457F342D4EFB8692406D49D4E2B5C70F725F127862E22CE6BDCD'
|
||||
assert_eq!(
|
||||
get_update_fee_for_denom(&deps.as_ref(), &updates[0..0], "uion".to_string()),
|
||||
Ok(Coin::new(0, "uion"))
|
||||
);
|
||||
assert_eq!(
|
||||
get_update_fee_for_denom(&deps.as_ref(), &updates[0..1], "uion".to_string()),
|
||||
Ok(Coin::new(10, "uion"))
|
||||
);
|
||||
assert_eq!(
|
||||
get_update_fee_for_denom(&deps.as_ref(), &updates[0..2], "uion".to_string()),
|
||||
Ok(Coin::new(20, "uion"))
|
||||
);
|
||||
|
||||
|
||||
// test for invalid denom
|
||||
assert_eq!(
|
||||
get_update_fee_for_denom(&deps.as_ref(), &updates[0..0], "invalid_denom".to_string()),
|
||||
Err(PythContractError::InvalidFeeDenom {
|
||||
denom: "invalid_denom".to_string(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
assert_eq!(
|
||||
get_update_fee_for_denom(&deps.as_ref(), &updates[0..1], "invalid_denom".to_string()),
|
||||
Err(PythContractError::InvalidFeeDenom {
|
||||
denom: "invalid_denom".to_string(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
assert_eq!(
|
||||
get_update_fee_for_denom(&deps.as_ref(), &updates[0..2], "invalid_denom".to_string()),
|
||||
Err(PythContractError::InvalidFeeDenom {
|
||||
denom: "invalid_denom".to_string(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
|
||||
// check for overflow
|
||||
let big_fee: u128 = (u128::MAX / 4) * 3;
|
||||
config(&mut deps.storage)
|
||||
.save(&ConfigInfo {
|
||||
fee: Coin::new(big_fee, base_denom),
|
||||
..create_zero_config_info()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// base denom
|
||||
assert_eq!(
|
||||
get_update_fee_for_denom(&deps.as_ref(), &updates[0..1], base_denom.to_string()),
|
||||
Ok(Coin::new(big_fee, base_denom))
|
||||
);
|
||||
assert!(
|
||||
get_update_fee_for_denom(&deps.as_ref(), &updates[0..2], base_denom.to_string())
|
||||
.is_err()
|
||||
);
|
||||
|
||||
// valid but not base
|
||||
assert_eq!(
|
||||
get_update_fee_for_denom(&deps.as_ref(), &updates[0..1], "uion".to_string()),
|
||||
Ok(Coin::new(big_fee, "uion"))
|
||||
);
|
||||
assert!(
|
||||
get_update_fee_for_denom(&deps.as_ref(), &updates[0..2], "uion".to_string()).is_err()
|
||||
);
|
||||
|
||||
// invalid
|
||||
assert_eq!(
|
||||
get_update_fee_for_denom(&deps.as_ref(), &updates[0..1], "invalid_denom".to_string()),
|
||||
Err(PythContractError::InvalidFeeDenom {
|
||||
denom: "invalid_denom".to_string(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
assert_eq!(
|
||||
get_update_fee_for_denom(&deps.as_ref(), &updates[0..2], "invalid_denom".to_string()),
|
||||
Err(PythContractError::InvalidFeeDenom {
|
||||
denom: "invalid_denom".to_string(),
|
||||
}
|
||||
.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_valid_time_period() {
|
||||
let (mut deps, _env) = setup_test();
|
||||
|
|
|
@ -5,6 +5,7 @@ pub mod contract;
|
|||
pub mod governance;
|
||||
pub mod msg;
|
||||
pub mod state;
|
||||
pub mod wormhole;
|
||||
|
||||
#[cfg(feature = "injective")]
|
||||
mod injective;
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// These types are copied from the Wormhole contract. See the links with each type to see the original code
|
||||
// The reason to do so was dependency conflict. Wormhole contracts were using a very old version of a dependency
|
||||
// which is not compatible with the one used by osmosis-sdk. And since we weren't using anything else from
|
||||
// the Wormhole contract the types are moved here.
|
||||
|
||||
use {
|
||||
cosmwasm_std::Binary,
|
||||
schemars::JsonSchema,
|
||||
serde::{
|
||||
Deserialize,
|
||||
Serialize,
|
||||
},
|
||||
};
|
||||
|
||||
type HumanAddr = String;
|
||||
|
||||
// This type is copied from
|
||||
// https://github.com/wormhole-foundation/wormhole/blob/main/cosmwasm/contracts/wormhole/src/state.rs#L75
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct ParsedVAA {
|
||||
pub version: u8,
|
||||
pub guardian_set_index: u32,
|
||||
pub timestamp: u32,
|
||||
pub nonce: u32,
|
||||
pub len_signers: u8,
|
||||
|
||||
pub emitter_chain: u16,
|
||||
pub emitter_address: Vec<u8>,
|
||||
pub sequence: u64,
|
||||
pub consistency_level: u8,
|
||||
pub payload: Vec<u8>,
|
||||
|
||||
pub hash: Vec<u8>,
|
||||
}
|
||||
|
||||
|
||||
// The type is copied from
|
||||
// https://github.com/wormhole-foundation/wormhole/blob/main/cosmwasm/contracts/wormhole/src/msg.rs#L37
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum WormholeQueryMsg {
|
||||
GuardianSetInfo {},
|
||||
VerifyVAA { vaa: Binary, block_time: u64 },
|
||||
GetState {},
|
||||
QueryAddressHex { address: HumanAddr },
|
||||
}
|
|
@ -11,6 +11,9 @@ keywords = [ "pyth", "oracle", "cosmwasm" ]
|
|||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
osmosis=[]
|
||||
|
||||
[dependencies]
|
||||
pyth-sdk = "0.7.0"
|
||||
cosmwasm-std = { version = "1.0.0" }
|
||||
|
|
|
@ -48,6 +48,10 @@ pub enum PythContractError {
|
|||
/// The message did not include a sufficient fee.
|
||||
#[error("InsufficientFee")]
|
||||
InsufficientFee,
|
||||
|
||||
/// The message did not include a sufficient fee.
|
||||
#[error("InvalidFeeDenom")]
|
||||
InvalidFeeDenom { denom: String },
|
||||
}
|
||||
|
||||
impl From<PythContractError> for StdError {
|
||||
|
|
|
@ -39,6 +39,9 @@ pub enum QueryMsg {
|
|||
PriceFeed { id: PriceIdentifier },
|
||||
#[returns(Coin)]
|
||||
GetUpdateFee { vaas: Vec<Binary> },
|
||||
#[cfg(feature = "osmosis")]
|
||||
#[returns(Coin)]
|
||||
GetUpdateFeeForDenom { denom: String, vaas: Vec<Binary> },
|
||||
#[returns(Duration)]
|
||||
GetValidTimePeriod,
|
||||
}
|
||||
|
@ -76,6 +79,24 @@ pub fn get_update_fee(
|
|||
}))
|
||||
}
|
||||
|
||||
#[cfg(feature = "osmosis")]
|
||||
/// Get the fee required in order to update the on-chain state with the provided
|
||||
/// `price_update_vaas`.
|
||||
pub fn get_update_fee_for_denom(
|
||||
querier: &QuerierWrapper,
|
||||
contract_addr: Addr,
|
||||
price_update_vaas: &[Binary],
|
||||
denom: String,
|
||||
) -> StdResult<Coin> {
|
||||
querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
|
||||
contract_addr: contract_addr.into_string(),
|
||||
msg: to_binary(&QueryMsg::GetUpdateFeeForDenom {
|
||||
vaas: price_update_vaas.to_vec(),
|
||||
denom,
|
||||
})?,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Get the default length of time for which a price update remains valid.
|
||||
pub fn get_valid_time_period(querier: &QuerierWrapper, contract_addr: Addr) -> StdResult<Duration> {
|
||||
querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
|
||||
|
|
|
@ -71,6 +71,7 @@ impl MockPyth {
|
|||
Ok(QueryMsg::GetValidTimePeriod) => {
|
||||
SystemResult::Ok(to_binary(&self.valid_time_period).into())
|
||||
}
|
||||
|
||||
Ok(QueryMsg::GetUpdateFee { vaas }) => {
|
||||
let new_amount = self
|
||||
.fee_per_vaa
|
||||
|
@ -80,6 +81,16 @@ impl MockPyth {
|
|||
.unwrap();
|
||||
SystemResult::Ok(to_binary(&Coin::new(new_amount, &self.fee_per_vaa.denom)).into())
|
||||
}
|
||||
#[cfg(feature = "osmosis")]
|
||||
Ok(QueryMsg::GetUpdateFeeForDenom { vaas, denom }) => {
|
||||
let new_amount = self
|
||||
.fee_per_vaa
|
||||
.amount
|
||||
.u128()
|
||||
.checked_mul(vaas.len() as u128)
|
||||
.unwrap();
|
||||
SystemResult::Ok(to_binary(&Coin::new(new_amount, denom)).into())
|
||||
}
|
||||
Err(_e) => SystemResult::Err(SystemError::InvalidRequest {
|
||||
error: "Invalid message".into(),
|
||||
request: msg.clone(),
|
||||
|
|
Loading…
Reference in New Issue