Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions beam-contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,23 @@ impl BeamOracleContract {
PriceOracleContractBase::expires(e, asset)
}

// Estimates amount of fee tokens required to extend the asset retention config for a given time
//
// # Arguments
//
// * `period` - Desired retention period extension (in seconds)
//
// # Returns
//
// Fee asset and estimated amount required for the fee bump
//
// # Panics
//
// Panics if the asset is not supported or if retention config is malformed/missing
pub fn estimate_retention_cost(e: &Env, period: u64) -> (Address, i128) {
PriceOracleContractBase::estimate_retention_cost(e, period)
}

// Extends asset expiration date by a given amount of tokens.
//
// # Arguments
Expand All @@ -109,11 +126,15 @@ impl BeamOracleContract {
// * `asset` - Quoted asset
// * `amount` - Amount of tokens to burn for extending the expiration date
//
// # Returns
//
// Current asset expiration timestamp (in seconds)
//
// # Panics
//
// Panics if asset is not supported or if retention config is malformed/missing
pub fn extend_asset_ttl(e: &Env, sponsor: Address, asset: Asset, amount: i128) {
PriceOracleContractBase::extend_asset_ttl(e, sponsor, asset, amount, 0);
pub fn extend_asset_ttl(e: &Env, sponsor: Address, asset: Asset, amount: i128) -> u64 {
PriceOracleContractBase::extend_asset_ttl(e, sponsor, asset, amount, 0)
}

// Return fee token address daily price feed retainer fee amount
Expand Down
17 changes: 13 additions & 4 deletions beam-contract/src/tests/contract_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ extern crate std;
use crate::{BeamOracleContract, BeamOracleContractClient};
use oracle::testutils::register_token;
use oracle::types::{Asset, FeeConfig};
use oracle::{assets, init_contract_with_admin};
use oracle::{assets, init_contract_with_admin, timestamps};
use soroban_sdk::{testutils::Address as _, Address, Vec};
use test_case::test_case;

Expand Down Expand Up @@ -87,9 +87,18 @@ fn check_extending_asset_ttl() {
let sponsor = Address::generate(&env);
fee_token_client.mint(&sponsor, &10_000_000);

//check the extending
client.extend_asset_ttl(&sponsor, &new_asset, &1_000_000);
assert_eq!(client.expires(&new_asset), Some(87_300));
//estimate bump cost for 1 day
let day = 24 * 60 * 60u64;
let amount = client.estimate_retention_cost(&day);
assert_eq!(amount.0, fee_token_client.address);
assert_eq!(amount.1, 1_000_000);

//check bump
let current_expiration = client.expires(&new_asset).unwrap();
assert_eq!(current_expiration, 0);
let ttl = client.extend_asset_ttl(&sponsor, &new_asset, &amount.1);
assert_eq!(ttl, client.expires(&new_asset).unwrap());
assert_eq!(ttl, timestamps::ledger_timestamp(&env) / 1000 + day);

//check that expiration records length matches assets length
env.as_contract(&client.address, || {
Expand Down
48 changes: 32 additions & 16 deletions oracle/src/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const ASSET_LIMIT: u32 = 256;
//storage keys
const ASSETS_KEY: &str = "assets";
const EXPIRATION_KEY: &str = "expiration";
const DAY: i128 = 86400000;

fn get_expiration_timestamp(e: &Env, initial_expiration_period: u32) -> u64 {
if initial_expiration_period > 0 {
Expand Down Expand Up @@ -88,7 +89,7 @@ pub fn extend_ttl(
asset: Asset,
amount: i128,
initial_expiration_period: u32,
) {
) -> u64 {
//check if the amount is valid
if amount <= 0 {
e.panic_with_error(Error::InvalidAmount);
Expand All @@ -100,24 +101,14 @@ pub fn extend_ttl(
}
let asset_index = asset_index.unwrap();
//load required fee amount from retention config
let (xrf, fee) = match settings::get_fee_config(e) {
FeeConfig::Some(fee_data) => {
if fee_data.1 <= 0 {
e.panic_with_error(Error::InvalidConfig);
}
fee_data
}
FeeConfig::None => {
e.panic_with_error(Error::InvalidConfig);
}
};
//burn corresponding amount of fee tokens
TokenClient::new(&e, &xrf).burn(&sponsor, &amount);
let (xrf, fee) = load_fee_settings(e);
//calculate extension period
let bump = amount * 86400000 / fee; // in milliseconds
let bump = amount * DAY / fee; // in milliseconds
if bump <= 0 {
e.panic_with_error(Error::InvalidAmount);
}
//burn corresponding amount of fee tokens
TokenClient::new(&e, &xrf).burn(&sponsor, &amount);
//load expiration info
let mut expiration = load_expiration_records(e);
let now = timestamps::ledger_timestamp(&e);
Expand All @@ -133,7 +124,32 @@ pub fn extend_ttl(
//write into the vector that holds expiration dates for all symbols
expiration.set(asset_index, asset_expiration);
//update expiration records in instance storage
set_expirations_records(e, &expiration)
set_expirations_records(e, &expiration);
//return current asset TTL
asset_expiration
}

// Estimate amount of fee tokens required to bump the retention for a given time (in milliseconds)
pub fn estimate_retention_cost(e: &Env, bump: u64) -> (Address, i128) {
//load daily retention cost from config
let (xrf, fee) = load_fee_settings(e);
let amount = bump as i128 * fee / DAY;
(xrf, amount)
}

// Load current asset retention fee settings
fn load_fee_settings(e: &Env) -> (Address, i128) {
match settings::get_fee_config(e) {
FeeConfig::Some(fee_data) => {
if fee_data.1 <= 0 {
e.panic_with_error(Error::InvalidConfig);
}
fee_data
}
FeeConfig::None => {
e.panic_with_error(Error::InvalidConfig);
}
}
}

// Load expiration data for all assets
Expand Down
28 changes: 25 additions & 3 deletions oracle/src/price_oracle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ impl PriceOracleContractBase {
//
// # Returns
//
// Asset expiration timestamp (in seconds) or None if asset is not supported
// Asset expiration timestamp (in seconds) or None if asset is expired
//
// # Panics
//
Expand All @@ -108,6 +108,23 @@ impl PriceOracleContractBase {
}
}

// Estimates amount of fee tokens required to extend the asset retention config for a given time
//
// # Arguments
//
// * `period` - Desired retention period extension (in seconds)
//
// # Returns
//
// Fee asset and estimated amount required for the fee bump
//
// # Panics
//
// Panics if the asset is not supported or if retention config is malformed/missing
pub fn estimate_retention_cost(e: &Env, period: u64) -> (Address, i128) {
assets::estimate_retention_cost(e, period * 1000)
}

// Extends the asset expiration date by a given amount of tokens.
//
// # Arguments
Expand All @@ -117,6 +134,10 @@ impl PriceOracleContractBase {
// * `amount` - Amount of tokens to burn for extending the expiration date
// * `initial_expiration_period` - Initial expiration period for new assets (in days)
//
// # Returns
//
// Current asset expiration timestamp (in seconds)
//
// # Panics
//
// Panics if the asset is not supported or if retention config is malformed/missing
Expand All @@ -126,10 +147,11 @@ impl PriceOracleContractBase {
asset: Asset,
amount: i128,
initial_expiration_period: u32,
) {
) -> u64 {
//check sponsor authorization
sponsor.require_auth();
assets::extend_ttl(e, sponsor, asset, amount, initial_expiration_period);
//extend and return current TTL
assets::extend_ttl(e, sponsor, asset, amount, initial_expiration_period) / 1000
}

// Return the fee token address daily price feed retainer fee amount
Expand Down
25 changes: 23 additions & 2 deletions pulse-contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,23 @@ impl PulseOracleContract {
PriceOracleContractBase::expires(e, asset)
}

// Estimates amount of fee tokens required to extend the asset retention config for a given time
//
// # Arguments
//
// * `period` - Desired retention period extension (in seconds)
//
// # Returns
//
// Fee asset and estimated amount required for the fee bump
//
// # Panics
//
// Panics if the asset is not supported or if retention config is malformed/missing
pub fn estimate_retention_cost(e: &Env, period: u64) -> (Address, i128) {
PriceOracleContractBase::estimate_retention_cost(e, period)
}

// Extends the asset expiration date by a given amount of tokens.
//
// # Arguments
Expand All @@ -107,17 +124,21 @@ impl PulseOracleContract {
// * `asset` - Quoted asset
// * `amount` - Amount of tokens to burn for extending the expiration date
//
// # Returns
//
// Current asset expiration timestamp (in seconds)
//
// # Panics
//
// Panics if the asset is not supported or if retention config is malformed/missing
pub fn extend_asset_ttl(e: &Env, sponsor: Address, asset: Asset, amount: i128) {
pub fn extend_asset_ttl(e: &Env, sponsor: Address, asset: Asset, amount: i128) -> u64 {
PriceOracleContractBase::extend_asset_ttl(
e,
sponsor,
asset,
amount,
INITIAL_EXPIRATION_PERIOD,
);
)
}

// Return the fee token address daily price feed retainer fee amount
Expand Down
14 changes: 10 additions & 4 deletions pulse-contract/src/tests/contract_interface_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,16 @@ fn extend_asset_ttl_test() {
let asset = &init_data.assets.first_unchecked();
let initial_expiration = client.expires(&asset).unwrap();

//extend TTL by 10 day (864000 seconds)
client.extend_asset_ttl(&sponsor, &asset, &10_000_000);
//estimate bump cost for 10 day (864000 seconds)
let ten_days = 10 * 24 * 60 * 60u64;
let amount = client.estimate_retention_cost(&ten_days);
assert_eq!(amount.0, fee_token.address);
assert_eq!(amount.1, 10_000_000);

//extend TTL
let ttl = client.extend_asset_ttl(&sponsor, &asset, &amount.1);
assert_eq!(ttl, client.expires(&asset).unwrap());

//verify new expiration
let new_expiration = client.expires(&asset).unwrap();
assert_eq!(new_expiration, initial_expiration + 864000);
assert_eq!(ttl, initial_expiration + 864000);
}
Loading