Skip to content

Commit

Permalink
Change price conversion function in oracle pallet to work for assets …
Browse files Browse the repository at this point in the history
…with different decimals (#490)

* Add trait `DecimalsLookup`

* Create default implementation for `DecimalsLookup`

* Change conversion function in Oracle pallet

* Implement `DecimalsLookup` for all configs

* Fix calls to `one()` function

* Create more tests for price conversion

* Fix failing tests in issue pallet

* Fix failing tests in redeem pallet

* Fix failing tests in vault-registry

* Fix vault-registry benchmark

* Implement DecimalsLookup for Pendulum and Amplitude

* Use DecimalsLookup depending on network in metrics calculations

* Small refactoring

* Fix missing import

* Change conversion calculation
#490 (comment)

* Small refactoring

* Test limits of possible conversion amounts

* Use tolerance to other comparison as well
#490 (comment)
  • Loading branch information
ebma authored Feb 20, 2024
1 parent 35a4895 commit bda384b
Show file tree
Hide file tree
Showing 21 changed files with 447 additions and 119 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions clients/vault/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ async-trait = "0.1.40"
base64 = { version = '0.13.0', default-features = false, features = ['alloc'] }
bincode = "1.3.3"
clap = { version = "3.1", features = ["env"] }
cfg-if = "1.0.0"
rand = "0.8.5"
futures = "0.3.5"
governor = "0.5.0"
Expand Down
12 changes: 12 additions & 0 deletions clients/vault/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,15 @@ pub const CHAIN_HEIGHT_POLLING_INTERVAL: Duration = Duration::from_millis(500);

/// explicitly yield at most once per second
pub const YIELD_RATE: Quota = Quota::per_second(nonzero!(1u32));

cfg_if::cfg_if! {
if #[cfg(feature = "standalone-metadata")] {
pub type DecimalsLookupImpl = primitives::DefaultDecimalsLookup;
} else if #[cfg(feature = "parachain-metadata-pendulum")] {
pub type DecimalsLookupImpl = primitives::PendulumDecimalsLookup;
} else if #[cfg(feature = "parachain-metadata-amplitude")] {
pub type DecimalsLookupImpl = primitives::AmplitudeDecimalsLookup;
} else if #[cfg(feature = "parachain-metadata-foucoco")] {
pub type DecimalsLookupImpl = primitives::AmplitudeDecimalsLookup;
}
}
8 changes: 4 additions & 4 deletions clients/vault/src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use std::{collections::HashMap, convert::TryInto};

