Skip to content

Commit

Permalink
Convert ms to seconds inside interface functions according to SEP-40
Browse files Browse the repository at this point in the history
  • Loading branch information
hawthorne-abendsen authored May 1, 2024
1 parent 371f92e commit 9293e3e
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 39 deletions.
1 change: 0 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ jobs:
uses: stellar-expert/soroban-build-workflow/.github/workflows/release.yml@main
with:
release_name: ${{ github.ref_name }}
build_path: '[""]'
release_description: 'Release of the contract'
secrets:
release_token: ${{ secrets.GITHUB_TOKEN }}
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "reflector-oracle"
version = "3.0.0"
version = "4.0.0"
edition = "2021"

[lib]
Expand Down
4 changes: 2 additions & 2 deletions src/extensions/env_extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub trait EnvExtensions {

fn set_resolution(&self, resolution: u32);

fn get_retention_period(&self) -> Option<u64>;
fn get_retention_period(&self) -> u64;

fn set_retention_period(&self, period: u64);

Expand Down Expand Up @@ -96,7 +96,7 @@ impl EnvExtensions for Env {
get_instance_storage(&self).set(&RESOLUTION, &resolution)
}

fn get_retention_period(&self) -> Option<u64> {
fn get_retention_period(&self) -> u64 {
get_instance_storage(&self)
.get(&RETENTION_PERIOD)
.unwrap_or_default()
Expand Down
46 changes: 27 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ impl PriceOracleContract {
//
// History retention period (in seconds)
pub fn period(e: Env) -> Option<u64> {
e.get_retention_period()
let period = e.get_retention_period();
if period == 0 {
return None;
} else {
return Some(period / 1000); //convert to seconds
}
}

// Returns all assets quoted by the contract.
Expand All @@ -61,28 +66,29 @@ impl PriceOracleContract {
e.get_assets()
}

// Returns the most recent price update timestamp.
// Returns the most recent price update timestamp in seconds.
//
// # Returns
//
// Timestamp of the last recorded price update
pub fn last_timestamp(e: Env) -> u64 {
e.get_last_timestamp()
e.get_last_timestamp() / 1000 //convert to seconds
}

// Returns price in base asset at specific timestamp.
//
// # Arguments
//
// * `asset` - Asset to quote
// * `timestamp` - Timestamp
// * `timestamp` - Timestamp in seconds
//
// # Returns
//
// Price record for the given asset at the given timestamp or None if the record was not found
pub fn price(e: Env, asset: Asset, timestamp: u64) -> Option<PriceData> {
let resolution = e.get_resolution();
let normalized_timestamp = timestamp.get_normalized_timestamp(resolution.into());
let normalized_timestamp = //convert to milliseconds and normalize
(timestamp * 1000).get_normalized_timestamp(resolution.into());
//get the price
get_price_data(&e, asset, normalized_timestamp)
}
Expand Down Expand Up @@ -164,7 +170,8 @@ impl PriceOracleContract {
quote_asset: Asset,
timestamp: u64,
) -> Option<PriceData> {
let normalized_timestamp = timestamp.get_normalized_timestamp(e.get_resolution().into());
let normalized_timestamp = //convert to milliseconds and normalize
(timestamp * 1000).get_normalized_timestamp(e.get_resolution().into());
let decimals = e.get_decimals();
get_x_price(&e, base_asset, quote_asset, normalized_timestamp, decimals)
}
Expand Down Expand Up @@ -367,7 +374,7 @@ impl PriceOracleContract {
panic_with_error!(&e, Error::InvalidTimestamp);
}

let retention_period = e.get_retention_period().unwrap();
let retention_period = e.get_retention_period();

let ledgers_to_live: u32 = ((retention_period / 1000 / 5) + 1) as u32;

Expand Down Expand Up @@ -488,7 +495,7 @@ fn get_twap<F: Fn(u64) -> Option<PriceData>>(
return None;
}

let last_price_timestamp = prices.first()?.timestamp;
let last_price_timestamp = prices.first()?.timestamp * 1000; //convert to milliseconds to match the timestamp format
let timeframe = e.get_resolution() as u64;
let current_time = now(&e);

Expand Down Expand Up @@ -524,10 +531,7 @@ fn get_x_price_by_indexes(
let (base_asset, quote_asset) = asset_pair_indexes;
//check if the asset are the same
if base_asset == quote_asset {
return Some(PriceData {
price: 10i128.pow(decimals),
timestamp,
});
return Some(get_normalized_price_data(10i128.pow(decimals), timestamp));
}

//get the price for base_asset
Expand All @@ -543,12 +547,12 @@ fn get_x_price_by_indexes(
}

//calculate the cross price
Some(PriceData {
price: base_asset_price
Some(get_normalized_price_data(
base_asset_price
.unwrap()
.fixed_div_floor(quote_asset_price.unwrap(), decimals),
timestamp,
})
))
}

fn get_asset_pair_indexes(e: &Env, base_asset: Asset, quote_asset: Asset) -> Option<(u8, u8)> {
Expand Down Expand Up @@ -578,8 +582,12 @@ fn get_price_data_by_index(e: &Env, asset: u8, timestamp: u64) -> Option<PriceDa
if price.is_none() {
return None;
}
Some(PriceData {
price: price.unwrap(),
timestamp,
})
Some(get_normalized_price_data(price.unwrap(), timestamp))
}

fn get_normalized_price_data(price: i128, timestamp: u64) -> PriceData {
PriceData {
price,
timestamp: timestamp / 1000, //convert to seconds
}
}
50 changes: 35 additions & 15 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ use {extensions::i128_extensions::I128Extensions, types::asset::Asset};
const RESOLUTION: u32 = 300_000;
const DECIMALS: u32 = 14;

fn convert_to_seconds(timestamp: u64) -> u64 {
timestamp / 1000
}

fn init_contract_with_admin<'a>() -> (Env, PriceOracleContractClient<'a>, ConfigData) {
let env = Env::default();

Expand Down Expand Up @@ -85,8 +89,13 @@ fn get_updates(env: &Env, assets: &Vec<Asset>, price: i128) -> Vec<i128> {
fn version_test() {
let (_env, client, _init_data) = init_contract_with_admin();
let result = client.version();

assert_eq!(result, 3);
let version = env!("CARGO_PKG_VERSION")
.split(".")
.next()
.unwrap()
.parse::<u32>()
.unwrap();
assert_eq!(result, version);
}

#[test]
Expand All @@ -103,7 +112,7 @@ fn init_test() {
assert_eq!(resolution, RESOLUTION / 1000);

let period = client.period().unwrap();
assert_eq!(period, init_data.period);
assert_eq!(period, init_data.period / 1000);

let decimals = client.decimals();
assert_eq!(decimals, DECIMALS);
Expand Down Expand Up @@ -209,7 +218,7 @@ fn last_price_test() {
result,
Some(PriceData {
price: normalize_price(200),
timestamp: 900_000 as u64
timestamp: convert_to_seconds(900_000)
})
);
}
Expand All @@ -234,7 +243,7 @@ fn last_timestamp_test() {

result = client.last_timestamp();

assert_eq!(result, 600_000);
assert_eq!(result, convert_to_seconds(600_000));
}

#[test]
Expand Down Expand Up @@ -320,7 +329,7 @@ fn set_period_test() {

let result = client.period().unwrap();

assert_eq!(result, period);
assert_eq!(result, convert_to_seconds(period));
}

#[test]
Expand Down Expand Up @@ -348,18 +357,18 @@ fn get_price_test() {
result,
Some(PriceData {
price: normalize_price(200),
timestamp: 900_000 as u64
timestamp: convert_to_seconds(900_000)
})
);

//check price at 899_000
result = client.price(&assets.get_unchecked(1), &899_000);
result = client.price(&assets.get_unchecked(1), &convert_to_seconds(899_000));
assert_ne!(result, None);
assert_eq!(
result,
Some(PriceData {
price: normalize_price(100),
timestamp: 600_000 as u64
timestamp: convert_to_seconds(600_000)
})
);
}
Expand Down Expand Up @@ -402,7 +411,7 @@ fn get_x_last_price_test() {
result,
Some(PriceData {
price: normalize_price(1),
timestamp: 600_000 as u64
timestamp: convert_to_seconds(600_000)
})
);
}
Expand All @@ -422,7 +431,11 @@ fn get_x_price_with_zero_test() {
//set prices for assets
client.set_price(&updates, &timestamp);

let result = client.x_price(&assets.get(0).unwrap(), &assets.get(1).unwrap(), &timestamp);
let result = client.x_price(
&assets.get(0).unwrap(),
&assets.get(1).unwrap(),
&convert_to_seconds(timestamp),
);

assert_eq!(result, None);
}
Expand Down Expand Up @@ -454,18 +467,22 @@ fn get_x_price_test() {
result,
Some(PriceData {
price: normalize_price(1),
timestamp: 900_000 as u64
timestamp: convert_to_seconds(900_000)
})
);

//check price at 899_000
result = client.x_price(&assets.get_unchecked(1), &assets.get_unchecked(2), &899_000);
result = client.x_price(
&assets.get_unchecked(1),
&assets.get_unchecked(2),
&convert_to_seconds(899_000),
);
assert_ne!(result, None);
assert_eq!(
result,
Some(PriceData {
price: normalize_price(1),
timestamp: 600_000 as u64
timestamp: convert_to_seconds(600_000)
})
);
}
Expand Down Expand Up @@ -589,7 +606,10 @@ fn get_non_registered_asset_price_test() {
fn get_asset_price_for_invalid_timestamp_test() {
let (env, client, config_data) = init_contract_with_admin();

let mut result = client.price(&config_data.assets.get_unchecked(1), &u64::MAX);
let mut result = client.price(
&config_data.assets.get_unchecked(1),
&convert_to_seconds(u64::MAX),
);
assert_eq!(result, None);

//try to get price for unknown asset
Expand Down

0 comments on commit 9293e3e

Please sign in to comment.