Skip to content

Commit

Permalink
WETH Oracle support
Browse files Browse the repository at this point in the history
  • Loading branch information
JuaniRios committed Dec 2, 2024
1 parent dca7f69 commit 8f06f16
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 150 deletions.
101 changes: 0 additions & 101 deletions bacon.toml

This file was deleted.

57 changes: 33 additions & 24 deletions integration-tests/src/tests/oracle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,27 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use crate::*;
use pallet_funding::AcceptedFundingAsset;
/// Tests for the oracle pallet integration.
/// Alice, Bob, Charlie are members of the OracleProvidersMembers.
/// Only members should be able to feed data into the oracle.
use parity_scale_codec::alloc::collections::HashMap;
use polimec_common::PLMC_FOREIGN_ID;
use polimec_runtime::{Oracle, RuntimeOrigin};
use sp_runtime::{bounded_vec, BoundedVec, FixedU128};
use tests::defaults::*;

use AcceptedFundingAsset::{DOT, USDC, USDT, WETH};
fn values(
values: [f64; 4],
values: [f64; 5],
) -> BoundedVec<(u32, FixedU128), <polimec_runtime::Runtime as orml_oracle::Config<()>>::MaxFeedValues> {
let [dot, usdc, usdt, plmc] = values;
let [dot, usdc, usdt, weth, plmc] = values;
bounded_vec![
(10u32, FixedU128::from_float(dot)),
(1337u32, FixedU128::from_float(usdc)),
(1984u32, FixedU128::from_float(usdt)),
(3344u32, FixedU128::from_float(plmc))
(DOT.id(), FixedU128::from_float(dot)),
(USDC.id(), FixedU128::from_float(usdc)),
(USDT.id(), FixedU128::from_float(usdt)),
(WETH.id(), FixedU128::from_float(weth)),
(PLMC_FOREIGN_ID, FixedU128::from_float(plmc))
]
}

Expand All @@ -43,19 +47,20 @@ fn members_can_feed_data() {
// pallet_funding genesis builder already inputs prices, so we need to advance one block to feed new values.
inst.advance_time(1u32);
let alice = PolimecNet::account_id_of(ALICE);
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(alice.clone()), values([4.84, 1.0, 1.0, 0.4])));
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(alice.clone()), values([4.84, 1.0, 1.0, 2500.0, 0.4])));

let bob = PolimecNet::account_id_of(BOB);
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(bob.clone()), values([4.84, 1.0, 1.0, 0.4])));
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(bob.clone()), values([4.84, 1.0, 1.0, 2500.0, 0.4])));

let charlie = PolimecNet::account_id_of(CHARLIE);
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(charlie.clone()), values([4.84, 1.0, 1.0, 0.4])));
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(charlie.clone()), values([4.84, 1.0, 1.0, 2500.0, 0.4])));

let expected_values = HashMap::from([
(10u32, FixedU128::from_float(4.84)),
(1337u32, FixedU128::from_float(1.0)),
(1984u32, FixedU128::from_float(1.0)),
(3344u32, FixedU128::from_float(0.4)),
(DOT.id(), FixedU128::from_float(4.84)),
(USDC.id(), FixedU128::from_float(1.0)),
(USDT.id(), FixedU128::from_float(1.0)),
(WETH.id(), FixedU128::from_float(2500.0)),
(PLMC_FOREIGN_ID, FixedU128::from_float(0.4)),
]);

for (key, value) in Oracle::get_all_values() {
Expand All @@ -70,7 +75,7 @@ fn non_members_cannot_feed_data() {
PolimecNet::execute_with(|| {
let dave = PolimecNet::account_id_of(DAVE);
assert_noop!(
Oracle::feed_values(RuntimeOrigin::signed(dave.clone()), values([4.84, 1.0, 1.0, 0.4])),
Oracle::feed_values(RuntimeOrigin::signed(dave.clone()), values([4.84, 1.0, 1.0, 2500.0, 0.4])),
orml_oracle::Error::<polimec_runtime::Runtime, ()>::NoPermission
);
});
Expand All @@ -84,20 +89,24 @@ fn data_is_correctly_combined() {
inst.advance_time(1u32);

let alice = PolimecNet::account_id_of(ALICE);
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(alice.clone()), values([1.0, 1.5, 1.1, 0.11111])));
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(alice.clone()), values([1.0, 1.5, 1.1, 2500.0, 0.11111])));

let bob = PolimecNet::account_id_of(BOB);
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(bob.clone()), values([2.0, 1.0, 1.2, 0.22222])));
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(bob.clone()), values([2.0, 1.0, 1.2, 2500.0, 0.22222])));

let charlie = PolimecNet::account_id_of(CHARLIE);
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(charlie.clone()), values([3.0, 0.8, 1.1, 0.33333])));
assert_ok!(Oracle::feed_values(
RuntimeOrigin::signed(charlie.clone()),
values([3.0, 0.8, 1.1, 2500.0, 0.33333])
));

