Skip to content

Commit

Permalink
Merge pull request #1804 from multiversx/more-price-aggregator-events
Browse files Browse the repository at this point in the history
add more events to PriceAggregator
  • Loading branch information
alyn509 authored Oct 9, 2024
2 parents 4ce80e2 + 29f4029 commit 2b9d402
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 40 deletions.
70 changes: 65 additions & 5 deletions contracts/core/price-aggregator/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,43 @@ multiversx_sc::imports!();
multiversx_sc::derive_imports!();

use crate::price_aggregator_data::{TimestampedPrice, TokenPair};
pub type RoundId = u32;
pub type Round = usize;
pub type Block = u64;
pub type Epoch = u64;
pub type Timestamp = u64;

#[type_abi]
#[derive(TopEncode)]
pub struct NewRoundEvent<M: ManagedTypeApi> {
price: BigUint<M>,
timestamp: u64,
timestamp: Timestamp,
decimals: u8,
block: u64,
epoch: u64,
block: Block,
epoch: Epoch,
}

#[type_abi]
#[derive(TopEncode)]
pub struct DiscardSubmissionEvent {
submission_timestamp: Timestamp,
first_submission_timestamp: Timestamp,
has_caller_already_submitted: bool,
}

#[multiversx_sc::module]
pub trait EventsModule {
fn emit_new_round_event(
&self,
token_pair: &TokenPair<Self::Api>,
round: Round,
price_feed: &TimestampedPrice<Self::Api>,
) {
let epoch = self.blockchain().get_block_epoch();
self.new_round_event(
&token_pair.from.clone(),
&token_pair.to.clone(),
epoch,
round,
&NewRoundEvent {
price: price_feed.price.clone(),
timestamp: price_feed.timestamp,
Expand All @@ -40,7 +54,53 @@ pub trait EventsModule {
&self,
#[indexed] from: &ManagedBuffer,
#[indexed] to: &ManagedBuffer,
#[indexed] epoch: u64,
#[indexed] round: Round,
new_round_event: &NewRoundEvent<Self::Api>,
);

fn emit_discard_submission_event(
&self,
token_pair: &TokenPair<Self::Api>,
round: Round,
submission_timestamp: Timestamp,
first_submission_timestamp: Timestamp,
has_caller_already_submitted: bool,
) {
self.discard_submission_event(
&token_pair.from.clone(),
&token_pair.to.clone(),
round,
&DiscardSubmissionEvent {
submission_timestamp,
first_submission_timestamp,
has_caller_already_submitted,
},
)
}

#[event("discard_submission")]
fn discard_submission_event(
&self,
#[indexed] from: &ManagedBuffer,
#[indexed] to: &ManagedBuffer,
#[indexed] round: Round,
discard_submission_event: &DiscardSubmissionEvent,
);

#[event("discard_round")]
fn discard_round_event(
&self,
#[indexed] from: &ManagedBuffer,
#[indexed] to: &ManagedBuffer,
#[indexed] round: Round,
);

#[event("add_submission")]
fn add_submission_event(
&self,
#[indexed] from: &ManagedBuffer,
#[indexed] to: &ManagedBuffer,
#[indexed] round: Round,
price: &BigUint,
);
}
85 changes: 55 additions & 30 deletions contracts/core/price-aggregator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod events;
pub mod median;
pub mod price_aggregator_data;

use events::{Round, Timestamp};
use multiversx_sc_modules::staking;
use price_aggregator_data::{OracleStatus, PriceFeed, TimestampedPrice, TokenPair};

Expand Down Expand Up @@ -47,6 +48,11 @@ pub trait PriceAggregator:
self.set_paused(true);
}

#[upgrade]
fn upgrade(&self) {
self.set_paused(true);
}

#[only_owner]
#[endpoint(changeAmounts)]
fn change_amounts(&self, staking_amount: BigUint, slash_amount: BigUint) {
Expand Down Expand Up @@ -118,29 +124,24 @@ pub trait PriceAggregator:
&self,
from: ManagedBuffer,
to: ManagedBuffer,
submission_timestamp: u64,
submission_timestamp: Timestamp,
price: BigUint,
decimals: u8,
) {
self.require_not_paused();
self.require_is_oracle();

let current_timestamp = self.blockchain().get_block_timestamp();
require!(
submission_timestamp <= current_timestamp,
"Timestamp is from the future"
);
self.require_valid_submission_timestamp(submission_timestamp);

self.check_decimals(&from, &to, decimals);

self.submit_unchecked(from, to, submission_timestamp, price, decimals);
self.submit_unchecked(from, to, price, decimals);
}

fn submit_unchecked(
&self,
from: ManagedBuffer,
to: ManagedBuffer,
submission_timestamp: u64,
price: BigUint,
decimals: u8,
) {
Expand All @@ -154,11 +155,15 @@ pub trait PriceAggregator:
let first_sub_time_mapper = self.first_submission_timestamp(&token_pair);
let last_sub_time_mapper = self.last_submission_timestamp(&token_pair);

let mut round_id = 0;
let wrapped_rounds = self.rounds().get(&token_pair);
if wrapped_rounds.is_some() {
round_id = wrapped_rounds.unwrap().len() + 1;
}

let current_timestamp = self.blockchain().get_block_timestamp();
let mut is_first_submission = false;
let mut first_submission_timestamp = if submissions.is_empty() {
self.require_valid_first_submission(submission_timestamp, current_timestamp);

first_sub_time_mapper.set(current_timestamp);
is_first_submission = true;

Expand All @@ -169,24 +174,38 @@ pub trait PriceAggregator:

// round was not completed in time, so it's discarded
if current_timestamp > first_submission_timestamp + MAX_ROUND_DURATION_SECONDS {
self.require_valid_first_submission(submission_timestamp, current_timestamp);

submissions.clear();
first_sub_time_mapper.set(current_timestamp);
last_sub_time_mapper.set(current_timestamp);

first_submission_timestamp = current_timestamp;
is_first_submission = true;
self.discard_round_event(&token_pair.from.clone(), &token_pair.to.clone(), round_id)
}

let caller = self.blockchain().get_caller();
let accepted = !submissions.contains_key(&caller)
&& (is_first_submission || submission_timestamp >= first_submission_timestamp);
let has_caller_already_submitted = submissions.contains_key(&caller);
let accepted = !has_caller_already_submitted
&& (is_first_submission || current_timestamp >= first_submission_timestamp);
if accepted {
submissions.insert(caller, price);
submissions.insert(caller.clone(), price.clone());
last_sub_time_mapper.set(current_timestamp);

self.create_new_round(token_pair, submissions, decimals);
self.create_new_round(token_pair.clone(), round_id, submissions, decimals);
self.add_submission_event(
&token_pair.from.clone(),
&token_pair.to.clone(),
round_id,
&price,
);
} else {
self.emit_discard_submission_event(
&token_pair,
round_id,
current_timestamp,
first_submission_timestamp,
has_caller_already_submitted,
);
}

self.oracle_status()
Expand All @@ -197,7 +216,12 @@ pub trait PriceAggregator:
});
}

fn require_valid_first_submission(&self, submission_timestamp: u64, current_timestamp: u64) {
fn require_valid_submission_timestamp(&self, submission_timestamp: u64) {
let current_timestamp = self.blockchain().get_block_timestamp();
require!(
submission_timestamp <= current_timestamp,
"Timestamp is from the future"
);
require!(
current_timestamp - submission_timestamp <= FIRST_SUBMISSION_TIMESTAMP_MAX_DIFF_SECONDS,
"First submission too old"
Expand All @@ -207,24 +231,22 @@ pub trait PriceAggregator:
#[endpoint(submitBatch)]
fn submit_batch(
&self,
submissions: MultiValueEncoded<MultiValue5<ManagedBuffer, ManagedBuffer, u64, BigUint, u8>>,
submissions: MultiValueEncoded<
MultiValue5<ManagedBuffer, ManagedBuffer, Timestamp, BigUint, u8>,
>,
) {
self.require_not_paused();
self.require_is_oracle();

let current_timestamp = self.blockchain().get_block_timestamp();
for (from, to, submission_timestamp, price, decimals) in submissions
.into_iter()
.map(|submission| submission.into_tuple())
{
require!(
submission_timestamp <= current_timestamp,
"Timestamp is from the future"
);
self.require_valid_submission_timestamp(submission_timestamp);

self.check_decimals(&from, &to, decimals);

self.submit_unchecked(from, to, submission_timestamp, price, decimals);
self.submit_unchecked(from, to, price, decimals);
}
}

Expand All @@ -248,6 +270,7 @@ pub trait PriceAggregator:
fn create_new_round(
&self,
token_pair: TokenPair<Self::Api>,
round: Round,
mut submissions: MapMapper<ManagedAddress, BigUint>,
decimals: u8,
) {
Expand Down Expand Up @@ -281,7 +304,7 @@ pub trait PriceAggregator:
.or_default()
.get()
.push(&price_feed);
self.emit_new_round_event(&token_pair, &price_feed);
self.emit_new_round_event(&token_pair, round, &price_feed);
}
}

Expand Down Expand Up @@ -377,9 +400,11 @@ pub trait PriceAggregator:
#[only_owner]
#[endpoint(setPairDecimals)]
fn set_pair_decimals(&self, from: ManagedBuffer, to: ManagedBuffer, decimals: u8) {
self.require_paused();

self.pair_decimals(&from, &to).set(Some(decimals));
let pair_decimals_mapper = self.pair_decimals(&from, &to);
if !pair_decimals_mapper.is_empty() {
self.require_paused();
}
pair_decimals_mapper.set(Some(decimals));
let pair = TokenPair { from, to };
self.clear_submissions(&pair);
}
Expand Down Expand Up @@ -422,13 +447,13 @@ pub trait PriceAggregator:
fn first_submission_timestamp(
&self,
token_pair: &TokenPair<Self::Api>,
) -> SingleValueMapper<u64>;
) -> SingleValueMapper<Timestamp>;

#[storage_mapper("last_submission_timestamp")]
fn last_submission_timestamp(
&self,
token_pair: &TokenPair<Self::Api>,
) -> SingleValueMapper<u64>;
) -> SingleValueMapper<Timestamp>;

#[storage_mapper("submissions")]
fn submissions(
Expand Down
1 change: 0 additions & 1 deletion contracts/core/price-aggregator/src/median.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
multiversx_sc::imports!();
multiversx_sc::derive_imports!();

/// Returns the sorted middle, or the average of the two middle indexed items if the
/// vector has an even number of elements.
Expand Down
8 changes: 5 additions & 3 deletions contracts/core/price-aggregator/src/price_aggregator_data.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::events::{RoundId, Timestamp};

multiversx_sc::imports!();
multiversx_sc::derive_imports!();

Expand All @@ -11,10 +13,10 @@ pub struct TokenPair<M: ManagedTypeApi> {
#[type_abi]
#[derive(NestedEncode, NestedDecode, TopEncode, TopDecode)]
pub struct PriceFeed<M: ManagedTypeApi> {
pub round_id: u32,
pub round_id: RoundId,
pub from: ManagedBuffer<M>,
pub to: ManagedBuffer<M>,
pub timestamp: u64,
pub timestamp: Timestamp,
pub price: BigUint<M>,
pub decimals: u8,
}
Expand All @@ -23,7 +25,7 @@ pub struct PriceFeed<M: ManagedTypeApi> {
#[derive(TopEncode, TopDecode, Debug, PartialEq, Eq)]
pub struct TimestampedPrice<M: ManagedTypeApi> {
pub price: BigUint<M>,
pub timestamp: u64,
pub timestamp: Timestamp,
pub decimals: u8,
}

Expand Down
27 changes: 27 additions & 0 deletions contracts/core/price-aggregator/tests/price_aggregator_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,25 @@ where
}
}

#[rustfmt::skip]
impl<Env, From, To, Gas> PriceAggregatorProxyMethods<Env, From, To, Gas>
where
Env: TxEnv,
Env::Api: VMApi,
From: TxFrom<Env>,
To: TxTo<Env>,
Gas: TxGas<Env>,
{
pub fn upgrade(
self,
) -> TxTypedUpgrade<Env, From, To, NotPayable, Gas, ()> {
self.wrapped_tx
.payment(NotPayable)
.raw_upgrade()
.original_result()
}
}

#[rustfmt::skip]
impl<Env, From, To, Gas> PriceAggregatorProxyMethods<Env, From, To, Gas>
where
Expand Down Expand Up @@ -387,3 +406,11 @@ where
pub block: u64,
pub epoch: u64,
}

#[type_abi]
#[derive(TopEncode)]
pub struct DiscardSubmissionEvent {
pub submission_timestamp: u64,
pub first_submission_timestamp: u64,
pub has_caller_already_submitted: bool,
}
Loading

0 comments on commit 2b9d402

Please sign in to comment.