use crate::{
system::{VaultData, VaultIdManager},
Error,
DecimalsLookupImpl, Error,
};
use async_trait::async_trait;
use lazy_static::lazy_static;
use primitives::{stellar, Asset};
use primitives::{stellar, Asset, DecimalsLookup};
use runtime::{
prometheus::{
gather, proto::MetricFamily, Encoder, Gauge, GaugeVec, IntCounter, IntGaugeVec, Opts,
Expand Down Expand Up @@ -288,7 +288,7 @@ pub async fn metrics_handler() -> Result<impl Reply, Rejection> {
}

fn raw_value_as_currency(value: u128, currency: CurrencyId) -> Result<f64, ServiceError<Error>> {
let scaling_factor = currency.one() as f64;
let scaling_factor = DecimalsLookupImpl::one(currency) as f64;
Ok(value as f64 / scaling_factor)
}

Expand Down Expand Up @@ -556,7 +556,7 @@ pub async fn publish_expected_stellar_balance<P: VaultRegistryPallet>(
if let Ok(v) = parachain_rpc.get_vault(&vault.vault_id).await {
let lowerbound = v.issued_tokens.saturating_sub(v.to_be_redeemed_tokens);
let upperbound = v.issued_tokens.saturating_add(v.to_be_issued_tokens);
let scaling_factor = vault.vault_id.wrapped_currency().one() as f64;
let scaling_factor = DecimalsLookupImpl::one(vault.vault_id.wrapped_currency()) as f64;

vault.metrics.asset_balance.lowerbound.set(lowerbound as f64 / scaling_factor);
vault.metrics.asset_balance.upperbound.set(upperbound as f64 / scaling_factor);
Expand Down
7 changes: 4 additions & 3 deletions clients/vault/tests/vault_integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ use runtime::{
};
use stellar_relay_lib::sdk::PublicKey;

use vault::{service::IssueFilter, Event as CancellationEvent, VaultIdManager};
use vault::{service::IssueFilter, DecimalsLookupImpl, Event as CancellationEvent, VaultIdManager};

mod helper;
use helper::*;
use primitives::DecimalsLookup;

#[tokio::test(flavor = "multi_thread")]
#[serial]
Expand Down Expand Up @@ -54,7 +55,7 @@ async fn test_redeem_succeeds() {
VaultIdManager::from_map(vault_provider.clone(), vault_wallet.clone(), vault_ids);

// We issue 1 (spacewalk-chain) unit
let issue_amount = CurrencyId::Native.one();
let issue_amount = DecimalsLookupImpl::one(CurrencyId::Native);
let vault_collateral = get_required_vault_collateral_for_issue(
&vault_provider,
issue_amount,
Expand Down Expand Up @@ -902,7 +903,7 @@ async fn test_execute_open_requests_succeeds() {
VaultIdManager::from_map(vault_provider.clone(), vault_wallet.clone(), vault_ids);

// We issue 1 (spacewalk-chain) unit
let issue_amount = CurrencyId::Native.one();
let issue_amount = DecimalsLookupImpl::one(CurrencyId::Native);
let vault_collateral = get_required_vault_collateral_for_issue(
&vault_provider,
issue_amount,
Expand Down
9 changes: 6 additions & 3 deletions clients/wallet/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use primitives::{
Asset, ClaimPredicate, Claimant, Memo, Operation, PublicKey, StellarSdkError, StroopAmount,
Transaction,
},
stellar_stroops_to_u128, StellarStroops,
stellar_stroops_to_u128, DecimalsLookup, StellarStroops,
};

pub trait AppendExt<T> {
Expand Down Expand Up @@ -98,8 +98,11 @@ pub trait RedeemOperationsExt: HorizonClient {

// ... and redeeming amount >= 1 XLM, use create account operation
if to_be_redeemed_asset == Asset::AssetTypeNative &&
to_be_redeemed_amount_u128 >= primitives::CurrencyId::StellarNative.one()
{
to_be_redeemed_amount_u128 >=
// It's okay to use the default here because the Stellar decimals are the same
primitives::DefaultDecimalsLookup::one(
primitives::CurrencyId::StellarNative,
) {
create_account_operation(destination_address, to_be_redeemed_amount)
}
// else use claimable balance
Expand Down
3 changes: 2 additions & 1 deletion pallets/currency/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ pub mod pallet {
+ Copy
+ Default
+ Debug
+ From<u64>;
+ From<u64>
+ From<u128>;

/// Relay chain currency e.g. DOT/KSM
#[pallet::constant]
Expand Down
3 changes: 2 additions & 1 deletion pallets/issue/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub use currency::{
Amount,
};
pub use primitives::CurrencyId;
use primitives::{AmountCompatibility, VaultCurrencyPair, VaultId};
use primitives::{AmountCompatibility, DefaultDecimalsLookup, VaultCurrencyPair, VaultId};

use crate as issue;
use crate::{Config, Error};
Expand Down Expand Up @@ -291,6 +291,7 @@ impl staking::Config for Test {
impl oracle::Config for Test {
type RuntimeEvent = TestEvent;
type WeightInfo = oracle::SubstrateWeight<Test>;
type DecimalsLookup = DefaultDecimalsLookup;
type DataProvider = DiaOracleAdapter<
MockDiaOracle,
UnsignedFixedPoint,
Expand Down
48 changes: 38 additions & 10 deletions pallets/issue/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,14 +447,24 @@ fn test_set_issue_period_only_root() {
#[test]
fn test_request_issue_fails_exceed_limit_volume_for_issue_request() {
run_test(|| {
let volume_limit = 1u128;
let volume_currency = DEFAULT_COLLATERAL_CURRENCY;
let volume_limit = 1_000u128;
crate::Pallet::<Test>::_rate_limit_update(
std::option::Option::<u128>::Some(volume_limit),
DEFAULT_COLLATERAL_CURRENCY,
volume_currency,
7200u64,
);

let issue_amount = volume_limit + 1;
let issue_asset = VAULT.wrapped_currency();
// First, convert the volume limit to the issue asset
let volume_limit_denoted_in_wrapped_asset =
Oracle::convert(&Amount::new(volume_limit, volume_currency), issue_asset)
.expect("Price conversion should work");

// We set the issue amount as the volume limit + 100 (we have to use at least 100 because
// the issue_amount might get rounded down during the price conversion between assets with
// decimals differing by 2)
let issue_amount = volume_limit_denoted_in_wrapped_asset.amount() + 100;
let issue_fee = 1;
let griefing_collateral = 1;
let amount_transferred = issue_amount;
Expand All @@ -468,15 +478,22 @@ fn test_request_issue_fails_exceed_limit_volume_for_issue_request() {
#[test]
fn test_request_issue_fails_after_execute_issue_exceed_limit_volume_for_issue_request() {
run_test(|| {
let volume_currency = DEFAULT_COLLATERAL_CURRENCY;
let volume_limit = 3u128;
crate::Pallet::<Test>::_rate_limit_update(
std::option::Option::<u128>::Some(volume_limit),
DEFAULT_COLLATERAL_CURRENCY,
Option::<u128>::Some(volume_limit),
volume_currency,
7200u64,
);

let issue_asset = VAULT.wrapped_currency();
let issue_amount = volume_limit;

// We set the issue amount as exactly the volume limit
// First, convert the volume limit to the issue asset
let volume_limit_denoted_in_wrapped_asset =
Oracle::convert(&Amount::new(volume_limit, volume_currency), issue_asset)
.expect("Price conversion should work");
let issue_amount = volume_limit_denoted_in_wrapped_asset.amount();
let issue_fee = 1;
let griefing_collateral = 1;
let amount_transferred = issue_amount;
Expand Down Expand Up @@ -519,15 +536,21 @@ fn test_request_issue_fails_after_execute_issue_exceed_limit_volume_for_issue_re
#[test]
fn test_request_issue_success_with_rate_limit() {
run_test(|| {
let volume_currency = DEFAULT_COLLATERAL_CURRENCY;
let volume_limit = 3u128;
crate::Pallet::<Test>::_rate_limit_update(
std::option::Option::<u128>::Some(volume_limit),
DEFAULT_COLLATERAL_CURRENCY,
volume_currency,
7200u64,
);

let issue_asset = VAULT.wrapped_currency();
let issue_amount = volume_limit;

// We set the issue amount as exactly the volume limit
let volume_limit_denoted_in_wrapped_asset =
Oracle::convert(&Amount::new(volume_limit, volume_currency), issue_asset)
.expect("Price conversion should work");
let issue_amount = volume_limit_denoted_in_wrapped_asset.amount();
let issue_fee = 1;
let griefing_collateral = 1;
let amount_transferred = issue_amount;
Expand Down Expand Up @@ -562,15 +585,20 @@ fn test_request_issue_success_with_rate_limit() {
#[test]
fn test_request_issue_reset_interval_and_succeeds_with_rate_limit() {
run_test(|| {
let volume_currency = DEFAULT_COLLATERAL_CURRENCY;
let volume_limit = 3u128;
crate::Pallet::<Test>::_rate_limit_update(
std::option::Option::<u128>::Some(volume_limit),
DEFAULT_COLLATERAL_CURRENCY,
volume_currency,
7200u64,
);

let issue_asset = VAULT.wrapped_currency();
let issue_amount = volume_limit;
let volume_limit_denoted_in_wrapped_asset =
Oracle::convert(&Amount::new(volume_limit, volume_currency), issue_asset)
.expect("Price conversion should work");

let issue_amount = volume_limit_denoted_in_wrapped_asset.amount();
let issue_fee = 1;
let griefing_collateral = 1;
let amount_transferred = issue_amount;
Expand Down
3 changes: 2 additions & 1 deletion pallets/nomination/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use oracle::{
},
};
pub use primitives::CurrencyId;
use primitives::{VaultCurrencyPair, VaultId};
use primitives::{DefaultDecimalsLookup, VaultCurrencyPair, VaultId};

use crate as nomination;
use crate::{Config, Error};
Expand Down Expand Up @@ -273,6 +273,7 @@ impl fee::Config for Test {
impl oracle::Config for Test {
type RuntimeEvent = TestEvent;
type WeightInfo = oracle::SubstrateWeight<Test>;
type DecimalsLookup = DefaultDecimalsLookup;
type DataProvider = DiaOracleAdapter<
MockDiaOracle,
UnsignedFixedPoint,
Expand Down
Loading

0 comments on commit bda384b

Please sign in to comment.