// Default CombineData implementation is the median value
let expected_values = HashMap::from([
(10u32, FixedU128::from_float(2.0)),
(1337u32, FixedU128::from_float(1.0)),
(1984u32, FixedU128::from_float(1.1)),
(3344u32, FixedU128::from_float(0.22222)),
(DOT.id(), FixedU128::from_float(2.0)),
(USDC.id(), FixedU128::from_float(1.0)),
(USDT.id(), FixedU128::from_float(1.1)),
(WETH.id(), FixedU128::from_float(2500.0)),
(PLMC_FOREIGN_ID, FixedU128::from_float(0.22222)),
]);

for (key, value) in Oracle::get_all_values() {
Expand All @@ -116,13 +125,13 @@ fn pallet_funding_works() {
inst.advance_time(1u32);

let alice = PolimecNet::account_id_of(ALICE);
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(alice.clone()), values([4.84, 1.0, 1.0, 0.4])));
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(alice.clone()), values([4.84, 1.0, 1.0, 2500.0, 0.4])));

let bob = PolimecNet::account_id_of(BOB);
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(bob.clone()), values([4.84, 1.0, 1.0, 0.4])));
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(bob.clone()), values([4.84, 1.0, 1.0, 2500.0, 0.4])));

let charlie = PolimecNet::account_id_of(CHARLIE);
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(charlie.clone()), values([4.84, 1.0, 1.0, 0.4])));
assert_ok!(Oracle::feed_values(RuntimeOrigin::signed(charlie.clone()), values([4.84, 1.0, 1.0, 2500.0, 0.4])));

