fixed mess with conversion rates (#1338)

This commit is contained in:
Svyatoslav Nikolsky
2022-03-03 14:46:07 +03:00
committed by Bastian Köcher
parent dd7404f249
commit e822bbf8ab
5 changed files with 99 additions and 49 deletions
@@ -24,7 +24,7 @@ use relay_substrate_client::Chain;
use sp_runtime::FixedU128; use sp_runtime::FixedU128;
use structopt::StructOpt; use structopt::StructOpt;
use strum::VariantNames; use strum::VariantNames;
use substrate_relay_helper::helpers::target_to_source_conversion_rate; use substrate_relay_helper::helpers::tokens_conversion_rate_from_metrics;
/// Estimate Delivery & Dispatch Fee command. /// Estimate Delivery & Dispatch Fee command.
#[derive(StructOpt, Debug, PartialEq)] #[derive(StructOpt, Debug, PartialEq)]
@@ -98,6 +98,8 @@ impl EstimateFee {
} }
} }
/// The caller may provide target to source tokens conversion rate override to use in fee
/// computation.
pub(crate) async fn estimate_message_delivery_and_dispatch_fee< pub(crate) async fn estimate_message_delivery_and_dispatch_fee<
Source: Chain, Source: Chain,
Target: Chain, Target: Chain,
@@ -121,14 +123,14 @@ pub(crate) async fn estimate_message_delivery_and_dispatch_fee<
) { ) {
(Some(ConversionRateOverride::Explicit(v)), _, _) => { (Some(ConversionRateOverride::Explicit(v)), _, _) => {
let conversion_rate_override = FixedU128::from_float(v); let conversion_rate_override = FixedU128::from_float(v);
log::info!(target: "bridge", "Conversion rate override: {:?} (explicit)", conversion_rate_override.to_float()); log::info!(target: "bridge", "{} -> {} conversion rate override: {:?} (explicit)", Target::NAME, Source::NAME, conversion_rate_override.to_float());
Some(conversion_rate_override) Some(conversion_rate_override)
}, },
(Some(ConversionRateOverride::Metric), Some(source_token_id), Some(target_token_id)) => { (Some(ConversionRateOverride::Metric), Some(source_token_id), Some(target_token_id)) => {
let conversion_rate_override = FixedU128::from_float( let conversion_rate_override = FixedU128::from_float(
target_to_source_conversion_rate(source_token_id, target_token_id).await?, tokens_conversion_rate_from_metrics(target_token_id, source_token_id).await?,
); );
log::info!(target: "bridge", "Conversion rate override: {:?} (from metric)", conversion_rate_override.to_float()); log::info!(target: "bridge", "{} -> {} conversion rate override: {:?} (from metric)", Target::NAME, Source::NAME, conversion_rate_override.to_float());
Some(conversion_rate_override) Some(conversion_rate_override)
}, },
_ => None, _ => None,
@@ -403,13 +403,13 @@ impl RelayHeadersAndMessages {
.as_ref() .as_ref()
.ok_or_else(format_err)? .ok_or_else(format_err)?
.shared_value_ref(), .shared_value_ref(),
left_to_right_metrics right_to_left_metrics
.source_to_base_conversion_rate .target_to_base_conversion_rate
.as_ref() .as_ref()
.ok_or_else(format_err)? .ok_or_else(format_err)?
.shared_value_ref(), .shared_value_ref(),
left_to_right_metrics right_to_left_metrics
.target_to_base_conversion_rate .source_to_base_conversion_rate
.as_ref() .as_ref()
.ok_or_else(format_err)? .ok_or_else(format_err)?
.shared_value_ref(), .shared_value_ref(),
@@ -106,7 +106,7 @@ async fn maybe_select_new_conversion_rate(
let left_to_base_conversion_rate = (*left_to_base_conversion_rate.read().await)?; let left_to_base_conversion_rate = (*left_to_base_conversion_rate.read().await)?;
let right_to_base_conversion_rate = (*right_to_base_conversion_rate.read().await)?; let right_to_base_conversion_rate = (*right_to_base_conversion_rate.read().await)?;
let actual_left_to_right_conversion_rate = let actual_left_to_right_conversion_rate =
right_to_base_conversion_rate / left_to_base_conversion_rate; left_to_base_conversion_rate / right_to_base_conversion_rate;
let rate_difference = let rate_difference =
(actual_left_to_right_conversion_rate - left_to_right_stored_conversion_rate).abs(); (actual_left_to_right_conversion_rate - left_to_right_stored_conversion_rate).abs();
@@ -229,15 +229,27 @@ mod tests {
#[test] #[test]
fn transaction_is_submitted_when_difference_is_above_threshold() { fn transaction_is_submitted_when_difference_is_above_threshold() {
let left_to_right_stored_conversion_rate = 1.0;
let left_to_base_conversion_rate = 18f64;
let right_to_base_conversion_rate = 180f64;
assert!(left_to_base_conversion_rate < right_to_base_conversion_rate);
assert_eq!( assert_eq!(
test_maybe_select_new_conversion_rate( test_maybe_select_new_conversion_rate(
TransactionStatus::Idle, TransactionStatus::Idle,
Some(1.0), Some(left_to_right_stored_conversion_rate),
Some(1.0), Some(left_to_base_conversion_rate),
Some(1.03), Some(right_to_base_conversion_rate),
0.02 0.02
), ),
(Some((1.0, 1.03)), TransactionStatus::Idle), (
Some((
left_to_right_stored_conversion_rate,
left_to_base_conversion_rate / right_to_base_conversion_rate,
)),
TransactionStatus::Idle
),
); );
} }
} }
@@ -29,26 +29,78 @@ pub fn token_price_metric(token_id: &str) -> Result<FloatJsonValueMetric, Promet
} }
/// Compute conversion rate between two tokens immediately, without spawning any metrics. /// Compute conversion rate between two tokens immediately, without spawning any metrics.
pub async fn target_to_source_conversion_rate( ///
source_token_id: &str, /// Returned rate may be used in expression: `from_tokens * rate -> to_tokens`.
target_token_id: &str, pub async fn tokens_conversion_rate_from_metrics(
from_token_id: &str,
to_token_id: &str,
) -> anyhow::Result<f64> { ) -> anyhow::Result<f64> {
let source_token_metric = token_price_metric(source_token_id)?; let from_token_metric = token_price_metric(from_token_id)?;
source_token_metric.update().await; from_token_metric.update().await;
let target_token_metric = token_price_metric(target_token_id)?; let to_token_metric = token_price_metric(to_token_id)?;
target_token_metric.update().await; to_token_metric.update().await;
let source_token_value = *source_token_metric.shared_value_ref().read().await; let from_token_value = *from_token_metric.shared_value_ref().read().await;
let target_token_value = *target_token_metric.shared_value_ref().read().await; let to_token_value = *to_token_metric.shared_value_ref().read().await;
// `FloatJsonValueMetric` guarantees that the value is positive && normal, so no additional // `FloatJsonValueMetric` guarantees that the value is positive && normal, so no additional
// checks required here // checks required here
match (source_token_value, target_token_value) { match (from_token_value, to_token_value) {
(Some(source_token_value), Some(target_token_value)) => (Some(from_token_value), Some(to_token_value)) =>
Ok(target_token_value / source_token_value), Ok(tokens_conversion_rate(from_token_value, to_token_value)),
_ => Err(anyhow::format_err!( _ => Err(anyhow::format_err!(
"Failed to compute conversion rate from {} to {}", "Failed to compute conversion rate from {} to {}",
target_token_id, from_token_id,
source_token_id, to_token_id,
)), )),
} }
} }
/// Compute conversion rate between two tokens, given token prices.
///
/// Returned rate may be used in expression: `from_tokens * rate -> to_tokens`.
///
/// Both prices are assumed to be normal and non-negative.
pub fn tokens_conversion_rate(from_token_value: f64, to_token_value: f64) -> f64 {
from_token_value / to_token_value
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rialto_to_millau_conversion_rate_is_correct() {
let rialto_price = 18.18;
let millau_price = 136.35;
assert!(rialto_price < millau_price);
let conversion_rate = tokens_conversion_rate(rialto_price, millau_price);
let rialto_amount = 100.0;
let millau_amount = rialto_amount * conversion_rate;
assert!(
rialto_amount > millau_amount,
"{} RLT * {} = {} MLU",
rialto_amount,
conversion_rate,
millau_amount,
);
}
#[test]
fn millau_to_rialto_conversion_rate_is_correct() {
let rialto_price = 18.18;
let millau_price = 136.35;
assert!(rialto_price < millau_price);
let conversion_rate = tokens_conversion_rate(millau_price, rialto_price);
let millau_amount = 100.0;
let rialto_amount = millau_amount * conversion_rate;
assert!(
rialto_amount > millau_amount,
"{} MLU * {} = {} RLT",
millau_amount,
conversion_rate,
rialto_amount,
);
}
}
@@ -16,7 +16,7 @@
//! Tools for supporting message lanes between two Substrate-based chains. //! Tools for supporting message lanes between two Substrate-based chains.
use crate::messages_lane::SubstrateMessageLane; use crate::{helpers::tokens_conversion_rate, messages_lane::SubstrateMessageLane};
use codec::Decode; use codec::Decode;
use frame_system::AccountInfo; use frame_system::AccountInfo;
@@ -119,19 +119,11 @@ impl<SC: Chain, TC: Chain> StandaloneMessagesMetrics<SC, TC> {
/// Return conversion rate from target to source tokens. /// Return conversion rate from target to source tokens.
pub async fn target_to_source_conversion_rate(&self) -> Option<f64> { pub async fn target_to_source_conversion_rate(&self) -> Option<f64> {
Self::compute_target_to_source_conversion_rate( let from_token_value =
*self.target_to_base_conversion_rate.as_ref()?.shared_value_ref().read().await, (*self.target_to_base_conversion_rate.as_ref()?.shared_value_ref().read().await)?;
*self.source_to_base_conversion_rate.as_ref()?.shared_value_ref().read().await, let to_token_value =
) (*self.source_to_base_conversion_rate.as_ref()?.shared_value_ref().read().await)?;
} Some(tokens_conversion_rate(from_token_value, to_token_value))
/// Return conversion rate from target to source tokens, given conversion rates from
/// target/source tokens to some base token.
fn compute_target_to_source_conversion_rate(
target_to_base_conversion_rate: Option<f64>,
source_to_base_conversion_rate: Option<f64>,
) -> Option<f64> {
Some(source_to_base_conversion_rate? / target_to_base_conversion_rate?)
} }
} }
@@ -379,14 +371,6 @@ mod tests {
use frame_support::storage::generator::StorageValue; use frame_support::storage::generator::StorageValue;
use sp_core::storage::StorageKey; use sp_core::storage::StorageKey;
#[async_std::test]
async fn target_to_source_conversion_rate_works() {
assert_eq!(
StandaloneMessagesMetrics::<relay_rococo_client::Rococo, relay_wococo_client::Wococo>::compute_target_to_source_conversion_rate(Some(183.15), Some(12.32)),
Some(12.32 / 183.15),
);
}
#[test] #[test]
fn token_decimals_used_properly() { fn token_decimals_used_properly() {
let plancks = 425_000_000_000; let plancks = 425_000_000_000;