let _project_id = inst.create_finished_project(
default_project_metadata(ISSUER.into()),
Expand Down
10 changes: 9 additions & 1 deletion nodes/parachain/src/chain_spec/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ use polimec_runtime::{
AccountId, AuraId as AuthorityId, Balance, BlockchainOperationTreasury, ContributionTreasuryAccount,
ExistentialDeposit, FeeRecipient, OracleProvidersMembershipConfig, Runtime, TreasuryAccount, PLMC,
};
use sp_core::{crypto::UncheckedInto, sr25519};
use sp_core::{
crypto::{Ss58AddressFormat, UncheckedInto},
sr25519,
};
use sp_runtime::{traits::AccountIdConversion, Perbill, Percent};

pub type ChainSpec = sc_service::GenericChainSpec<Extensions>;
Expand Down Expand Up @@ -65,6 +68,11 @@ pub fn eve() -> polimec_runtime::AccountId {
get_account_id_from_seed::<sr25519::Public>("Eve")
}

pub fn acc_from_ss58(string: &str) -> polimec_runtime::AccountId {
use sp_core::crypto::Ss58Codec;
sp_core::sr25519::Public::from_ss58check(string).unwrap().into()
}

pub struct GenesisConfigParams {
pub stakers: Vec<AccountId>,
pub council_members: Vec<AccountId>,
Expand Down
18 changes: 15 additions & 3 deletions nodes/parachain/src/chain_spec/polimec_paseo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,32 @@
use sc_service::ChainType;

use crate::chain_spec::{
common::{alice, bob, charlie, dave, eve, genesis_config, GenesisConfigParams},
common::{acc_from_ss58, alice, bob, charlie, dave, eve, genesis_config, GenesisConfigParams},
get_properties, Extensions, GenericChainSpec, DEFAULT_PARA_ID,
};
use polimec_runtime::{AccountId, MinCandidateStk};

pub fn get_local_chain_spec() -> GenericChainSpec {
let endowed_accounts = vec![alice(), bob(), charlie(), dave()];
let endowed_accounts = vec![
alice(),
bob(),
charlie(),
dave(),
acc_from_ss58("5Do5UoayFvDrHroGS1YMqxTVUysSkrhNwVMzmj1foVb3vzzb"),
acc_from_ss58("5E5E37FNZD9KVHyGgSHt8pc2kq8e3VUS5rf8GmrxCa7ySs8s"),
acc_from_ss58("5ELLzYckeuomgTnv4Pf1aT4itxu35cn1KWNCGcftzv5N2x7o"),
];
let endowed_accounts =
endowed_accounts.iter().map(|x| (x.clone(), MinCandidateStk::get() * 20)).collect::<Vec<_>>();
let genesis_config_params = GenesisConfigParams {
stakers: vec![alice(), bob()],
council_members: vec![alice()],
technical_committee_members: vec![alice()],
oracle_members: vec![alice(), bob(), charlie()],
oracle_members: vec![
acc_from_ss58("5Do5UoayFvDrHroGS1YMqxTVUysSkrhNwVMzmj1foVb3vzzb"),
acc_from_ss58("5E5E37FNZD9KVHyGgSHt8pc2kq8e3VUS5rf8GmrxCa7ySs8s"),
acc_from_ss58("5ELLzYckeuomgTnv4Pf1aT4itxu35cn1KWNCGcftzv5N2x7o"),
],
endowed_accounts,
funding_assets_owner: eve(),
id: DEFAULT_PARA_ID,
Expand Down
32 changes: 26 additions & 6 deletions pallets/oracle-ocw/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,16 @@ pub mod pallet {
let account = public.clone().into_account();
if <T as pallet::Config>::Members::contains(&account) {
if let Ok(generic_public) = TryInto::<GenericPublicOf<T>>::try_into(public) {
log::trace!(target: LOG_TARGET, "OCW is a member!");

return Some(generic_public.into());
}
}
None
});

if let Some(_authority_key) = maybe_key {
log::trace!(target: LOG_TARGET, "Executing Logic...");
let mut lock = StorageLock::<Time>::with_deadline(
b"oracle_ocw::lock",
Duration::from_millis(LOCK_TIMEOUT_EXPIRATION),
Expand All @@ -140,6 +143,8 @@ pub mod pallet {
// We try to acquire the lock here. If failed, we know another ocw
// is executing at the moment and exit this ocw.
if let Ok(_guard) = lock.try_lock() {
log::trace!(target: LOG_TARGET, "Acquired Lock");

let val = StorageValueRef::persistent(b"oracle_ocw::last_send");
let last_send_for_assets_result: Result<
Option<BTreeMap<AssetName, BlockNumberFor<T>>>,
Expand All @@ -152,27 +157,42 @@ pub mod pallet {
(AssetName::USDC, Zero::zero()),
(AssetName::DOT, Zero::zero()),
(AssetName::PLMC, Zero::zero()),
(AssetName::WETH, Zero::zero()),
]),
};
log::trace!(target: LOG_TARGET, "Last send for assets: {:?}", last_send_for_assets);

// Fix for missing PLMC in last_send_for_assets for old nodes, that did not have PLMC in the list.
last_send_for_assets.entry(AssetName::PLMC).or_insert_with(Zero::zero);
// Fix for missing WETH in last_send_for_assets for old nodes, that did not have PLMC in the list.
last_send_for_assets.entry(AssetName::WETH).or_insert_with(Zero::zero);

let assets = last_send_for_assets
.iter()
.filter_map(|(asset_name, last_send)| {
// The interval's window where we can submit new prices
let window = T::FetchWindow::get();

// Position of the current block inside the fetch interval
let remainder = block_number.rem(T::FetchInterval::get());
if remainder >= BlockNumberFor::<T>::zero() &&
remainder < window && last_send < &block_number.saturating_sub(window)
{
return Some(*asset_name);

// Condition 1: Is the current block inside the submission window?
let is_in_window = remainder < window;

// Condition 2: Has enough time passed since the asset's last send?
let is_outside_last_window = last_send <= &block_number.saturating_sub(window);

// Include the asset if both conditions are met
if is_in_window && is_outside_last_window {
Some(*asset_name)
} else {
None
}
None
})
.collect::<Vec<AssetName>>();
.collect::<Vec<AssetName>>();

if assets.is_empty() {
log::trace!(target: LOG_TARGET, "Assets to fetch list is empty :c");
return;
}

Expand Down
16 changes: 10 additions & 6 deletions pallets/oracle-ocw/src/mock.rs

Large diffs are not rendered by default.

11 changes: 7 additions & 4 deletions pallets/oracle-ocw/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,33 @@ fn call_offchain_worker() {
let (mut ext, offchain_state, pool_state) = new_test_ext_with_offchain_storage();
price_oracle_response(&mut offchain_state.write());
ext.execute_with(|| {
run_to_block(6);
run_to_block(1);

let tx = pool_state.write().transactions.pop().unwrap();
let tx = Extrinsic::decode(&mut &*tx).unwrap();
assert_eq!(tx.signature.unwrap().0, 0);

match tx.call {
RuntimeCall::Oracle(orml_oracle::Call::feed_values { values }) =>
RuntimeCall::Oracle(orml_oracle::Call::feed_values { values }) => {
dbg!(&values);
for (asset, price) in values {
match asset {
10 => assert_close_enough(price, FixedU128::from_float(6.138485575453039783)),
1984 => assert_close_enough(price, FixedU128::from_float(1.000154206100002620)),
1337 => assert_close_enough(price, FixedU128::from_float(1.000093378020633965)),
3344 => assert_close_enough(price, FixedU128::from_float(0.414564170729477207)),
10_000 => assert_close_enough(price, FixedU128::from_float(3611.253612654460630264)),
_ => panic!("Unexpected asset"),
}
},
}
},
_ => panic!("Unexpected call"),
}
});
}

fn test_fetcher_against_real_api<F: FetchPrice>() {
for asset in [AssetName::DOT, AssetName::USDC, AssetName::USDT, AssetName::PLMC] {
for asset in [AssetName::DOT, AssetName::USDC, AssetName::USDT, AssetName::PLMC, AssetName::WETH] {
let url = F::get_url(asset);
if url.is_empty() {
continue;
Expand Down
Loading

0 comments on commit 8f06f16

Please sign in to comment.